From 81ef32499e589dbf1eb4d782586b5c479069ba73 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Mon, 10 Sep 2012 02:36:21 +0200 Subject: [PATCH] DATAGRAPH-294 derived finder methods for numerically indexed values --- .../mapping/Neo4jPersistentProperty.java | 2 + .../neo4j/repository/query/StartClause.java | 5 +- .../mapping/Neo4JPersistentPropertyImpl.java | 31 ++++++--- .../neo4j/repository/DerivedFinderTests.java | 35 +++++++++-- .../query/DerivedFinderMethodTest.java | 17 ++++- .../repository/query/ThingRepository.java | 63 ++++++++++--------- 6 files changed, 106 insertions(+), 47 deletions(-) diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java index 79c3c5b79..125d60603 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java @@ -99,4 +99,6 @@ public interface Neo4jPersistentProperty extends PersistentProperty getTargetType(); boolean isTargetTypeEnforced(); + + boolean isIndexedNumerically(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClause.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClause.java index 78cf68eb0..d941d4533 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClause.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClause.java @@ -21,6 +21,7 @@ import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.Neo4jTemplate; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.parser.Part; +import org.neo4j.index.lucene.ValueContext; import java.util.*; @@ -73,7 +74,8 @@ class StartClause { if (shouldRenderQuery()) { result.put(firstParam, renderQuery(values)); } else { - result.put(firstParam, IteratorUtil.first(values.values())); + Object value=IteratorUtil.first(values.values()); + result.put(firstParam, value); } return result; } @@ -102,6 +104,7 @@ class StartClause { } private Object convertIfNecessary(Neo4jTemplate template, Object value, Neo4jPersistentProperty property) { + if (property.isIndexedNumerically()) return new ValueContext(value).indexNumeric(); if (property.isNeo4jPropertyType() && property.isNeo4jPropertyValue(value)) return value; PropertyConverter converter = new PropertyConverter(template.getConversionService(), property); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/mapping/Neo4JPersistentPropertyImpl.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/mapping/Neo4JPersistentPropertyImpl.java index 455a22bf1..5532026c1 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/mapping/Neo4JPersistentPropertyImpl.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/mapping/Neo4JPersistentPropertyImpl.java @@ -225,22 +225,33 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty fieldType) { + return (fieldType.isPrimitive() && !fieldType.equals(boolean.class) && !fieldType.equals(void.class)) + || fieldType.equals(Character.class) + || (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType)); + } + + public boolean isIndexedNumerically() { + if (!isIndexed() || !getIndexInfo().isNumeric()) return false; + return isNumeric(getType()) || isNumeric(getPropertyType()) || + (getType().isArray() && !getType().getComponentType().isArray() && isNumeric(getType().getComponentType())); + } + private static boolean isNeo4jPropertyType(final Class fieldType) { // todo: add array support - return fieldType.isPrimitive() - || fieldType.equals(String.class) - || fieldType.equals(Character.class) + return fieldType.equals(String.class) || fieldType.equals(Boolean.class) - || (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType)) + || fieldType.equals(boolean.class) + || isNumeric(fieldType) || (fieldType.isArray() && !fieldType.getComponentType().isArray() && isNeo4jPropertyType(fieldType.getComponentType())); } @Override public boolean isNeo4jPropertyValue(Object value) { - if (value == null || value.getClass().isArray()) { - return false; - } - return isNeo4jPropertyType(value.getClass()); + if (value == null || value.getClass().isArray()) { + return false; + } + return isNeo4jPropertyType(value.getClass()); } public boolean isSyntheticField() { @@ -336,8 +347,8 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty annotationType : annotations.keySet()) { 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 8fe34807c..293db0a95 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 @@ -24,10 +24,7 @@ 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.neo4j.annotation.Fetch; -import org.springframework.data.neo4j.annotation.GraphId; -import org.springframework.data.neo4j.annotation.NodeEntity; -import org.springframework.data.neo4j.annotation.RelatedTo; +import org.springframework.data.neo4j.annotation.*; import org.springframework.data.neo4j.config.EnableNeo4jRepositories; import org.springframework.data.neo4j.config.Neo4jConfiguration; import org.springframework.data.neo4j.support.Neo4jTemplate; @@ -59,6 +56,20 @@ class Ingredient { this.name = name; } } +@NodeEntity +class Dish { + @GraphId + Long id; + + @Indexed(unique = true) int number; + + Dish() { + } + + Dish(int number) { + this.number = number; + } +} @NodeEntity class CookBook { @@ -103,6 +114,10 @@ class Recipe { } } +interface DishRepository extends GraphRepository { + Dish findByNumber(int number); +} + interface RecipeRepository extends GraphRepository { Set findById(long id); @@ -127,6 +142,8 @@ interface RecipeRepository extends GraphRepository { @ContextConfiguration public class DerivedFinderTests { + private Dish dish; + @Configuration @EnableNeo4jRepositories static class TestConfig extends Neo4jConfiguration { @@ -147,6 +164,9 @@ public class DerivedFinderTests { @Autowired private RecipeRepository recipeRepository; + @Autowired + private DishRepository dishRepository; + private Ingredient fish, spice, oliveOil, pear; private CookBook nakedChef, baking101; @@ -172,6 +192,7 @@ 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)); + dish = dishRepository.save(new Dish(100)); transaction.success(); } finally { transaction.finish(); @@ -250,4 +271,10 @@ public class DerivedFinderTests { assertThat(single(recipes).title, is(equalTo("pesto"))); } + @Test + public void shouldFindUsingIndexedNumericValue() throws Exception { + Dish foundDish = dishRepository.findByNumber(100); + + assertThat(foundDish.number, is(equalTo(dish.number))); + } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/DerivedFinderMethodTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/DerivedFinderMethodTest.java index a8d02a472..bcedf6ed4 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/DerivedFinderMethodTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/DerivedFinderMethodTest.java @@ -46,6 +46,8 @@ import java.util.concurrent.TimeUnit; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import org.neo4j.index.lucene.ValueContext; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class}) @@ -57,6 +59,8 @@ public class DerivedFinderMethodTest { Long id; @Indexed String firstName; + @Indexed + int number; @Indexed String lastName; @@ -299,6 +303,13 @@ public class DerivedFinderMethodTest { param.getTime()); } + @Test + public void testFindByNumericIndexedField() throws Exception { + assertRepositoryQueryMethod(ThingRepository.class, "findByNumber", new Object[]{10}, + "START `thing`=node:`Thing`(`number`={0})", + ValueContext.numeric(10)); + } + private void assertRepositoryQueryMethod(Class repositoryClass, String methodName, Object[] paramValues, String expectedQuery, Object...expectedParam) { Method method = methodFor(repositoryClass, methodName); DerivedCypherRepositoryQuery derivedCypherRepositoryQuery = new DerivedCypherRepositoryQuery(ctx, new GraphQueryMethod(method, new DefaultRepositoryMetadata(repositoryClass), null, ctx), template); @@ -310,7 +321,11 @@ public class DerivedFinderMethodTest { assertEquals(expectedQuery,query.substring(query.indexOf(firstWord)).substring(0,expectedQuery.length())); assertEquals(expectedParam.length,params.size()); for (int i = 0; i < expectedParam.length; i++) { - assertEquals(expectedParam[i],params.get(String.valueOf(i))); + if (expectedParam[i] instanceof ValueContext) { + assertEquals(((ValueContext)expectedParam[i]).getValue(),((ValueContext)params.get(String.valueOf(i))).getValue()); + } else { + assertEquals(expectedParam[i],params.get(String.valueOf(i))); + } } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/ThingRepository.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/ThingRepository.java index 152f3db42..775c55db1 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/ThingRepository.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/ThingRepository.java @@ -21,35 +21,36 @@ import java.util.Collection; import java.util.Date; public interface ThingRepository extends GraphRepository { - public DerivedFinderMethodTest.Thing findByFirstNameAndLastName(String firstName, String lastName); - public DerivedFinderMethodTest.Thing findByFirstName(String firstName); - public DerivedFinderMethodTest.Thing findByDescription(String firstName); - public DerivedFinderMethodTest.Thing findByDescriptionAndFirstName(String description,String firstName); - public DerivedFinderMethodTest.Thing findByFirstNameAndDescription(String firstName,String description); - public DerivedFinderMethodTest.Thing findByAge(int age); - public DerivedFinderMethodTest.Thing findByAgeAndFirstName(int age,String firstName); - public DerivedFinderMethodTest.Thing findByFirstNameLike(String firstName); - public DerivedFinderMethodTest.Thing findByFirstNameContains(String firstName); - public DerivedFinderMethodTest.Thing findByFirstNameEndsWith(String firstName); - public DerivedFinderMethodTest.Thing findByFirstNameStartsWith(String firstName); - - public DerivedFinderMethodTest.Thing findByName(String name); - public DerivedFinderMethodTest.Thing findByNameStartsWith(String name); - public DerivedFinderMethodTest.Thing findByNameEndsWith(String name); - public DerivedFinderMethodTest.Thing findByNameContains(String name); - - public DerivedFinderMethodTest.Thing findByNameLike(String name); - public DerivedFinderMethodTest.Thing findByNameNotLike(String name); - public DerivedFinderMethodTest.Thing findByNameMatches(String name); - public DerivedFinderMethodTest.Thing findByTaggedIsTrue(); - public DerivedFinderMethodTest.Thing findByTaggedIsFalse(); - - public DerivedFinderMethodTest.Thing findByNameExists(); - public DerivedFinderMethodTest.Thing findByNameIn(Collection values); - public DerivedFinderMethodTest.Thing findByNameNotIn(Collection values); - - public DerivedFinderMethodTest.Thing findByBornBefore(Date date); - public DerivedFinderMethodTest.Thing findByBornAfter(Date date); - public DerivedFinderMethodTest.Thing findById(long id); - public DerivedFinderMethodTest.Thing findByOwnerId(long id); + DerivedFinderMethodTest.Thing findByFirstNameAndLastName(String firstName, String lastName); + DerivedFinderMethodTest.Thing findByFirstName(String firstName); + DerivedFinderMethodTest.Thing findByDescription(String firstName); + DerivedFinderMethodTest.Thing findByDescriptionAndFirstName(String description,String firstName); + DerivedFinderMethodTest.Thing findByFirstNameAndDescription(String firstName,String description); + DerivedFinderMethodTest.Thing findByAge(int age); + DerivedFinderMethodTest.Thing findByAgeAndFirstName(int age,String firstName); + DerivedFinderMethodTest.Thing findByFirstNameLike(String firstName); + DerivedFinderMethodTest.Thing findByFirstNameContains(String firstName); + DerivedFinderMethodTest.Thing findByFirstNameEndsWith(String firstName); + DerivedFinderMethodTest.Thing findByFirstNameStartsWith(String firstName); + DerivedFinderMethodTest.Thing findByNumber(int number); + + DerivedFinderMethodTest.Thing findByName(String name); + DerivedFinderMethodTest.Thing findByNameStartsWith(String name); + DerivedFinderMethodTest.Thing findByNameEndsWith(String name); + DerivedFinderMethodTest.Thing findByNameContains(String name); + + DerivedFinderMethodTest.Thing findByNameLike(String name); + DerivedFinderMethodTest.Thing findByNameNotLike(String name); + DerivedFinderMethodTest.Thing findByNameMatches(String name); + DerivedFinderMethodTest.Thing findByTaggedIsTrue(); + DerivedFinderMethodTest.Thing findByTaggedIsFalse(); + + DerivedFinderMethodTest.Thing findByNameExists(); + DerivedFinderMethodTest.Thing findByNameIn(Collection values); + DerivedFinderMethodTest.Thing findByNameNotIn(Collection values); + + DerivedFinderMethodTest.Thing findByBornBefore(Date date); + DerivedFinderMethodTest.Thing findByBornAfter(Date date); + DerivedFinderMethodTest.Thing findById(long id); + DerivedFinderMethodTest.Thing findByOwnerId(long id); }