Skip to content

Commit 1ac70dd

Browse files
author
Thomas Darimont
committed
DATAMONGO-420 - Improve support of quote handling for custom query parameters.
We now allow users to specify whether a given query or fields value is already quoted. This can be configured via the newly introduced customQuoting property of @query. This enables the user to specify complete query parts via String parameters. Original pull request: #185.
1 parent 9b65e0c commit 1ac70dd

File tree

4 files changed

+87
-7
lines changed

4 files changed

+87
-7
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,29 @@
6868
* @return
6969
*/
7070
boolean delete() default false;
71+
72+
/**
73+
* Returns an array of {@link CustomQuoting} that controls the quote handling for the respective query parts. Defaults
74+
* to {@code which means no custom quoting.
75+
*
76+
* @since 1.5
77+
* @return
78+
*/
79+
CustomQuoting[] customQuoting() default {};
80+
81+
/**
82+
* @author Thomas Darimont
83+
* @since 1.5
84+
*/
85+
public enum CustomQuoting {
86+
/**
87+
* The {@link Query#value()} should be treated as already quoted.
88+
*/
89+
QUERY,
90+
91+
/**
92+
* The {@link Query#fields()} should be treated as already quoted.
93+
*/
94+
FIELDS
95+
}
7196
}

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2013 the original author or authors.
2+
* Copyright 2011-2014 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.
@@ -18,6 +18,7 @@
1818
import java.io.Serializable;
1919
import java.lang.reflect.Method;
2020
import java.util.Arrays;
21+
import java.util.Collections;
2122
import java.util.List;
2223

2324
import org.springframework.core.annotation.AnnotationUtils;
@@ -28,6 +29,7 @@
2829
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2930
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
3031
import org.springframework.data.mongodb.repository.Query;
32+
import org.springframework.data.mongodb.repository.Query.CustomQuoting;
3133
import org.springframework.data.repository.core.RepositoryMetadata;
3234
import org.springframework.data.repository.query.QueryMethod;
3335
import org.springframework.data.util.ClassTypeInformation;
@@ -39,9 +41,12 @@
3941
* Mongo specific implementation of {@link QueryMethod}.
4042
*
4143
* @author Oliver Gierke
44+
* @author Thomas Darimont
4245
*/
4346
public class MongoQueryMethod extends QueryMethod {
4447

48+
private static final String CUSTOM_QUOTING = "customQuoting";
49+
4550
@SuppressWarnings("unchecked") private static final List<Class<? extends Serializable>> GEO_NEAR_RESULTS = Arrays
4651
.asList(GeoResult.class, GeoResults.class, GeoPage.class);
4752

@@ -96,6 +101,17 @@ String getAnnotatedQuery() {
96101
return StringUtils.hasText(query) ? query : null;
97102
}
98103

104+
/**
105+
* Returns the {@link CustomQuoting} that should be considered when rendering the query object.
106+
*
107+
* @return
108+
*/
109+
List<CustomQuoting> getCustomQuoting() {
110+
111+
CustomQuoting[] customQuoting = (CustomQuoting[]) AnnotationUtils.getValue(getQueryAnnotation(), CUSTOM_QUOTING);
112+
return customQuoting != null ? Arrays.asList(customQuoting) : Collections.<CustomQuoting> emptyList();
113+
}
114+
99115
/**
100116
* Returns the field specification to be used for the query.
101117
*
@@ -143,7 +159,7 @@ public MongoParameters getParameters() {
143159
}
144160

145161
/**
146-
* Returns whether te query is a geo near query.
162+
* Returns whether the query is a geo near query.
147163
*
148164
* @return
149165
*/

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.mongodb.repository.query;
1717

18+
import java.util.List;
1819
import java.util.regex.Matcher;
1920
import java.util.regex.Pattern;
2021

@@ -23,6 +24,7 @@
2324
import org.springframework.data.mongodb.core.MongoOperations;
2425
import org.springframework.data.mongodb.core.query.BasicQuery;
2526
import org.springframework.data.mongodb.core.query.Query;
27+
import org.springframework.data.mongodb.repository.Query.CustomQuoting;
2628

2729
import com.mongodb.util.JSON;
2830

@@ -31,6 +33,7 @@
3133
*
3234
* @author Oliver Gierke
3335
* @author Christoph Strobl
36+
* @author Thomas Darimont
3437
*/
3538
public class StringBasedMongoQuery extends AbstractMongoQuery {
3639

@@ -42,6 +45,8 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
4245
private final String fieldSpec;
4346
private final boolean isCountQuery;
4447
private final boolean isDeleteQuery;
48+
private final boolean queryIsAlreadyQuoted;
49+
private final boolean fieldsAreAlreadyQuoted;
4550

4651
/**
4752
* Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod} and {@link MongoOperations}.
@@ -69,6 +74,10 @@ public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperati
6974
this.isCountQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().count() : false;
7075
this.isDeleteQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().delete() : false;
7176

77+
List<CustomQuoting> customQuoting = method.getCustomQuoting();
78+
this.queryIsAlreadyQuoted = customQuoting.contains(CustomQuoting.QUERY);
79+
this.fieldsAreAlreadyQuoted = customQuoting.contains(CustomQuoting.FIELDS);
80+
7281
if (isCountQuery && isDeleteQuery) {
7382
throw new IllegalArgumentException(String.format(COUND_AND_DELETE, method));
7483
}
@@ -81,12 +90,12 @@ public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperati
8190
@Override
8291
protected Query createQuery(ConvertingParameterAccessor accessor) {
8392

84-
String queryString = replacePlaceholders(query, accessor);
93+
String queryString = replacePlaceholders(query, accessor, queryIsAlreadyQuoted);
8594

8695
Query query = null;
8796

8897
if (fieldSpec != null) {
89-
String fieldString = replacePlaceholders(fieldSpec, accessor);
98+
String fieldString = replacePlaceholders(fieldSpec, accessor, fieldsAreAlreadyQuoted);
9099
query = new BasicQuery(queryString, fieldString);
91100
} else {
92101
query = new BasicQuery(queryString);
@@ -119,21 +128,28 @@ protected boolean isDeleteQuery() {
119128
return this.isDeleteQuery;
120129
}
121130

122-
private String replacePlaceholders(String input, ConvertingParameterAccessor accessor) {
131+
private String replacePlaceholders(String input, ConvertingParameterAccessor accessor, boolean valuesAreAlreadyQuoted) {
123132

124133
Matcher matcher = PLACEHOLDER.matcher(input);
125134
String result = input;
126135

127136
while (matcher.find()) {
128137
String group = matcher.group();
129138
int index = Integer.parseInt(matcher.group(1));
130-
result = result.replace(group, getParameterWithIndex(accessor, index));
139+
result = result.replace(group, getParameterWithIndex(accessor, index, valuesAreAlreadyQuoted));
131140
}
132141

133142
return result;
134143
}
135144

136-
private String getParameterWithIndex(ConvertingParameterAccessor accessor, int index) {
145+
private String getParameterWithIndex(ConvertingParameterAccessor accessor, int index, boolean valueIsQuoted) {
146+
147+
Object result = accessor.getBindableValue(index);
148+
149+
if (result instanceof String && valueIsQuoted) {
150+
return (String) result;
151+
}
152+
137153
return JSON.serialize(accessor.getBindableValue(index));
138154
}
139155
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.hamcrest.Matchers.*;
1919
import static org.junit.Assert.*;
2020
import static org.mockito.Mockito.*;
21+
import static org.springframework.data.mongodb.repository.Query.CustomQuoting.*;
2122

2223
import java.lang.reflect.Method;
2324

@@ -46,6 +47,7 @@
4647
*
4748
* @author Oliver Gierke
4849
* @author Christoph Strobl
50+
* @author Thomas Darimont
4951
*/
5052
@RunWith(MockitoJUnitRunner.class)
5153
public class StringBasedMongoQueryUnitTests {
@@ -158,6 +160,24 @@ public void preventsDeleteAndCountFlagAtTheSameTime() throws Exception {
158160
createQueryForMethod("invalidMethod", String.class);
159161
}
160162

163+
/**
164+
* @see DATAMONGO-420
165+
*/
166+
@Test
167+
public void shouldSupportDetectingAlreadyQuotedValuesInQueryAndFieldDefinitions() throws Exception {
168+
169+
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByDynamicQueryWithFieldsAndCustomQuoting",
170+
String.class, String.class);
171+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {
172+
"'firstname':'bubu', 'lastname': 'baba'", "'lastname':1" });
173+
174+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
175+
176+
assertThat(query.getQueryObject(),
177+
is(new BasicQuery("{ \"firstname\": \"bubu\", \"lastname\": \"baba\"}").getQueryObject()));
178+
assertThat(query.getFieldsObject(), is(new BasicQuery(null, "{ \"lastname\": 1}").getFieldsObject()));
179+
}
180+
161181
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
162182

163183
Method method = SampleRepository.class.getMethod(name, parameters);
@@ -184,5 +204,8 @@ private interface SampleRepository {
184204

185205
@Query(value = "{ 'lastname' : ?0 }", delete = true, count = true)
186206
void invalidMethod(String lastname);
207+
208+
@Query(value = "{?0}", fields = "{?1}", customQuoting = { QUERY, FIELDS })
209+
DBObject findByDynamicQueryWithFieldsAndCustomQuoting(String criteria, String fields);
187210
}
188211
}

0 commit comments

Comments
 (0)