Skip to content

Commit 712d8be

Browse files
mp911deodrotbohm
authored andcommitted
DATAMONGO-1565 - Polishing.
Consider quoted/unquoted parameter use with the same parameter reference. Extend date range in license headers.
1 parent 536dcc1 commit 712d8be

File tree

2 files changed

+87
-35
lines changed

2 files changed

+87
-35
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015 the original author or authors.
2+
* Copyright 2015-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,9 @@
1515
*/
1616
package org.springframework.data.mongodb.repository.query;
1717

18+
import lombok.Data;
19+
import lombok.RequiredArgsConstructor;
20+
1821
import java.util.ArrayList;
1922
import java.util.LinkedHashMap;
2023
import java.util.List;
@@ -44,6 +47,7 @@
4447
* @author Christoph Strobl
4548
* @author Thomas Darimont
4649
* @author Oliver Gierke
50+
* @author Mark Paluch
4751
* @since 1.9
4852
*/
4953
class ExpressionEvaluatingParameterBinder {
@@ -90,7 +94,7 @@ public String bind(String raw, MongoParameterAccessor accessor, BindingContext b
9094
*
9195
* @param input must not be {@literal null} or empty.
9296
* @param accessor must not be {@literal null}.
93-
* @param bindings must not be {@literal null}.
97+
* @param bindingContext must not be {@literal null}.
9498
* @return
9599
*/
96100
private String replacePlaceholders(String input, MongoParameterAccessor accessor, BindingContext bindingContext) {
@@ -232,22 +236,23 @@ private Pattern createReplacementPattern(List<ParameterBinding> bindings) {
232236
* @param groupName The actual {@link Matcher#group() group}.
233237
* @return
234238
*/
235-
private String extractPlaceholder(String groupName) {
239+
private Placeholder extractPlaceholder(String groupName) {
236240

237241
if (!groupName.endsWith("'") && !groupName.endsWith("\"")) {
238-
return groupName;
242+
return new Placeholder(groupName, false);
239243
}
240-
return groupName.substring(0, groupName.length() - 1);
244+
return new Placeholder(groupName.substring(0, groupName.length() - 1), true);
241245
}
242246

243247
/**
244248
* @author Christoph Strobl
249+
* @author Mark Paluch
245250
* @since 1.9
246251
*/
247252
static class BindingContext {
248253

249254
final MongoParameters parameters;
250-
final Map<String, ParameterBinding> bindings;
255+
final Map<Placeholder, ParameterBinding> bindings;
251256

252257
/**
253258
* Creates new {@link BindingContext}.
@@ -279,13 +284,13 @@ public List<ParameterBinding> getBindings() {
279284

280285
/**
281286
* Get the concrete {@link ParameterBinding} for a given {@literal placeholder}.
282-
*
287+
*
283288
* @param placeholder must not be {@literal null}.
284289
* @return
285290
* @throws java.util.NoSuchElementException
286291
* @since 1.10
287292
*/
288-
ParameterBinding getBindingFor(String placeholder) {
293+
ParameterBinding getBindingFor(Placeholder placeholder) {
289294

290295
if (!bindings.containsKey(placeholder)) {
291296
throw new NoSuchElementException(String.format("Could not to find binding for placeholder '%s'.", placeholder));
@@ -296,20 +301,43 @@ ParameterBinding getBindingFor(String placeholder) {
296301

297302
/**
298303
* Get the associated {@link MongoParameters}.
299-
*
304+
*
300305
* @return
301306
*/
302307
public MongoParameters getParameters() {
303308
return parameters;
304309
}
305310

306-
private static Map<String, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
311+
private static Map<Placeholder, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
307312

308-
Map<String, ParameterBinding> map = new LinkedHashMap<String, ParameterBinding>(bindings.size(), 1);
313+
Map<Placeholder, ParameterBinding> map = new LinkedHashMap<Placeholder, ParameterBinding>(bindings.size(), 1);
309314
for (ParameterBinding binding : bindings) {
310-
map.put(binding.getParameter(), binding);
315+
map.put(new Placeholder(binding.getParameter(), binding.isQuoted()), binding);
311316
}
312317
return map;
313318
}
314319
}
320+
321+
/**
322+
* Encapsulates a quoted/unquoted parameter placeholder.
323+
*
324+
* @author Mark Paluch
325+
* @since 1.9
326+
*/
327+
@Data
328+
@RequiredArgsConstructor
329+
static class Placeholder {
330+
331+
private final String parameter;
332+
private final boolean quoted;
333+
334+
/*
335+
* (non-Javadoc)
336+
* @see java.lang.Object#toString()
337+
*/
338+
@Override
339+
public String toString() {
340+
return quoted ? String.format("'%s'", parameter) : parameter;
341+
}
342+
}
315343
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2015 the original author or authors.
2+
* Copyright 2011-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@
5050
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
5151
import org.springframework.expression.spel.standard.SpelExpressionParser;
5252

53+
import com.mongodb.BasicDBList;
5354
import com.mongodb.BasicDBObject;
5455
import com.mongodb.BasicDBObjectBuilder;
5556
import com.mongodb.DBObject;
@@ -85,9 +86,9 @@ public void setUp() {
8586
public void bindsSimplePropertyCorrectly() throws Exception {
8687

8788
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
88-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
89+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
8990

90-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
91+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
9192
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
9293

9394
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -99,13 +100,13 @@ public void bindsComplexPropertyCorrectly() throws Exception {
99100
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class);
100101

101102
Address address = new Address("Foo", "0123", "Bar");
102-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address);
103+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address);
103104

104105
DBObject dbObject = new BasicDBObject();
105106
converter.write(address, dbObject);
106107
dbObject.removeField(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY);
107108

108-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
109+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
109110
BasicDBObject queryObject = new BasicDBObject("address", dbObject);
110111
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
111112

@@ -118,7 +119,7 @@ public void bindsMultipleParametersCorrectly() throws Exception {
118119
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAndAddress", String.class, Address.class);
119120

120121
Address address = new Address("Foo", "0123", "Bar");
121-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
122+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
122123

123124
DBObject addressDbObject = new BasicDBObject();
124125
converter.write(address, addressDbObject);
@@ -127,7 +128,7 @@ public void bindsMultipleParametersCorrectly() throws Exception {
127128
DBObject reference = new BasicDBObject("address", addressDbObject);
128129
reference.put("lastname", "Matthews");
129130

130-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
131+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
131132
assertThat(query.getQueryObject(), is(reference));
132133
}
133134

@@ -228,10 +229,10 @@ public void shouldParseQueryWithParametersInExpression() throws Exception {
228229
@Test
229230
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
230231

231-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
232+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
232233
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
233234

234-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
235+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
235236
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
236237

237238
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -243,10 +244,10 @@ public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
243244
@Test
244245
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
245246

246-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
247+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
247248
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
248249

249-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
250+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
250251
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
251252

252253
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -259,9 +260,9 @@ public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exceptio
259260
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
260261

261262
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
262-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
263+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
263264

264-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
265+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
265266
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
266267

267268
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -304,10 +305,10 @@ public void shouldParseJsonKeyReplacementCorrectly() throws Exception {
304305
@Test
305306
public void shouldSupportExpressionsInCustomQueries() throws Exception {
306307

307-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
308+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
308309
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
309310

310-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
311+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
311312
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
312313

313314
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -319,11 +320,11 @@ public void shouldSupportExpressionsInCustomQueries() throws Exception {
319320
@Test
320321
public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception {
321322

322-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
323+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
323324
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class,
324325
String.class);
325326

326-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
327+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
327328
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
328329

329330
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -335,11 +336,11 @@ public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exc
335336
@Test
336337
public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception {
337338

338-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
339+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
339340
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
340341
boolean.class, String.class, String.class);
341342

342-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
343+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
343344
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
344345
"{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}");
345346

@@ -353,28 +354,48 @@ public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() t
353354
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
354355

355356
byte[] binaryData = "Matthews".getBytes("UTF-8");
356-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
357+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, binaryData);
357358
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
358359

359-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
360+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
360361
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
361362
+ DatatypeConverter.printBase64Binary(binaryData) + "', '$type' : " + BSON.B_GENERAL + "}}");
362363

363364
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
364365
}
365366

367+
/**
368+
* @see DATAMONGO-1565
369+
*/
370+
@Test
371+
public void bindsPropertyReferenceMultipleTimesCorrectly() throws Exception {
372+
373+
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE);
374+
375+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3);
376+
377+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
378+
BasicDBList or = new BasicDBList();
379+
or.add(new BasicDBObject("age", 3));
380+
or.add(new BasicDBObject("displayAge", "3"));
381+
BasicDBObject queryObject = new BasicDBObject("$or", or);
382+
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
383+
384+
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
385+
}
386+
366387
/**
367388
* @see DATAMONGO-1565
368389
*/
369390
@Test
370391
public void shouldIgnorePlaceholderPatternInReplacementValue() throws Exception {
371392

372-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
393+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
373394
"nothing-special");
374395

375396
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
376397

377-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
398+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
378399
assertThat(query.getQueryObject(),
379400
is(JSON.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
380401
}
@@ -512,6 +533,9 @@ private interface SampleRepository extends Repository<Person, Long> {
512533
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
513534
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
514535

536+
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
537+
boolean findByAgeQuotedAndUnquoted(int age);
538+
515539
@Query("{ 'arg0' : ?0, 'arg1' : ?1 }")
516540
List<Person> findByStringWithWildcardChar(String arg0, String arg1);
517541
}

0 commit comments

Comments
 (0)