From ac3fcc4190b98f3d68199014166c81a14e1ca532 Mon Sep 17 00:00:00 2001 From: Nicki Watt Date: Tue, 9 Jul 2013 16:21:47 +0100 Subject: [PATCH] DATAGRAPH-352/330 : Fixed issues relating to Sorting and Paging --- .../repository/AbstractGraphRepository.java | 4 +- .../neo4j/repository/query/CypherQuery.java | 30 ++++++++++- .../neo4j/repository/DerivedFinderTests.java | 53 +++++++++++++++++-- .../repository/GraphRepositoryTests.java | 48 ++++++++++++++--- 4 files changed, 122 insertions(+), 13 deletions(-) diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java index b160a925c..c612ef6d3 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java @@ -34,6 +34,7 @@ import org.springframework.data.neo4j.annotation.QueryType; import org.springframework.data.neo4j.conversion.EndResult; import org.springframework.data.neo4j.conversion.Result; import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.repository.query.CypherQuery; import org.springframework.data.neo4j.support.Neo4jTemplate; import org.springframework.data.neo4j.support.index.NoSuchIndexException; import org.springframework.data.neo4j.support.index.NullReadableIndex; @@ -346,7 +347,8 @@ public abstract class AbstractGraphRepository im @Override public EndResult findAll(Sort sort) { - return findAll(); // todo + CypherQuery cq = new CypherQuery(template.getEntityType(clazz).getEntity(),template); + return query(cq.toQueryString(sort), Collections.EMPTY_MAP); } @Override 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 fb461e658..bb0bc88f8 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,6 +25,7 @@ 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; @@ -91,6 +92,26 @@ public class CypherQuery implements CypherQueryDefinition { return this; } + private Sort getCypherEntityRefAwareSort(Sort sorts) { + List entityAwareOrders = new ArrayList(); + Iterator i = sorts.iterator(); + while( i.hasNext()) { + Sort.Order o = i.next(); + entityAwareOrders.add( getEntityAwareOrderRef(o) ); + } + Sort entityAwareSort = new Sort(entityAwareOrders); + return entityAwareSort; + } + + private Sort.Order getEntityAwareOrderRef(Sort.Order o) { + // Cater for cases which cause 80% of the reported grief, i.e. assumes a string + // with no period refers to a property on entity - however this will + // not always be the case .. confirm with Michael .. + return (o.getProperty().contains(".")) + ? o + : new Sort.Order(o.getDirection(),getEntityName(entity)+"."+o.getProperty()); + } + private boolean addedStartClause(PartInfo partInfo) { if (!partInfo.isIndexed()) return false; for (StartClause startClause : startClauses) { @@ -192,8 +213,15 @@ public class CypherQuery implements CypherQueryDefinition { @Override public String toQueryString(Sort sort) { + return toQueryString(sort,true); + } + + private String toQueryString(Sort sort,boolean applyMissingRefs) { StringBuilder builder = new StringBuilder(render()); - if (sort != null) builder.append(addSorts(sort)); + if (sort != null) { + builder.append(addSorts( + applyMissingRefs ? getCypherEntityRefAwareSort(sort) : sort)); + } return builder.toString(); } 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 6c1aeafae..b72a888e8 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 @@ -20,10 +20,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Transaction; +import org.neo4j.helpers.collection.IteratorUtil; import org.neo4j.test.ImpermanentGraphDatabase; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Sort; import org.springframework.data.neo4j.annotation.*; import org.springframework.data.neo4j.config.EnableNeo4jRepositories; import org.springframework.data.neo4j.config.Neo4jConfiguration; @@ -31,14 +33,17 @@ import org.springframework.data.neo4j.support.Neo4jTemplate; import org.springframework.data.neo4j.support.node.Neo4jHelper; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; -import java.util.HashSet; -import java.util.Set; +import java.util.*; +import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.neo4j.graphdb.Direction.INCOMING; +import static org.neo4j.helpers.collection.IteratorUtil.asCollection; import static org.neo4j.helpers.collection.IteratorUtil.single; import static org.springframework.data.neo4j.SetHelper.asSet; @@ -130,6 +135,12 @@ interface RecipeRepository extends GraphRepository { Set findByIngredient(Ingredient ingredient); + Iterable findByIngredient(Ingredient ingredient, Sort sort); + + Iterable findByIngredientOrderByAuthorAsc(Ingredient ingredient); + + Iterable findByIngredientOrderByAuthorDesc(Ingredient ingredient); + Set findBySecret(Ingredient ingredient); Set findByIngredientAndAuthor(Ingredient ingredient, String author); @@ -171,11 +182,11 @@ public class DerivedFinderTests { @Autowired private DishRepository dishRepository; - private Ingredient fish, spice, oliveOil, pear; + private Ingredient fish, spice, oliveOil, pear, chocolate; private CookBook nakedChef, baking101; - private Recipe focaccia; + private Recipe focaccia, chocolateFudgeCake, whiteChocolateSquares; @Before public void setUp() throws Exception { @@ -185,6 +196,7 @@ public class DerivedFinderTests { Transaction transaction = graphDatabaseService.beginTx(); try { + chocolate = ingredientRepository.save(new Ingredient("chocolate")); fish = ingredientRepository.save(new Ingredient("fish")); spice = ingredientRepository.save(new Ingredient("spice x")); oliveOil = ingredientRepository.save(new Ingredient("olive oil")); @@ -196,6 +208,10 @@ public class DerivedFinderTests { recipeRepository.save(new Recipe("The Colonel", "fried chicken", null, spice, null)); recipeRepository.save(new Recipe("Jamie", "pesto", oliveOil, null, nakedChef)); focaccia = recipeRepository.save(new Recipe("Hugh", "focaccia", oliveOil, null, baking101)); + + chocolateFudgeCake = recipeRepository.save(new Recipe("Nigella", "Chocolate Fudge cake", chocolate, null, null)); + whiteChocolateSquares = recipeRepository.save(new Recipe("Heston", "White Chocolate squares", chocolate, null, null)); + dish = dishRepository.save(new Dish(100)); transaction.success(); } finally { @@ -288,4 +304,33 @@ public class DerivedFinderTests { assertThat(foundDish.number, is(equalTo(dish.number))); } + + @Test + public void shouldFindCorrectlyOrderedUsingMethodSignatureAscending() { + Iterable recipes = recipeRepository.findByIngredientOrderByAuthorAsc(chocolate); + List recs = (List)asCollection(recipes); + assertThat( recs.size(), equalTo(2)); + assertEquals("Heston" , recs.get(0).author); + assertEquals("Nigella" , recs.get(1).author); + } + + @Test + public void shouldFindCorrectlyOrderedUsingMethodSignatureDescending() { + Iterable recipes = recipeRepository.findByIngredientOrderByAuthorDesc(chocolate); + List recs = (List)asCollection(recipes); + assertThat( recs.size(), equalTo(2)); + assertEquals("Nigella" , recs.get(0).author); + assertEquals("Heston" , recs.get(1).author); + } + + @Test + public void shouldFindCorrectlyOrderedUsingSortParam() { + Sort.Order order = new Sort.Order(Sort.Direction.DESC,"author"); + Sort sort = new Sort(order); + Iterable recipes = recipeRepository.findByIngredient(chocolate, sort); + List recs = (List)asCollection(recipes); + assertThat( recs.size(), equalTo(2)); + assertEquals("Nigella" , recs.get(0).author); + assertEquals("Heston" , recs.get(1).author); + } } 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 e639bf9da..09db976d5 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 @@ -17,15 +17,10 @@ package org.springframework.data.neo4j.repository; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.neo4j.cypher.ExecutionEngine; -import org.neo4j.cypher.ExecutionResult; -import org.neo4j.graphdb.DynamicRelationshipType; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Transaction; import org.neo4j.helpers.collection.IteratorUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,7 +46,10 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -146,7 +144,43 @@ public class GraphRepositoryTests { assertThat(personRepository.exists(testTeam.michael.getId()), is(false)); } - @Test @Transactional + @Test + public void findAll() { + Iterable allPersons = personRepository.findAll(); + assertThat(asCollection(allPersons), hasItems(testTeam.michael, testTeam.david, testTeam.emil)); + } + + @Test + public void findAllSortedAscending() { + Sort sort = new Sort(Sort.Direction.ASC, "name"); + Iterable allPersons = personRepository.findAll(sort); + assertEquals(asList(testTeam.david, testTeam.emil, testTeam.michael), asCollection(allPersons)); + } + + @Test + public void findAllSortedDescending() { + Sort sort = new Sort(Sort.Direction.DESC, "name"); + Iterable allPersons = personRepository.findAll(sort); + assertEquals(asList(testTeam.michael, testTeam.emil, testTeam.david), asCollection(allPersons)); + } + + @Test + public void findAllPageableWithSortDescending() { + Sort sort = new Sort(Sort.Direction.DESC, "name"); + PageRequest page1Request = new PageRequest(0, 1, sort); + PageRequest page2Request = new PageRequest(1, 1, sort); + PageRequest page3Request = new PageRequest(2, 1, sort); + + Iterable page1Result = personRepository.findAll(page1Request); + Iterable page2Result = personRepository.findAll(page2Request); + Iterable page3Result = personRepository.findAll(page3Request); + + assertEquals (asList(testTeam.michael), asCollection(page1Result)); + assertEquals (asList(testTeam.emil), asCollection(page2Result)); + assertEquals (asList(testTeam.david), asCollection(page3Result)); + } + + @Test @Transactional public void testFindIterableOfPersonWithQueryAnnotation() { Iterable teamMembers = personRepository.findAllTeamMembers(testTeam.sdg); assertThat( asCollection( teamMembers ), hasItems( testTeam.michael, testTeam.david, testTeam.emil ) );