DATAGRAPH-214 numeric fields can be indexed non-numerically
DATAGRAPH-181 fix for creating unique entities with numeric unique fields, still needs a fix in Neo4j
This commit is contained in:
@@ -89,6 +89,9 @@ public class Group {
|
||||
private String indexLevelName;
|
||||
private String[] roleNames;
|
||||
|
||||
@Indexed(numeric = false)
|
||||
private Byte secret;
|
||||
|
||||
public Date getCreationDate() {
|
||||
return creationDate;
|
||||
}
|
||||
@@ -285,4 +288,12 @@ public class Group {
|
||||
public void setMentorship(Mentorship mentorship) {
|
||||
this.mentorship = mentorship;
|
||||
}
|
||||
|
||||
public Byte getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(Byte secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.Transaction;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
@@ -265,6 +266,22 @@ public class IndexTest extends EntityTestBase {
|
||||
assertEquals(new HashSet<Group>(Arrays.asList(group, group2)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFindAllGroupsByNonNumericIndexedNumber() {
|
||||
final Group group = new Group();
|
||||
final byte value = (byte) 100;
|
||||
group.setSecret(value);
|
||||
groupRepository.save(group);
|
||||
final PropertyContainer node = neo4jTemplate.getPersistentState(group);
|
||||
final Iterable<Group> found = this.groupRepository.findAllByPropertyValue("secret", value);
|
||||
assertEquals(1, IteratorUtil.count(found));
|
||||
final Node foundWithTemplate = neo4jTemplate.lookup("Group","secret", value).to(Node.class).singleOrNull();
|
||||
assertEquals(node, foundWithTemplate);
|
||||
final Node foundGroup = neo4jTemplate.getGraphDatabaseService().index().forNodes("Group").get("secret", value).getSingle();
|
||||
assertEquals(node, foundGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void shouldFindGroupyByQueryString() {
|
||||
|
||||
@@ -41,6 +41,8 @@ public @interface Indexed {
|
||||
|
||||
boolean unique() default false;
|
||||
|
||||
boolean numeric() default true;
|
||||
|
||||
// FQN is a fix for javac compiler bug http://bugs.sun.com/view_bug.do?bug_id=6512707
|
||||
org.springframework.data.neo4j.annotation.Indexed.Level level() default org.springframework.data.neo4j.annotation.Indexed.Level.CLASS;
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ public class IndexingPropertyFieldAccessorListenerFactory<S extends PropertyCont
|
||||
@Override
|
||||
public void valueChanged(Object entity, Object oldVal, Object newVal) {
|
||||
@SuppressWarnings("unchecked") Index<T> index = template.getIndex(property, entity.getClass());
|
||||
if (newVal instanceof Number) newVal = ValueContext.numeric((Number) newVal);
|
||||
if (newVal instanceof Number && property.getIndexInfo().isNumeric()) newVal = ValueContext.numeric((Number) newVal);
|
||||
|
||||
final T state = template.getPersistentState(entity);
|
||||
index.remove(state, indexKey);
|
||||
|
||||
@@ -29,6 +29,7 @@ public class IndexInfo {
|
||||
private final Indexed.Level level;
|
||||
private String indexKey;
|
||||
private final boolean unique;
|
||||
private boolean numeric;
|
||||
|
||||
public IndexInfo(Indexed annotation, Neo4jPersistentProperty property) {
|
||||
this.indexName = determineIndexName(annotation, property);
|
||||
@@ -37,6 +38,7 @@ public class IndexInfo {
|
||||
this.indexKey = fieldName.isEmpty() ? property.getNeo4jPropertyName() : fieldName;
|
||||
unique = annotation.unique();
|
||||
level = annotation.level();
|
||||
numeric = annotation.numeric();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,4 +68,8 @@ public class IndexInfo {
|
||||
public boolean isUnique() {
|
||||
return unique;
|
||||
}
|
||||
|
||||
public boolean isNumeric() {
|
||||
return numeric;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.data.domain.Sort;
|
||||
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.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
|
||||
import org.springframework.data.neo4j.support.index.NullReadableIndex;
|
||||
@@ -177,12 +178,13 @@ public abstract class AbstractGraphRepository<S extends PropertyContainer, T> im
|
||||
|
||||
}
|
||||
|
||||
private IndexHits<S> getIndexHits(String indexName, String property, Object value) {
|
||||
if (value instanceof Number) {
|
||||
private IndexHits<S> getIndexHits(String indexName, String propertyName, Object value) {
|
||||
final Neo4jPersistentProperty property = template.getPersistentProperty(clazz, propertyName);
|
||||
if (value instanceof Number && (property==null || property.getIndexInfo().isNumeric())) {
|
||||
Number number = (Number) value;
|
||||
return getIndex(indexName, property).query(property, createInclusiveRangeQuery(property, number,number));
|
||||
return getIndex(indexName, propertyName).query(propertyName, createInclusiveRangeQuery(propertyName, number,number));
|
||||
}
|
||||
return getIndex(indexName, property).get(property, value);
|
||||
return getIndex(indexName, propertyName).get(propertyName, value);
|
||||
}
|
||||
|
||||
protected ReadableIndex<S> getIndex(String indexName, String property) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
import org.neo4j.index.lucene.ValueContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@@ -583,12 +584,19 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> getIndex(Class<?> indexedType, String propertyName) {
|
||||
final Neo4jPersistentEntityImpl<?> persistentEntity = getPersistentEntity(indexedType);
|
||||
final Neo4jPersistentProperty property = persistentEntity.getPersistentProperty(propertyName);
|
||||
final Neo4jPersistentProperty property = getPersistentProperty(indexedType, propertyName);
|
||||
if (property==null) return getIndexProvider().getIndex(indexedType,null);
|
||||
return getIndexProvider().getIndex(property, indexedType);
|
||||
}
|
||||
|
||||
public Neo4jPersistentProperty getPersistentProperty(Class<?> type, String propertyName) {
|
||||
if (type==null || propertyName==null) return null;
|
||||
final Neo4jPersistentEntityImpl<?> persistentEntity = getPersistentEntity(type);
|
||||
final int dotIndex = propertyName.lastIndexOf(".");
|
||||
if (dotIndex >-1) propertyName = propertyName.substring(dotIndex, propertyName.length());
|
||||
return persistentEntity.getPersistentProperty(propertyName);
|
||||
}
|
||||
|
||||
private IndexProvider getIndexProvider() {
|
||||
return infrastructure.getIndexProvider();
|
||||
}
|
||||
@@ -677,9 +685,10 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
public Node createUniqueNode(Object entity) {
|
||||
final Neo4jPersistentEntityImpl<?> persistentEntity = getPersistentEntity(entity.getClass());
|
||||
final Neo4jPersistentProperty uniqueProperty = persistentEntity.getUniqueProperty();
|
||||
final Object value = uniqueProperty.getValueFromEntity(entity, MappingPolicy.MAP_FIELD_DIRECT_POLICY);
|
||||
Object value = uniqueProperty.getValueFromEntity(entity, MappingPolicy.MAP_FIELD_DIRECT_POLICY);
|
||||
if (value==null) return createNode();
|
||||
final IndexInfo indexInfo = uniqueProperty.getIndexInfo();
|
||||
if (value instanceof Number && indexInfo.isNumeric()) value=ValueContext.numeric((Number)value);
|
||||
return getGraphDatabase().getOrCreateNode(indexInfo.getIndexName(), indexInfo.getIndexKey(), value, Collections.<String, Object>emptyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.neo4j.unique.domain.Club;
|
||||
import org.springframework.data.neo4j.unique.domain.InvalidClub;
|
||||
import org.springframework.data.neo4j.unique.domain.UniqueClub;
|
||||
import org.springframework.data.neo4j.unique.domain.UniqueNumericIdClub;
|
||||
import org.springframework.data.neo4j.unique.repository.ClubRepository;
|
||||
import org.springframework.data.neo4j.unique.repository.InvalidClubRepository;
|
||||
import org.springframework.data.neo4j.unique.repository.UniqueClubRepository;
|
||||
import org.springframework.data.neo4j.unique.repository.UniqueNumericIdClubRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -51,6 +53,9 @@ public class UniqueEntityTest {
|
||||
@Autowired
|
||||
protected GraphDatabaseService graphDatabaseService;
|
||||
|
||||
@Autowired
|
||||
private UniqueNumericIdClubRepository uniqueNumericIdClubRepository;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
clubRepository.deleteAll();
|
||||
@@ -69,6 +74,17 @@ public class UniqueEntityTest {
|
||||
|
||||
assertEquals(1, uniqueClubRepository.count());
|
||||
}
|
||||
@Test
|
||||
public void shouldOnlyCreateSingleInstanceForUniqueNumericNodeEntity() {
|
||||
UniqueNumericIdClub club = new UniqueNumericIdClub();
|
||||
club.setClubId(100L);
|
||||
uniqueNumericIdClubRepository.save(club);
|
||||
|
||||
club = new UniqueNumericIdClub(100L);
|
||||
uniqueNumericIdClubRepository.save(club);
|
||||
|
||||
assertEquals(1, uniqueNumericIdClubRepository.count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateMultipleInstancesForNonUniqueNodeEntity() {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.unique.domain;
|
||||
|
||||
import org.springframework.data.neo4j.annotation.GraphId;
|
||||
import org.springframework.data.neo4j.annotation.Indexed;
|
||||
import org.springframework.data.neo4j.annotation.NodeEntity;
|
||||
|
||||
@NodeEntity
|
||||
public class UniqueNumericIdClub {
|
||||
|
||||
@Indexed(unique = true, numeric = false)
|
||||
private Long clubId;
|
||||
|
||||
@GraphId
|
||||
Long id;
|
||||
|
||||
public UniqueNumericIdClub() {
|
||||
}
|
||||
|
||||
public UniqueNumericIdClub(Long clubId) {
|
||||
this.clubId = clubId;
|
||||
}
|
||||
|
||||
public Long getClubId() {
|
||||
return clubId;
|
||||
}
|
||||
|
||||
public void setClubId(Long clubId) {
|
||||
this.clubId = clubId;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright 2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.unique.repository;
|
||||
|
||||
import org.springframework.data.neo4j.repository.GraphRepository;
|
||||
import org.springframework.data.neo4j.repository.NamedIndexRepository;
|
||||
import org.springframework.data.neo4j.unique.domain.Club;
|
||||
import org.springframework.data.neo4j.unique.domain.UniqueClub;
|
||||
import org.springframework.data.neo4j.unique.domain.UniqueNumericIdClub;
|
||||
|
||||
public interface UniqueNumericIdClubRepository extends GraphRepository<UniqueNumericIdClub>, NamedIndexRepository<UniqueNumericIdClub> {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user