diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java index bbb5bd345..83dff9368 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java @@ -25,13 +25,10 @@ import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.parser.Part; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; -import static org.springframework.util.StringUtils.collectionToCommaDelimitedString; -import static org.springframework.util.StringUtils.collectionToDelimitedString; -import static org.springframework.util.StringUtils.hasText; +import static org.springframework.util.StringUtils.*; public class CypherQuery implements CypherQueryDefinition { private final VariableContext variableContext = new VariableContext(); @@ -42,6 +39,7 @@ public class CypherQuery implements CypherQueryDefinition { private int index = 0; private final Neo4jPersistentEntity entity; private final Neo4jTemplate template; + private boolean isCountQuery = false; public CypherQuery(final Neo4jPersistentEntity entity, Neo4jTemplate template) { this.entity = entity; @@ -176,7 +174,12 @@ public class CypherQuery implements CypherQueryDefinition { builder.append(" WHERE ").append(whereClauses); } - builder.append(" RETURN ").append(String.format(QueryTemplates.VARIABLE, getEntityName(entity))); + String returnEntity = String.format(QueryTemplates.VARIABLE,getEntityName(entity)); + if (isCountQuery) { + builder.append(" RETURN ").append("count(").append(returnEntity).append(")"); + } else { + builder.append(" RETURN ").append(returnEntity); + } return builder.toString(); } @@ -235,4 +238,8 @@ public class CypherQuery implements CypherQueryDefinition { public String toString() { return toQueryString(); } + + public void setIsCountQuery(boolean isCountQuery) { + this.isCountQuery = isCountQuery; + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryBuilder.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryBuilder.java index 92b9513b7..70faecfc6 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryBuilder.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryBuilder.java @@ -38,6 +38,11 @@ class CypherQueryBuilder { this.query = new CypherQuery(entity, template); } + public CypherQueryBuilder asCountQuery() { + query.setIsCountQuery(true); + return this; + } + public CypherQueryBuilder addRestriction(Part part) { query.addPart(part, context.getPersistentPropertyPath(part.getProperty())); return this; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryCreator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryCreator.java index fee5bb9ff..b7a0f2017 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryCreator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryCreator.java @@ -37,6 +37,7 @@ class CypherQueryCreator extends AbstractQueryCreator, Neo4jPersistentProperty> context; private final Class domainClass; private final Neo4jTemplate template; + private boolean isCountProjection = false; /** * Creates a new {@link CypherQueryCreator} using the given {@link PartTree}, {@link org.springframework.data.neo4j.support.mapping.Neo4jMappingContext} and domain @@ -58,6 +59,7 @@ class CypherQueryCreator extends AbstractQueryCreator iterator) { CypherQueryBuilder builder = new CypherQueryBuilder(context, domainClass,template); + if (isCountProjection) { + builder = builder.asCountQuery(); + } builder.addRestriction(part); return builder; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/DerivedFinderTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/DerivedFinderTests.java index b72a888e8..e7630a672 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/DerivedFinderTests.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/DerivedFinderTests.java @@ -151,6 +151,14 @@ interface RecipeRepository extends GraphRepository { Set findByIngredientAndCookBook(Ingredient ingredient, CookBook cookBook); Set findBySecrets(Ingredient ingredient); + + Long countByAuthor(String author); + + Long countByIngredientAndAuthor(Ingredient ingredient, String author); + + Long countByIngredient(Ingredient ingredient); + + Integer countByCookBookTitle(String title); } @RunWith(SpringJUnit4ClassRunner.class) @@ -292,6 +300,31 @@ public class DerivedFinderTests { assertThat(single(recipes).author, is(equalTo("Jamie"))); } + @Test + public void shouldCountCorrectlyUsingProperty() throws Exception { + Long actualCountVal = recipeRepository.countByAuthor("Hugh"); + assertThat(actualCountVal, is(equalTo(3L))); + } + + @Test + public void shouldCountCorrectlyUsingEntityAndProperty() throws Exception { + Long actualCountVal = recipeRepository.countByIngredientAndAuthor(oliveOil,"Hugh"); + assertThat(actualCountVal, is(equalTo(1L))); + } + + @Test + public void shouldCountCorrectlyUsingEntity() throws Exception { + Long actualCountVal = recipeRepository.countByIngredient(chocolate); + assertThat(actualCountVal, is(equalTo(2L))); + } + + @Test + public void shouldCountCorrectlyUsingPropertyTraversal() throws Exception { + Integer actualCountVal = recipeRepository.countByCookBookTitle("Naked Chef"); + assertThat(actualCountVal, is(equalTo(1))); + } + + @Test public void shouldFindUsingEntityAndPropertyTraversal() throws Exception { Set recipes = recipeRepository.findByIngredientAndCookBookTitle(oliveOil, "Naked Chef"); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/GraphRepositoryTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/GraphRepositoryTests.java index 6e0689ee4..db7ebe9d8 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/GraphRepositoryTests.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/GraphRepositoryTests.java @@ -372,6 +372,14 @@ public class GraphRepositoryTests { assertThat(findByName, hasItem(testTeam.michael)); } + @Test @Transactional + public void countByName() { + Long num = personRepository.countByName(testTeam.michael.getName()); + assertEquals((Long)1L,num); + } + + + @Test @Transactional public void findByPersonalityEnum() { testTeam.michael.setPersonality(Personality.EXTROVERT); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java index b1033787c..b95da535e 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java @@ -78,6 +78,8 @@ public interface PersonRepository extends GraphRepository, NamedIndexRep Iterable findAllTeamMembersSorted(@Param("p_team") Group team, Sort sort); + Long countByName(String name); + // Derived queries Iterable findByName(String name);