indexing, short property-names as default

This commit is contained in:
Michael Hunger
2010-09-07 00:45:02 +02:00
parent c92390388d
commit 8d69cda2a9
11 changed files with 128 additions and 46 deletions

View File

@@ -12,5 +12,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface GraphEntity {
boolean useShortNames() default false;
boolean useShortNames() default true;
boolean fullIndex() default false;
}

View File

@@ -9,6 +9,9 @@ import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.index.IndexHits;
import org.neo4j.index.IndexService;
import org.springframework.datastore.graph.neo4j.spi.node.Neo4jHelper;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.persistence.support.EntityInstantiator;
@@ -17,13 +20,15 @@ public class Finder<T extends NodeBacked> {
private final Class<T> clazz;
private final GraphDatabaseService graphDatabaseService;
private final IndexService indexService;
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
public Finder(Class<T> clazz, GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
public Finder(Class<T> clazz, GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, IndexService indexService) {
this.clazz = clazz;
this.graphDatabaseService = graphDatabaseService;
this.graphEntityInstantiator = graphEntityInstantiator;
}
this.indexService = indexService;
}
public long count() {
return Neo4jHelper.count(clazz, graphDatabaseService);
@@ -32,13 +37,13 @@ public class Finder<T extends NodeBacked> {
public Iterable<T> findAll() {
Node subrefNode = Neo4jHelper.findSubreferenceNode(clazz, graphDatabaseService);
if (subrefNode==null) return Collections.emptyList();
// TODO add lazy list on top of graph
List<T> result = new ArrayList<T>((int) count());
for (Relationship rel : subrefNode.getRelationships(Neo4jHelper.INSTANCE_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) {
Node node = rel.getStartNode();
result.add(graphEntityInstantiator.createEntityFromState(node, clazz));
}
return result;
return new IterableWrapper<T,Relationship>(subrefNode.getRelationships(Neo4jHelper.INSTANCE_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) {
@Override
protected T underlyingObjectToObject(Relationship rel) {
Node node = rel.getStartNode();
return graphEntityInstantiator.createEntityFromState(node, clazz);
}
};
}
public T findById(long id) {
@@ -48,4 +53,30 @@ public class Finder<T extends NodeBacked> {
return null;
}
}
public T getByIndex(String property, Object value) {
try {
final Node node = indexService.getSingleNode(property, value);
if (node==null) return null;
return graphEntityInstantiator.createEntityFromState(node, clazz);
} catch(NotFoundException e) {
return null;
}
}
public Iterable<T> getAllByIndex(String property, Object value) {
try {
final IndexHits<Node> nodes = indexService.getNodes(property, value);
if (nodes==null) return Collections.emptyList();
return new IterableWrapper<T,Node>(nodes) {
@Override
protected T underlyingObjectToObject(Node node) {
return graphEntityInstantiator.createEntityFromState(node, clazz);
}
};
} catch(NotFoundException e) {
return null;
}
}
}

View File

@@ -2,6 +2,7 @@ package org.springframework.datastore.graph.neo4j.finder;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.index.IndexService;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.neo4j.spi.node.Neo4jHelper;
import org.springframework.persistence.support.EntityInstantiator;
@@ -10,14 +11,16 @@ public class FinderFactory {
private final GraphDatabaseService graphDatabaseService;
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private final IndexService indexService;
public FinderFactory(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
public FinderFactory(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, IndexService indexService) {
this.graphDatabaseService = graphDatabaseService;
this.graphEntityInstantiator = graphEntityInstantiator;
this.indexService = indexService;
}
public <T extends NodeBacked> Finder<T> getFinderForClass(Class<T> clazz) {
return new Finder<T>(clazz, graphDatabaseService, graphEntityInstantiator);
return new Finder<T>(clazz, graphDatabaseService, graphEntityInstantiator, indexService);
}
public Class<NodeBacked> getEntityClass(String shortName) {

View File

@@ -2,15 +2,14 @@ package org.springframework.datastore.graph.neo4j.jpa;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.Transaction;
import org.neo4j.index.IndexService;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.neo4j.finder.FinderFactory;
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.persistence.*;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.transaction.*;
@@ -25,24 +24,26 @@ import java.util.Map;
@Transactional
@Configurable
public class Neo4jEntityManager implements EntityManager {
GraphDatabaseService graphDatabaseService;
EntityInstantiator<NodeBacked, Node> nodeInstantiator;
private GraphDatabaseService graphDatabaseService;
private EntityInstantiator<NodeBacked, Node> nodeInstantiator;
private PersistenceUnitInfo info;
private Map params;
private IndexService indexService;
private volatile boolean closed;
private final FinderFactory finderFactory;
public Neo4jEntityManager(final GraphDatabaseService graphDatabaseService, final EntityInstantiator<NodeBacked, Node> nodeInstantiator, PersistenceUnitInfo info, Map params) {
public Neo4jEntityManager(final GraphDatabaseService graphDatabaseService, final EntityInstantiator<NodeBacked, Node> nodeInstantiator, PersistenceUnitInfo info, Map params, IndexService indexService) {
this.graphDatabaseService = graphDatabaseService;
this.nodeInstantiator = nodeInstantiator;
this.info = info;
this.params = params;
finderFactory = new FinderFactory(graphDatabaseService, nodeInstantiator);
this.indexService = indexService;
finderFactory = new FinderFactory(graphDatabaseService, nodeInstantiator, indexService);
}
public Neo4jEntityManager() {
finderFactory = new FinderFactory(graphDatabaseService, nodeInstantiator);
finderFactory = new FinderFactory(graphDatabaseService, nodeInstantiator, indexService);
}
private Node nodeFor(final Object entity) {

View File

@@ -2,10 +2,10 @@ package org.springframework.datastore.graph.neo4j.jpa;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.index.IndexService;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.persistence.support.EntityInstantiator;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitInfo;
@@ -20,23 +20,25 @@ public class Neo4jEntityManagerFactory implements EntityManagerFactory {
EntityInstantiator<NodeBacked, Node> nodeInstantiator;
private PersistenceUnitInfo info;
private Map params;
private IndexService indexService;
public Neo4jEntityManagerFactory(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> nodeInstantiator, PersistenceUnitInfo info, Map params) {
public Neo4jEntityManagerFactory(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> nodeInstantiator, IndexService indexService, PersistenceUnitInfo info, Map params) {
this.graphDatabaseService = graphDatabaseService;
this.nodeInstantiator = nodeInstantiator;
this.indexService = indexService;
this.info = info;
this.params = params;
}
@Override
public EntityManager createEntityManager() {
return new Neo4jEntityManager(graphDatabaseService,nodeInstantiator,info,params);
return new Neo4jEntityManager(graphDatabaseService,nodeInstantiator,info,params, indexService);
}
/* TODO handle different directories for target datastore */
@Override
public EntityManager createEntityManager(Map map) {
return new Neo4jEntityManager(graphDatabaseService,nodeInstantiator,info,params);
return new Neo4jEntityManager(graphDatabaseService,nodeInstantiator,info,params, indexService);
}
@Override

View File

@@ -2,9 +2,9 @@ package org.springframework.datastore.graph.neo4j.jpa;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.index.IndexService;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.neo4j.spi.node.Neo4jHelper;
import org.springframework.persistence.support.EntityInstantiator;
import javax.annotation.Resource;
@@ -21,16 +21,18 @@ import java.util.Map;
@Configurable
public class Neo4jPersistenceProvider implements PersistenceProvider {
@Resource
GraphDatabaseService graphDatabaseService;
private GraphDatabaseService graphDatabaseService;
@Resource
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
@Resource
private IndexService indexService;
@Override
public EntityManagerFactory createEntityManagerFactory(String emName, Map params) {
System.out.println("emName = " + emName);
System.out.println("params = " + params);
return new Neo4jEntityManagerFactory(graphDatabaseService,graphEntityInstantiator,null,params);
return new Neo4jEntityManagerFactory(graphDatabaseService,graphEntityInstantiator, indexService, null,params);
}
@Override
@@ -42,6 +44,6 @@ public class Neo4jPersistenceProvider implements PersistenceProvider {
System.out.println("info.getProperties() = " + info.getProperties());
System.out.println("info.getPersistenceUnitName() = " + info.getPersistenceUnitName());
System.out.println("params = " + params);
return new Neo4jEntityManagerFactory(graphDatabaseService,graphEntityInstantiator,info,params);
return new Neo4jEntityManagerFactory(graphDatabaseService,graphEntityInstantiator, indexService, info,params);
}
}

View File

@@ -8,6 +8,7 @@ import java.util.*;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.*;
import org.neo4j.index.IndexService;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.util.GraphDatabaseUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -47,13 +48,15 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
private FieldAccessorFactory fieldAccessorFactory;
private EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
@Autowired
public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
private IndexService indexService;
@Autowired
public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator, IndexService indexService) {
this.graphDatabaseService = gds;
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
this.fieldAccessorFactory = new FieldAccessorFactory(graphEntityInstantiator, relationshipEntityInstantiator);
this.indexService = indexService;
this.fieldAccessorFactory = new FieldAccessorFactory(graphEntityInstantiator, relationshipEntityInstantiator);
}
@@ -151,14 +154,14 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
/*
public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N node, Class<R> relationshipType, String type) {
Relationship rel = this.getUnderlyingNode().createRelationshipTo(node.getUnderlyingNode(), DynamicRelationshipType.withName(type));
return relationshipEntityInstantiator.createEntityFromState(rel, relationshipType);
return (R)createRelationshipEntity(relationshipType,rel);
// relationshipEntityInstantiator.createEntityFromState(rel, relationshipType);
}
*/
public RelationshipBacked NodeBacked.relateTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
Relationship rel = this.getUnderlyingNode().createRelationshipTo(node.getUnderlyingNode(), DynamicRelationshipType.withName(type));
return createRelationshipEntity(relationshipType,rel);
}
private static RelationshipBacked createRelationshipEntity(Class<? extends RelationshipBacked> relationshipType, Relationship rel) {
try {
final Constructor<? extends RelationshipBacked> constructor = relationshipType.getDeclaredConstructor();
@@ -298,10 +301,13 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
if (Modifier.isFinal(field.getModifiers())) return new ShouldProceedOrReturn(newVal);
if (isPropertyType(field.getType())) {
String propName = FieldAccessorFactory.getNeo4jPropertyName(field);
Node node = entity.getUnderlyingNode();
if (newVal==null) {
entity.getUnderlyingNode().removeProperty(propName);
node.removeProperty(propName);
if (isIndexedProperty(field)) indexService.removeIndex(node,propName);
} else {
entity.getUnderlyingNode().setProperty(propName, newVal);
node.setProperty(propName, newVal);
if (isIndexedProperty(field)) indexService.index(node,propName,newVal);
}
log.info("SET " + field + " -> Neo4J simple node property [" + propName + "] with value=[" + newVal + "]");
return new ShouldProceedOrReturn(true,newVal);
@@ -379,5 +385,12 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
|| fieldType.equals(Boolean.class)
|| (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType));
}
// todo @property annotation
// todo fieldlist in @graphentity
private boolean isIndexedProperty(Field field) {
final GraphEntity graphEntity = field.getDeclaringClass().getAnnotation(GraphEntity.class);
if (graphEntity==null) return false;
return graphEntity.fullIndex();
}
}

View File

@@ -6,7 +6,7 @@ import org.springframework.datastore.graph.api.GraphEntityRelationship;
import java.util.Collection;
@GraphEntity(useShortNames = true)
@GraphEntity(fullIndex = true)
public class Group {
@GraphEntityRelationship(type = "persons", direction = Direction.OUTGOING, elementClass = Person.class)

View File

@@ -8,7 +8,7 @@ import org.springframework.datastore.graph.api.GraphEntityRelationship;
import org.springframework.datastore.graph.api.GraphEntityRelationshipEntity;
@GraphEntity
@GraphEntity(useShortNames = false)
public class Person {
private Long id;

View File

@@ -1,18 +1,16 @@
package org.springframework.datastore.graph.neo4j.spi;
import junit.framework.Assert;
import org.junit.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.*;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.index.IndexService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.neo4j.Friendship;
import org.springframework.datastore.graph.neo4j.Group;
@@ -42,8 +40,11 @@ public class Neo4jGraphPersistenceTest {
@Autowired
private FinderFactory finderFactory;
@Test
@Autowired
private IndexService indexService;
@Test
public void testStuffWasAutowired() {
Assert.assertNotNull( graphDatabaseService );
Assert.assertNotNull( graphEntityInstantiator );
@@ -214,7 +215,7 @@ public class Neo4jGraphPersistenceTest {
@Test
public void testFindOutsideTransaction() {
final FinderFactory factory = new FinderFactory(graphDatabaseService, graphEntityInstantiator);
final FinderFactory factory = new FinderFactory(graphDatabaseService, graphEntityInstantiator, indexService);
final Finder<Person> finder = factory.getFinderForClass(Person.class);
Assert.assertEquals(false,finder.findAll().iterator().hasNext());
}
@@ -399,4 +400,25 @@ public class Neo4jGraphPersistenceTest {
Group group = new Group();
group.setReadOnlyPersons(new HashSet<Person>());
}
@Test(expected = InvalidDataAccessApiUsageException.class)
@Transactional
public void testFindGroupByIndex() {
Group group = new Group();
group.setName("test");
final Finder<Group> finder = finderFactory.getFinderForClass(Group.class);
final Group found = finder.getByIndex("name", "test");
Assert.assertEquals(group,found);
}
@Test(expected = InvalidDataAccessApiUsageException.class)
@Transactional
public void testFindAllGroupsByIndex() {
Group group = new Group();
group.setName("test");
Group group2 = new Group();
group.setName("test");
final Finder<Group> finder = finderFactory.getFinderForClass(Group.class);
final Iterable<Group> found = finder.getAllByIndex("name", "test");
final Collection<Group> result = IteratorUtil.addToCollection(found.iterator(), new HashSet<Group>());
Assert.assertEquals(new HashSet<Group>(Arrays.asList(group,group2)), result);
}
}

View File

@@ -83,10 +83,16 @@
destroy-method="shutdown" scope="singleton">
<constructor-arg index="0" value="${neo4j.databaseDirectory}" />
</bean>
<bean id="indexService" class="org.neo4j.index.lucene.LuceneIndexService" destroy-method="shutdown">
<constructor-arg index="0" ref="graphDbService" />
</bean>
<bean id="finderFactory" class="org.springframework.datastore.graph.neo4j.finder.FinderFactory">
<constructor-arg ref="graphDbService" />
<constructor-arg ref="graphEntityInstantiator" />
<constructor-arg ref="indexService" />
</bean>
<bean id="graphEntityInstantiator" name="gei" class="org.springframework.datastore.graph.neo4j.spi.node.Neo4jConstructorGraphEntityInstantiator" />