DATAMONGO-1565 - Polishing.

Consider quoted/unquoted parameter use with the same parameter reference. Extend date range in license headers.
This commit is contained in:
Mark Paluch
2016-12-16 16:18:32 +01:00
committed by Oliver Gierke
parent 14e326dc09
commit 1a105333aa
2 changed files with 87 additions and 35 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.repository.query;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -44,6 +47,7 @@ import com.mongodb.util.JSON;
* @author Christoph Strobl
* @author Thomas Darimont
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.9
*/
class ExpressionEvaluatingParameterBinder {
@@ -90,7 +94,7 @@ class ExpressionEvaluatingParameterBinder {
*
* @param input must not be {@literal null} or empty.
* @param accessor must not be {@literal null}.
* @param bindings must not be {@literal null}.
* @param bindingContext must not be {@literal null}.
* @return
*/
private String replacePlaceholders(String input, MongoParameterAccessor accessor, BindingContext bindingContext) {
@@ -232,22 +236,23 @@ class ExpressionEvaluatingParameterBinder {
* @param groupName The actual {@link Matcher#group() group}.
* @return
*/
private String extractPlaceholder(String groupName) {
private Placeholder extractPlaceholder(String groupName) {
if (!groupName.endsWith("'") && !groupName.endsWith("\"")) {
return groupName;
return new Placeholder(groupName, false);
}
return groupName.substring(0, groupName.length() - 1);
return new Placeholder(groupName.substring(0, groupName.length() - 1), true);
}
/**
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.9
*/
static class BindingContext {
final MongoParameters parameters;
final Map<String, ParameterBinding> bindings;
final Map<Placeholder, ParameterBinding> bindings;
/**
* Creates new {@link BindingContext}.
@@ -279,13 +284,13 @@ class ExpressionEvaluatingParameterBinder {
/**
* Get the concrete {@link ParameterBinding} for a given {@literal placeholder}.
*
*
* @param placeholder must not be {@literal null}.
* @return
* @throws java.util.NoSuchElementException
* @since 1.10
*/
ParameterBinding getBindingFor(String placeholder) {
ParameterBinding getBindingFor(Placeholder placeholder) {
if (!bindings.containsKey(placeholder)) {
throw new NoSuchElementException(String.format("Could not to find binding for placeholder '%s'.", placeholder));
@@ -296,20 +301,43 @@ class ExpressionEvaluatingParameterBinder {
/**
* Get the associated {@link MongoParameters}.
*
*
* @return
*/
public MongoParameters getParameters() {
return parameters;
}
private static Map<String, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
private static Map<Placeholder, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
Map<String, ParameterBinding> map = new LinkedHashMap<String, ParameterBinding>(bindings.size(), 1);
Map<Placeholder, ParameterBinding> map = new LinkedHashMap<Placeholder, ParameterBinding>(bindings.size(), 1);
for (ParameterBinding binding : bindings) {
map.put(binding.getParameter(), binding);
map.put(new Placeholder(binding.getParameter(), binding.isQuoted()), binding);
}
return map;
}
}
/**
* Encapsulates a quoted/unquoted parameter placeholder.
*
* @author Mark Paluch
* @since 1.9
*/
@Data
@RequiredArgsConstructor
static class Placeholder {
private final String parameter;
private final boolean quoted;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return quoted ? String.format("'%s'", parameter) : parameter;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
@@ -86,9 +87,9 @@ public class StringBasedMongoQueryUnitTests {
public void bindsSimplePropertyCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -100,13 +101,13 @@ public class StringBasedMongoQueryUnitTests {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class);
Address address = new Address("Foo", "0123", "Bar");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address);
DBObject dbObject = new BasicDBObject();
converter.write(address, dbObject);
dbObject.removeField(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
BasicDBObject queryObject = new BasicDBObject("address", dbObject);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
@@ -119,7 +120,7 @@ public class StringBasedMongoQueryUnitTests {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAndAddress", String.class, Address.class);
Address address = new Address("Foo", "0123", "Bar");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
DBObject addressDbObject = new BasicDBObject();
converter.write(address, addressDbObject);
@@ -128,7 +129,7 @@ public class StringBasedMongoQueryUnitTests {
DBObject reference = new BasicDBObject("address", addressDbObject);
reference.put("lastname", "Matthews");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is(reference));
}
@@ -229,10 +230,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -244,10 +245,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -260,9 +261,9 @@ public class StringBasedMongoQueryUnitTests {
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -305,10 +306,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueries() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -320,11 +321,11 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class,
String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -336,11 +337,11 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
boolean.class, String.class, String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
"{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}");
@@ -354,16 +355,36 @@ public class StringBasedMongoQueryUnitTests {
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
byte[] binaryData = "Matthews".getBytes("UTF-8");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, binaryData);
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
+ DatatypeConverter.printBase64Binary(binaryData) + "', '$type' : " + BSON.B_GENERAL + "}}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void bindsPropertyReferenceMultipleTimesCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
BasicDBList or = new BasicDBList();
or.add(new BasicDBObject("age", 3));
or.add(new BasicDBObject("displayAge", "3"));
BasicDBObject queryObject = new BasicDBObject("$or", or);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1454
*/
@@ -381,12 +402,12 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldIgnorePlaceholderPatternInReplacementValue() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
"nothing-special");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is(JSON.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
}
@@ -524,6 +545,9 @@ public class StringBasedMongoQueryUnitTests {
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
boolean findByAgeQuotedAndUnquoted(int age);
@Query(value = "{ 'lastname' : ?0 }", exists = true)
boolean existsByLastname(String lastname);