Skip to content

Commit c0f3255

Browse files
mp911dechristophstrobl
authored andcommitted
DATAMONGO-1565 - Polishing.
Consider quoted/unquoted parameter use with the same parameter reference. Extend date range in license headers.
1 parent b1de52f commit c0f3255

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.
@@ -20,6 +20,7 @@
2020
import static org.mockito.Mockito.*;
2121

2222
import java.lang.reflect.Method;
23+
import java.util.ArrayList;
2324
import java.util.Collections;
2425
import java.util.List;
2526
import java.util.Map;
@@ -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
Document document = new Document();
106107
converter.write(address, document);
107108
document.remove(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
Document queryObject = new Document("address", document);
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
Document addressDocument = new Document();
125126
converter.write(address, addressDocument);
@@ -128,7 +129,7 @@ public void bindsMultipleParametersCorrectly() throws Exception {
128129
Document reference = new Document("lastname", "Matthews");
129130
reference.append("address", addressDocument);
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().toJson(), is(reference.toJson()));
133134
}
134135

@@ -232,10 +233,10 @@ public void shouldParseQueryWithParametersInExpression() throws Exception {
232233
@Test
233234
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
234235

235-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
236+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
236237
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
237238

238-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
239+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
239240
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
240241

241242
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -247,10 +248,10 @@ public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
247248
@Test
248249
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
249250

250-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
251+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
251252
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
252253

253-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
254+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
254255
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
255256

256257
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -263,9 +264,9 @@ public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exceptio
263264
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
264265

265266
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
266-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
267+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
267268

268-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
269+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
269270
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
270271

271272
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -307,10 +308,10 @@ public void shouldParseJsonKeyReplacementCorrectly() throws Exception {
307308
@Test
308309
public void shouldSupportExpressionsInCustomQueries() throws Exception {
309310

310-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
311+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
311312
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
312313

313-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
314+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
314315
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
315316

316317
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -322,11 +323,11 @@ public void shouldSupportExpressionsInCustomQueries() throws Exception {
322323
@Test
323324
public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception {
324325

325-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
326+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
326327
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class,
327328
String.class);
328329

329-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
330+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
330331
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
331332

332333
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -338,11 +339,11 @@ public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exc
338339
@Test
339340
public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception {
340341

341-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
342+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
342343
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
343344
boolean.class, String.class, String.class);
344345

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

@@ -356,16 +357,36 @@ public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() t
356357
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
357358

358359
byte[] binaryData = "Matthews".getBytes("UTF-8");
359-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
360+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, binaryData);
360361
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
361362

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

366367
assertThat(query.getQueryObject().toJson(), is(reference.getQueryObject().toJson()));
367368
}
368369

370+
/**
371+
* @see DATAMONGO-1565
372+
*/
373+
@Test
374+
public void bindsPropertyReferenceMultipleTimesCorrectly() throws Exception {
375+
376+
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE);
377+
378+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3);
379+
380+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
381+
List<Object> or = new ArrayList<>();
382+
or.add(new Document("age", 3));
383+
or.add(new Document("displayAge", "3"));
384+
Document queryObject = new Document("$or", or);
385+
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
386+
387+
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
388+
}
389+
369390
/**
370391
* @see DATAMONGO-1454
371392
*/
@@ -383,12 +404,12 @@ public void shouldSupportExistsProjection() throws Exception {
383404
@Test
384405
public void shouldIgnorePlaceholderPatternInReplacementValue() throws Exception {
385406

386-
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
407+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
387408
"nothing-special");
388409

389410
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
390411

391-
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
412+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
392413
assertThat(query.getQueryObject(),
393414
is(Document.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
394415
}
@@ -527,6 +548,9 @@ private interface SampleRepository extends Repository<Person, Long> {
527548
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
528549
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
529550

551+
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
552+
boolean findByAgeQuotedAndUnquoted(int age);
553+
530554
@Query(value = "{ 'lastname' : ?0 }", exists = true)
531555
boolean existsByLastname(String lastname);
532556

0 commit comments

Comments
 (0)