Skip to content

Commit 6743f4b

Browse files
committed
DATAMONGO-1454 - Derive string query exists projection from return type.
String query methods can derive exists projection from a boolean return type.
1 parent 862c963 commit 6743f4b

File tree

6 files changed

+75
-19
lines changed

6 files changed

+75
-19
lines changed

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,6 @@
6262
*/
6363
boolean count() default false;
6464

65-
/**
66-
* Returns whether the query defined should be executed as exists projection.
67-
*
68-
* @since 1.10
69-
* @return
70-
*/
71-
boolean exists() default false;
72-
7365
/**
7466
* Returns whether the query should delete matching documents.
7567
*

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.data.projection.ProjectionFactory;
3434
import org.springframework.data.repository.core.RepositoryMetadata;
3535
import org.springframework.data.repository.query.QueryMethod;
36+
import org.springframework.data.repository.util.QueryExecutionConverters;
3637
import org.springframework.data.util.ClassTypeInformation;
3738
import org.springframework.data.util.TypeInformation;
3839
import org.springframework.util.Assert;
@@ -53,6 +54,7 @@ public class MongoQueryMethod extends QueryMethod {
5354

5455
private final Method method;
5556
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
57+
private final Class<?> unwrappedReturnType;
5658

5759
private MongoEntityMetadata<?> metadata;
5860

@@ -73,6 +75,7 @@ public MongoQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFa
7375

7476
this.method = method;
7577
this.mappingContext = mappingContext;
78+
this.unwrappedReturnType = potentiallyUnwrapReturnTypeFor(method);
7679
}
7780

7881
/*
@@ -187,6 +190,16 @@ private boolean isGeoNearQuery(Method method) {
187190
return false;
188191
}
189192

193+
/**
194+
* Returns whether the query is a exists projection by checking the return type.
195+
*
196+
* @return
197+
* @since 1.10
198+
*/
199+
public boolean isExistsQuery() {
200+
return unwrappedReturnType.isAssignableFrom(Boolean.class) || unwrappedReturnType.isAssignableFrom(Boolean.TYPE);
201+
}
202+
190203
/**
191204
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
192205
*
@@ -250,4 +263,14 @@ public org.springframework.data.mongodb.core.query.Meta getQueryMetaAttributes()
250263

251264
return metaAttributes;
252265
}
266+
267+
private static Class<? extends Object> potentiallyUnwrapReturnTypeFor(Method method) {
268+
269+
if (QueryExecutionConverters.supports(method.getReturnType())) {
270+
// unwrap only one level to handle cases like Future<List<Entity>> correctly.
271+
return ClassTypeInformation.fromReturnTypeOf(method).getComponentType().getType();
272+
}
273+
274+
return method.getReturnType();
275+
}
253276
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperati
9797
this.fieldSpec = BINDING_PARSER.parseAndCollectParameterBindingsFromQueryIntoBindings(
9898
method.getFieldSpecification(), this.fieldSpecParameterBindings);
9999

100-
this.isCountQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().count() : false;
101-
this.isExistsQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().exists() : false;
102-
this.isDeleteQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().delete() : false;
100+
this.isCountQuery = method.hasAnnotatedQuery() && method.getQueryAnnotation().count();
101+
this.isExistsQuery = method.isExistsQuery();
102+
this.isDeleteQuery = method.hasAnnotatedQuery() && method.getQueryAnnotation().delete();
103103

104104
if (hasTwoQueryExecutions(this.isCountQuery, this.isExistsQuery, this.isDeleteQuery)) {
105105
throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method));
@@ -165,7 +165,7 @@ private boolean hasTwoQueryExecutions(boolean isCountQuery, boolean isExistsQuer
165165
*
166166
* @author Thomas Darimont
167167
*/
168-
private static enum ParameterBindingParser {
168+
private enum ParameterBindingParser {
169169

170170
INSTANCE;
171171

@@ -272,7 +272,7 @@ private static void collectParameterReferencesIntoBindings(List<ParameterBinding
272272

273273
} else if (value instanceof Pattern) {
274274

275-
String string = ((Pattern) value).toString().trim();
275+
String string = value.toString().trim();
276276
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(string);
277277

278278
while (valueMatcher.find()) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
259259
/**
260260
* @see DATAMONGO-1454
261261
*/
262-
@Query(value = "{ 'lastname' : ?0 }", exists = true)
262+
@Query(value = "{ 'lastname' : ?0 }")
263263
boolean someExistQuery(String lastname);
264264

265265
/**

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.lang.reflect.Method;
2222
import java.util.Collection;
2323
import java.util.List;
24+
import java.util.concurrent.Future;
2425

2526
import org.junit.Before;
2627
import org.junit.Test;
@@ -212,6 +213,28 @@ public void fallsBackToRepositoryDomainTypeIfMethodDoesNotReturnADomainType() th
212213
assertThat(method.getEntityInformation().getJavaType(), is(typeCompatibleWith(User.class)));
213214
}
214215

216+
/**
217+
* @see DATAMONGO-1454
218+
*/
219+
@Test
220+
public void createsMongoQueryMethodWithExistsProjection() throws Exception {
221+
222+
MongoQueryMethod method = queryMethod(PersonRepository.class, "existsBy");
223+
224+
assertThat(method.isExistsQuery(), is(true));
225+
}
226+
227+
/**
228+
* @see DATAMONGO-1454
229+
*/
230+
@Test
231+
public void createsMongoQueryMethodWithWrappedExistsProjection() throws Exception {
232+
233+
MongoQueryMethod method = queryMethod(PersonRepository.class, "existsByFuture");
234+
235+
assertThat(method.isExistsQuery(), is(true));
236+
}
237+
215238
private MongoQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters) throws Exception {
216239

217240
Method method = repository.getMethod(name, parameters);
@@ -254,6 +277,10 @@ interface PersonRepository extends Repository<User, Long> {
254277
* @see DATAMONGO-1266
255278
*/
256279
void deleteByUserName(String userName);
280+
281+
boolean existsBy();
282+
283+
Future<Boolean> existsByFuture();
257284
}
258285

259286
interface SampleRepository extends Repository<Contact, Long> {

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
* @author Oliver Gierke
6262
* @author Christoph Strobl
6363
* @author Thomas Darimont
64+
* @author Mark Paluch
6465
*/
6566
@RunWith(MockitoJUnitRunner.class)
6667
public class StringBasedMongoQueryUnitTests {
@@ -148,7 +149,7 @@ public void bindsNullParametersCorrectly() throws Exception {
148149
public void bindsDbrefCorrectly() throws Exception {
149150

150151
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByHavingSizeFansNotZero");
151-
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {});
152+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter);
152153

153154
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
154155
assertThat(query.getQueryObject(), is(new BasicQuery("{ fans : { $not : { $size : 0 } } }").getQueryObject()));
@@ -178,8 +179,7 @@ public void preventsDeleteAndCountFlagAtTheSameTime() throws Exception {
178179
@Test
179180
public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception {
180181

181-
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {
182-
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1) });
182+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1));
183183
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", DBObject.class,
184184
Map.class);
185185

@@ -196,7 +196,7 @@ public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception
196196
@Test
197197
public void shouldSupportRespectExistingQuotingInFindByTitleBeginsWithExplicitQuoting() throws Exception {
198198

199-
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { "fun" });
199+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "fun");
200200
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByTitleBeginsWithExplicitQuoting", String.class);
201201

202202
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
@@ -210,7 +210,7 @@ public void shouldSupportRespectExistingQuotingInFindByTitleBeginsWithExplicitQu
210210
@Test
211211
public void shouldParseQueryWithParametersInExpression() throws Exception {
212212

213-
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { 1, 2, 3, 4 });
213+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 1, 2, 3, 4);
214214
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithParametersInExpression", int.class,
215215
int.class, int.class, int.class);
216216

@@ -362,6 +362,17 @@ public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
362362
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
363363
}
364364

365+
/**
366+
* @see DATAMONGO-1454
367+
*/
368+
@Test
369+
public void shouldSupportExistsProjection() throws Exception {
370+
371+
StringBasedMongoQuery mongoQuery = createQueryForMethod("existsByLastname", String.class);
372+
373+
assertThat(mongoQuery.isExistsQuery(), is(true));
374+
}
375+
365376
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
366377

367378
Method method = SampleRepository.class.getMethod(name, parameters);
@@ -420,5 +431,8 @@ private interface SampleRepository extends Repository<Person, Long> {
420431

421432
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
422433
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
434+
435+
@Query(value = "{ 'lastname' : ?0 }")
436+
boolean existsByLastname(String lastname);
423437
}
424438
}

0 commit comments

Comments
 (0)