Browse Source

DATAMONGO-636 - Added support for countBy in derived queries.

We now change the query execution to a count execution in case a derived query has the PartTree.isCountProjection() set to true or a query defined in @Query has the newly introduced count() attribute set to true.

As the native Mongo count() returns a long we use a default ConversionService to potentially massage the query result into other numerical types.
pull/14/merge
Oliver Gierke 13 years ago
parent
commit
ab18bb5b96
  1. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
  2. 50
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  3. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
  4. 11
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
  5. 19
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
  6. 24
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
  7. 16
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2013 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,7 +15,11 @@ @@ -15,7 +15,11 @@
*/
package org.springframework.data.mongodb.repository;
import java.lang.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to declare finder queries directly on repository methods. Both attributes allow using a placeholder
@ -43,4 +47,12 @@ public @interface Query { @@ -43,4 +47,12 @@ public @interface Query {
* @return
*/
String fields() default "";
/**
* Returns whether the query defined should be executed as count projection.
*
* @since 1.3
* @return
*/
boolean count() default false;
}

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

@ -17,6 +17,8 @@ package org.springframework.data.mongodb.repository.query; @@ -17,6 +17,8 @@ package org.springframework.data.mongodb.repository.query;
import java.util.List;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoOperations;
@ -39,6 +41,8 @@ import org.springframework.util.Assert; @@ -39,6 +41,8 @@ import org.springframework.util.Assert;
*/
public abstract class AbstractMongoQuery implements RepositoryQuery {
private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService();
private final MongoQueryMethod method;
private final MongoOperations operations;
@ -86,9 +90,33 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -86,9 +90,33 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
return new CollectionExecution(accessor.getPageable()).execute(query);
} else if (method.isPageQuery()) {
return new PagedExecution(accessor.getPageable()).execute(query);
} else {
return new SingleEntityExecution().execute(query);
}
Object result = new SingleEntityExecution(isCountQuery()).execute(query);
if (result == null) {
return result;
}
Class<?> expectedReturnType = method.getReturnType().getType();
if (expectedReturnType.isAssignableFrom(result.getClass())) {
return result;
}
return CONVERSION_SERVICE.convert(result, expectedReturnType);
}
/**
* Creates a {@link Query} instance using the given {@link ConvertingParameterAccessor}. Will delegate to
* {@link #createQuery(ConvertingParameterAccessor)} by default but allows customization of the count query to be
* triggered.
*
* @param accessor must not be {@literal null}.
* @return
*/
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
return createQuery(accessor);
}
/**
@ -100,16 +128,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -100,16 +128,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
protected abstract Query createQuery(ConvertingParameterAccessor accessor);
/**
* Creates a {@link Query} instance using the given {@link ConvertingParameterAccessor}. Will delegate to
* {@link #createQuery(ConvertingParameterAccessor)} by default but allows customization of the count query to be
* triggered.
* Returns whether the query should get a count projection applied.
*
* @param accessor must not be {@literal null}.
* @return
*/
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
return createQuery(accessor);
}
protected abstract boolean isCountQuery();
private abstract class Execution {
@ -191,6 +214,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -191,6 +214,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
*/
class SingleEntityExecution extends Execution {
private final boolean countProjection;
private SingleEntityExecution(boolean countProjection) {
this.countProjection = countProjection;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.core.query.Query)
@ -199,7 +228,8 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -199,7 +228,8 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
Object execute(Query query) {
MongoEntityMetadata<?> metadata = method.getEntityInformation();
return operations.findOne(query, metadata.getJavaType());
return countProjection ? operations.count(query, metadata.getJavaType()) : operations.findOne(query,
metadata.getJavaType());
}
}

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* Copyright 2011-2013 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.
@ -171,7 +171,7 @@ public class MongoQueryMethod extends QueryMethod { @@ -171,7 +171,7 @@ public class MongoQueryMethod extends QueryMethod {
*
* @return
*/
private Query getQueryAnnotation() {
Query getQueryAnnotation() {
return method.getAnnotation(Query.class);
}

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2013 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.
@ -77,4 +77,13 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { @@ -77,4 +77,13 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
return new MongoQueryCreator(tree, accessor, context, false).createQuery();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isCountQuery()
*/
@Override
protected boolean isCountQuery() {
return tree.isCountProjection();
}
}

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* Copyright 2011-2013 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.
@ -38,17 +38,21 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { @@ -38,17 +38,21 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
private final String query;
private final String fieldSpec;
private final boolean isCountQuery;
/**
* Creates a new {@link StringBasedMongoQuery}.
*
* @param method
* @param template
* @param method must not be {@literal null}.
* @param template must not be {@literal null}.
*/
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations) {
super(method, mongoOperations);
this.query = query;
this.fieldSpec = method.getFieldSpecification();
this.isCountQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().count() : false;
}
public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) {
@ -82,6 +86,15 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { @@ -82,6 +86,15 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
return query;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isCountQuery()
*/
@Override
protected boolean isCountQuery() {
return isCountQuery;
}
private String replacePlaceholders(String input, ConvertingParameterAccessor accessor) {
Matcher matcher = PLACEHOLDER.matcher(input);

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

@ -546,4 +546,28 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -546,4 +546,28 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result, hasSize(1));
assertThat(result, hasItem(dave));
}
/**
* @see DATAMONGO-636
*/
@Test
public void executesDerivedCountProjection() {
assertThat(repository.countByLastname("Matthews"), is(2L));
}
/**
* @see DATAMONGO-636
*/
@Test
public void executesDerivedCountProjectionToInt() {
assertThat(repository.countByFirstname("Oliver August"), is(1));
}
/**
* @see DATAMONGO-636
*/
@Test
public void executesAnnotatedCountProjection() {
assertThat(repository.someCountQuery("Matthews"), is(2L));
}
}

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

@ -198,4 +198,20 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query @@ -198,4 +198,20 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
* @return
*/
List<Person> findByCredentials(Credentials credentials);
/**
* @see DATAMONGO-636
*/
long countByLastname(String lastname);
/**
* @see DATAMONGO-636
*/
int countByFirstname(String firstname);
/**
* @see DATAMONGO-636
*/
@Query(value = "{ 'lastname' : ?0 }", count = true)
long someCountQuery(String lastname);
}

Loading…
Cancel
Save