Skip to content

Commit 1a10533

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 14e326d commit 1a10533

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;
@@ -86,9 +87,9 @@ public void setUp() {
8687
public void bindsSimplePropertyCorrectly() throws Exception {
8788

8889
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
89-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
90+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
9091

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

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

102103
Address address = new Address("Foo", "0123", "Bar");
103-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address);
104+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address);
104105

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

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

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

121122
Address address = new Address("Foo", "0123", "Bar");
122-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
123+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
123124

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -354,16 +355,36 @@ public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() t
354355
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
355356

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

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

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

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

384-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
405+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
385406
"nothing-special");
386407

387408
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
388409

389-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
410+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
390411
assertThat(query.getQueryObject(),
391412
is(JSON.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
392413
}
@@ -524,6 +545,9 @@ private interface SampleRepository extends Repository<Person, Long> {
524545
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
525546
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
526547

548+
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
549+
boolean findByAgeQuotedAndUnquoted(int age);
550+
527551
@Query(value = "{ 'lastname' : ?0 }", exists = true)
528552
boolean existsByLastname(String lastname);
529553

0 commit comments

Comments
 (0)