Neo4j Template Api Evolution

This commit is contained in:
Michael Hunger
2011-02-22 15:13:03 +01:00
parent 7b170e42f2
commit aa9c09b6b4
13 changed files with 208 additions and 671 deletions

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2010 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.graph.neo4j.template;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
public class GraphDescription
{
private Map<String, NodeInfo> nodes = new LinkedHashMap<String, NodeInfo>();
public GraphDescription(final Properties props)
{
if (props == null)
throw new IllegalArgumentException("Properties must not be null");
new PropertyParser(props).load(this);
}
public GraphDescription()
{
}
public NodeInfo add(final String nodeName, final String attributeName, final Object value)
{
return getNode(nodeName).setProperty(attributeName, value);
}
private NodeInfo getNode(final String nodeName)
{
final NodeInfo node = nodes.get(nodeName);
if (node != null) return node;
final NodeInfo newNode = new NodeInfo(nodeName);
nodes.put(nodeName, newNode);
return newNode;
}
public void relate(final String from, final RelationshipType type, final String to)
{
getNode(from).relateTo(type, getNode(to));
}
void addToGraph(GraphDatabaseService graph)
{
boolean first = true;
for (NodeInfo nodeInfo : nodes.values())
{
final Node node = first ? graph.getReferenceNode() : graph.createNode();
first = false;
nodeInfo.updateProperties(node);
}
for (NodeInfo nodeInfo : nodes.values())
{
final Node from = graph.getNodeById(nodeInfo.getId());
nodeInfo.updateRelations(from, graph);
}
}
}

View File

@@ -1,86 +0,0 @@
package org.springframework.data.graph.neo4j.template;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.springframework.dao.DataAccessException;
import org.springframework.data.graph.UncategorizedGraphStoreException;
/**
* @author mh
* @since 18.02.11
*/
public abstract class GraphTransactionCallback<T> implements GraphCallback<T> {
public interface Status {
void mustRollback();
void interimCommit();
}
public abstract T doWithGraph(Status status, GraphDatabaseService graph) throws Exception;
@Override
public T doWithGraph(GraphDatabaseService graph) throws Exception {
final TransactionStatus status = new TransactionStatus(graph);
try {
return doWithGraph(status, graph);
} catch (Exception e) {
status.mustRollback();
throw e;
} finally {
status.finish();
}
}
private static class TransactionStatus implements Status {
private boolean rollback;
private final GraphDatabaseService neo;
private Transaction tx;
private TransactionStatus(final GraphDatabaseService neo) {
if (neo == null)
throw new IllegalArgumentException("GraphDatabaseService must not be null");
this.neo = neo;
begin();
}
private void begin() {
tx = neo.beginTx();
}
public void mustRollback() {
this.rollback = true;
}
public void interimCommit() {
finish();
begin();
}
private void finish() {
try {
if (rollback) {
tx.failure();
} else {
tx.success();
}
} finally {
tx.finish();
}
}
}
/**
* @author mh
* @since 19.02.11
*/
public abstract static class WithoutResult extends GraphTransactionCallback<Void> {
@Override
public Void doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
doWithGraphWithoutResult(status,graph);
return null;
}
public abstract void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception;
}
}

View File

@@ -3,42 +3,39 @@ package org.springframework.data.graph.neo4j.template;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.traversal.TraversalDescription;
import java.util.Map;
/**
* @author mh
* @since 19.02.11
*/
public interface Neo4jOperations {
<T> T doInTransaction(GraphTransactionCallback<T> callback);
<T> T update(GraphCallback<T> callback);
<T> T execute(GraphCallback<T> callback);
<T> T exec(GraphCallback<T> callback);
Node getReferenceNode();
Node getNode(long id);
Node createNode(Property... props);
Node createNode(Map<String, Object> props, String... indexFields);
Relationship getRelationship(long id);
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props);
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props, String... indexFields);
<T> Iterable<T> queryNodes(String indexName, Object queryOrQueryObject, PathMapper<T> pathMapper);
<T> Iterable<T> query(String indexName, PathMapper<T> pathMapper, Object queryOrQueryObject);
<T> Iterable<T> retrieveNodes(String indexName, String field, String value, PathMapper<T> pathMapper);
<T> Iterable<T> query(String indexName, PathMapper<T> pathMapper, String field, String value);
<T> Iterable<T> queryRelationships(String indexName, Object queryOrQueryObject, PathMapper<T> pathMapper);
<T> Iterable<T> traverseGraph(Node startNode, PathMapper<T> pathMapper, TraversalDescription traversal);
<T> Iterable<T> retrieveRelationships(String indexName, String field, String value, PathMapper<T> pathMapper);
<T> Iterable<T> traverseNext(Node startNode, PathMapper<T> pathMapper, RelationshipType type, Direction direction);
<T> Iterable<T> traverse(Node startNode, TraversalDescription traversal, PathMapper<T> pathMapper);
<T> Iterable<T> traverseNext(Node startNode, PathMapper<T> pathMapper, RelationshipType... type);
<T> Iterable<T> traverseDirectRelationships(Node startNode, RelationshipType type, Direction direction, PathMapper<T> pathMapper);
<T> Iterable<T> traverseNext(Node startNode, PathMapper<T> pathMapper);
<T> Iterable<T> traverseDirectRelationships(Node startNode, PathMapper<T> pathMapper, RelationshipType... type);
<T> Iterable<T> traverseDirectRelationships(Node startNode, PathMapper<T> pathMapper);
void index(Relationship relationship, String indexName, String field, Object value);
void index(Node node, String indexName, String field, Object value);
<T extends PropertyContainer> T index(String indexName, T element, String field, Object value);
<T extends PropertyContainer> T autoIndex(String indexName, T element, String... indexFields);
}

View File

@@ -18,20 +18,21 @@ package org.springframework.data.graph.neo4j.template;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.IterableWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import java.util.Arrays;
import java.util.Map;
public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTranslator {
public class Neo4jTemplate implements Neo4jOperations {
private final GraphDatabaseService graphDatabaseService;
private final Neo4jExceptionTranslator exceptionTranslator = new Neo4jExceptionTranslator();
private final IndexManager index;
private static void notNull(Object... pairs) {
assert pairs.length % 2 == 0 : "wrong number of pairs to check";
@@ -45,22 +46,32 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
public Neo4jTemplate(final GraphDatabaseService graphDatabaseService) {
notNull(graphDatabaseService, "graphDatabaseService");
this.graphDatabaseService = graphDatabaseService;
index = this.graphDatabaseService.index();
}
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
@Override
public <T> T doInTransaction(final GraphTransactionCallback<T> callback) {
public <T> T update(final GraphCallback<T> callback) {
notNull(callback, "callback");
return execute(callback);
Transaction tx = graphDatabaseService.beginTx();
try {
T result = exec(callback);
tx.success();
return result;
} catch (RuntimeException e) {
tx.failure();
throw e;
} finally {
tx.finish();
}
}
@Override
public <T> T execute(final GraphCallback<T> callback) {
public <T> T exec(final GraphCallback<T> callback) {
notNull(callback, "callback");
try {
return callback.doWithGraph(graphDatabaseService);
@@ -81,12 +92,13 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public Node createNode(final Property... properties) {
notNull(properties, "properties");
return doInTransaction(new GraphTransactionCallback<Node>() {
public Node createNode(final Map<String, Object> properties, final String... indexFields) {
return update(new GraphCallback<Node>() {
@Override
public Node doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
return setProperties(graphDatabaseService.createNode(), properties);
public Node doWithGraph(GraphDatabaseService graph) throws Exception {
Node node = graphDatabaseService.createNode();
if (properties == null) return node;
return autoIndex(null, setProperties(node, properties), indexFields);
}
});
}
@@ -112,35 +124,48 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public void index(final Relationship relationship, final String indexName, final String field, final Object value) {
notNull(relationship, "relationship", field, "field", value, "value");
doInTransaction(new GraphTransactionCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
relationshipIndex(indexName).add(relationship, field, value);
}
});
public <T extends PropertyContainer> T autoIndex(String indexName, T element, String... indexFields) {
for (String indexField : indexFields) {
if (!element.hasProperty(indexField)) continue;
index(indexName,element, indexField,element.getProperty(indexField));
}
return element;
}
@Override
public void index(final Node node, final String indexName, final String field, final Object value) {
notNull(node, "node", field, "field", value, "value");
doInTransaction(new GraphTransactionCallback.WithoutResult() {
public <T extends PropertyContainer> T index(final String indexName, final T element, final String field, final Object value) {
notNull(element, "element", field, "field", value, "value");
update(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
nodeIndex(indexName).add(node, field, value);
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
RelationshipIndex relationshipIndex = relationshipIndex(indexName);
if (relationshipIndex != null && element instanceof Relationship) {
relationshipIndex.add((Relationship) element, field, value);
} else if (element instanceof Node) {
nodeIndex(indexName).add((Node) element, field, value);
} else {
throw new IllegalArgumentException("Provided element is neither node nor relationship " + element);
}
}
});
return element;
}
private RelationshipIndex relationshipIndex(String indexName) {
return graphDatabaseService.index().forRelationships(indexName == null ? "relationship" : indexName);
if (indexName != null && index.existsForRelationships(indexName)) {
return index.forRelationships(indexName);
}
return null;
}
@Override
public <T> Iterable<T> queryNodes(String indexName, Object queryOrQueryObject, final PathMapper<T> pathMapper) {
public <T> Iterable<T> query(String indexName, final PathMapper<T> pathMapper, Object queryOrQueryObject) {
notNull(queryOrQueryObject, "queryOrQueryObject", pathMapper, "pathMapper");
try {
RelationshipIndex relationshipIndex = relationshipIndex(indexName);
if (relationshipIndex!=null) {
return mapRelationships(relationshipIndex.query(queryOrQueryObject), pathMapper);
}
return mapNodes(nodeIndex(indexName).query(queryOrQueryObject), pathMapper);
} catch (RuntimeException e) {
throw translateExceptionIfPossible(e);
@@ -148,35 +173,19 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public <T> Iterable<T> retrieveNodes(String indexName, String field, String value, final PathMapper<T> pathMapper) {
public <T> Iterable<T> query(String indexName, final PathMapper<T> pathMapper, String field, String value) {
notNull(field, "field", value, "value", pathMapper, "pathMapper");
try {
RelationshipIndex relationshipIndex = relationshipIndex(indexName);
if (relationshipIndex!=null) {
return mapRelationships(relationshipIndex.get(field, value), pathMapper);
}
return mapNodes(nodeIndex(indexName).get(field, value), pathMapper);
} catch (RuntimeException e) {
throw translateExceptionIfPossible(e);
}
}
@Override
public <T> Iterable<T> queryRelationships(String indexName, Object queryOrQueryObject, final PathMapper<T> pathMapper) {
notNull(queryOrQueryObject, "queryOrQueryObject", pathMapper, "pathMapper");
try {
return mapRelationships(relationshipIndex(indexName).query(queryOrQueryObject), pathMapper);
} catch (RuntimeException e) {
throw translateExceptionIfPossible(e);
}
}
@Override
public <T> Iterable<T> retrieveRelationships(String indexName, String field, String value, final PathMapper<T> pathMapper) {
notNull(field, "field", value, "value", pathMapper, "pathMapper");
try {
return mapRelationships(relationshipIndex(indexName).get(field, value), pathMapper);
} catch (RuntimeException e) {
throw translateExceptionIfPossible(e);
}
}
private <T> Iterable<T> mapNodes(final Iterable<Node> nodes, final PathMapper<T> pathMapper) {
assert nodes != null;
assert pathMapper != null;
@@ -189,11 +198,11 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
private Index<Node> nodeIndex(String indexName) {
return graphDatabaseService.index().forNodes(indexName == null ? "node" : indexName);
return index.forNodes(indexName == null ? "node" : indexName);
}
@Override
public <T> Iterable<T> traverse(Node startNode, TraversalDescription traversal, final PathMapper<T> pathMapper) {
public <T> Iterable<T> traverseGraph(Node startNode, final PathMapper<T> pathMapper, TraversalDescription traversal) {
notNull(startNode, "startNode", traversal, "traversal", pathMapper, "pathMapper");
try {
return mapPaths(traversal.traverse(startNode), pathMapper);
@@ -214,7 +223,7 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public <T> Iterable<T> traverseDirectRelationships(Node startNode, RelationshipType relationshipType, Direction direction, final PathMapper<T> pathMapper) {
public <T> Iterable<T> traverseNext(Node startNode, final PathMapper<T> pathMapper, RelationshipType relationshipType, Direction direction) {
notNull(startNode, "startNode", relationshipType, "relationshipType", direction, "direction", pathMapper, "pathMapper");
try {
return mapRelationships(startNode.getRelationships(relationshipType, direction), pathMapper);
@@ -224,7 +233,7 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public <T> Iterable<T> traverseDirectRelationships(Node startNode, final PathMapper<T> pathMapper, RelationshipType... relationshipTypes) {
public <T> Iterable<T> traverseNext(Node startNode, final PathMapper<T> pathMapper, RelationshipType... relationshipTypes) {
notNull(startNode, "startNode", relationshipTypes, "relationshipType", pathMapper, "pathMapper");
try {
return mapRelationships(startNode.getRelationships(relationshipTypes), pathMapper);
@@ -234,7 +243,7 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public <T> Iterable<T> traverseDirectRelationships(Node startNode, final PathMapper<T> pathMapper) {
public <T> Iterable<T> traverseNext(Node startNode, final PathMapper<T> pathMapper) {
notNull(startNode, "startNode", pathMapper, "pathMapper");
try {
return mapRelationships(startNode.getRelationships(), pathMapper);
@@ -255,23 +264,27 @@ public class Neo4jTemplate implements Neo4jOperations, PersistenceExceptionTrans
}
@Override
public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Property... properties) {
public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Map<String, Object> properties, final String... indexFields) {
notNull(startNode, "startNode", endNode, "endNode", relationshipType, "relationshipType", properties, "properties");
return doInTransaction(new GraphTransactionCallback<Relationship>() {
return update(new GraphCallback<Relationship>() {
@Override
public Relationship doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
return setProperties(startNode.createRelationshipTo(endNode, relationshipType), properties);
public Relationship doWithGraph(GraphDatabaseService graph) throws Exception {
Relationship relationship = startNode.createRelationshipTo(endNode, relationshipType);
if (properties == null) return relationship;
return autoIndex("relationship", setProperties(relationship, properties), indexFields);
}
});
}
private <T extends PropertyContainer> T setProperties(T primitive, Property... properties) {
private <T extends PropertyContainer> T setProperties(T primitive, Map<String, Object> properties) {
assert primitive != null;
assert properties != null;
for (Property prop : properties) {
if (prop == null)
throw new IllegalArgumentException("at least one Property is null: " + Arrays.toString(properties));
primitive.setProperty(prop.getName(), prop.getValue());
if (properties==null) return primitive;
for (Map.Entry<String, Object> prop : properties.entrySet()) {
if (prop.getValue()==null) {
primitive.removeProperty(prop.getKey());
} else {
primitive.setProperty(prop.getKey(), prop.getValue());
}
}
return primitive;
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2010 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.graph.neo4j.template;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
class NodeInfo
{
private final String name;
private final Map<String, Object> props = new LinkedHashMap<String, Object>();
private final Set<RelationShipNodeInfo> relations = new LinkedHashSet<RelationShipNodeInfo>();
private long id;
public NodeInfo(final String nodeName)
{
this.name = nodeName;
}
public NodeInfo setProperty(final String name, final Object value)
{
this.props.put(name, value);
return this;
}
public void relateTo(final RelationshipType type, final NodeInfo node)
{
this.relations.add(new RelationShipNodeInfo(type, node));
}
public void setId(final long id)
{
this.id = id;
}
public long getId()
{
return id;
}
void updateProperties(final Node node)
{
node.setProperty("name", name);
for (Map.Entry<String, Object> prop : props.entrySet())
{
node.setProperty(prop.getKey(), prop.getValue());
}
setId(node.getId());
}
void updateRelations(final Node from, final GraphDatabaseService nodeService)
{
for (RelationShipNodeInfo relation : relations)
{
final Node to = nodeService.getNodeById(relation.getNodeInfo().getId());
from.createRelationshipTo(to, relation.getRelationshipType());
}
}
public boolean equals(final Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final NodeInfo nodeInfo = (NodeInfo) o;
return name.equals(nodeInfo.name);
}
public int hashCode()
{
return name.hashCode();
}
}

View File

@@ -16,33 +16,28 @@
package org.springframework.data.graph.neo4j.template;
public class Property
{
private final String name;
private final Object value;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
private Property(final String name, final Object value)
{
if (name == null)
throw new IllegalArgumentException("Name must not be null");
if (value == null)
throw new IllegalArgumentException("Value must not be null");
this.name = name;
this.value = value;
public class PropertyMap {
private final Map<String, Object> properties = new HashMap<String, Object>();
public PropertyMap set(String name, Object value) {
properties.put(name, value);
return this;
}
public static Property _(String name, Object value)
{
return new Property(name, value);
public static PropertyMap props() {
return new PropertyMap();
}
public Object getValue()
{
return value;
public Map<String, Object> toMap() {
return properties;
}
public String getName()
{
return name;
public static Map<String, Object> _(String name, Object value) {
return Collections.singletonMap(name,value);
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright 2010 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.graph.neo4j.template;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.springframework.data.graph.neo4j.template.util.Converter;
import static java.lang.String.format;
import java.util.Map;
import java.util.Properties;
import java.util.SortedMap;
import java.util.TreeMap;
public class PropertyParser
{
private final Properties props;
private final Converter converter = new Converter();
public PropertyParser(final Properties props)
{
this.props = props;
}
public void load(final GraphDescription graph)
{
final SortedMap<Object, Object> sortedSet = new TreeMap<Object, Object>(props);
for (Map.Entry<Object, Object> entry : sortedSet.entrySet())
{
if (entry.getKey() == null || entry.getValue() == null)
throw new IllegalArgumentException(format("%s=%s is partially null%nproperties %s",
entry.getKey(), entry.getValue(), props));
final String key = entry.getKey().toString();
final String value = entry.getValue().toString();
final String[] prop = key.split("\\.");
if (prop.length == 2)
{
final String[] values = value.split(":");
if (values.length != 2)
graph.add(prop[0], prop[1], value);
else
{
graph.add(prop[0], prop[1], converter.convert(values[0], values[1]));
}
} else
{
final String[] relationships = key.split("->");
if (relationships.length == 2)
{
graph.relate(relationships[0], DynamicRelationshipType.withName(relationships[1]), value);
}
}
}
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2010 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.graph.neo4j.template;
import org.neo4j.graphdb.RelationshipType;
class RelationShipNodeInfo
{
private final RelationshipType relationshipType;
private final NodeInfo nodeInfo;
RelationShipNodeInfo(final RelationshipType relationshipType, final NodeInfo nodeInfo)
{
this.relationshipType = relationshipType;
this.nodeInfo = nodeInfo;
}
public boolean equals(final Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final RelationShipNodeInfo that = (RelationShipNodeInfo) o;
return nodeInfo.equals(that.nodeInfo) && relationshipType.name().equals(that.relationshipType.name());
}
public int hashCode()
{
return relationshipType.name().hashCode() * 31 + nodeInfo.hashCode();
}
public NodeInfo getNodeInfo()
{
return nodeInfo;
}
public RelationshipType getRelationshipType()
{
return relationshipType;
}
}

View File

@@ -1,30 +1,24 @@
package org.springframework.data.graph.neo4j.template;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.TermQuery;
import org.hamcrest.CoreMatchers;
import org.hibernate.ejb.criteria.ParameterContainer;
import org.jboss.netty.util.HashedWheelTimer;
import org.junit.*;
import org.mockito.Mockito;
import org.neo4j.graphdb.*;
import org.neo4j.kernel.ImpermanentGraphDatabase;
import org.neo4j.kernel.Traversal;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.graph.neo4j.support.node.Neo4jHelper;
import javax.print.attribute.HashAttributeSet;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.Iterator;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.springframework.data.graph.neo4j.template.Property._;
import static org.junit.Assert.*;
import static org.springframework.data.graph.neo4j.template.PropertyMap._;
import static org.springframework.data.graph.neo4j.template.PropertyMap.props;
/**
* @author mh
@@ -34,14 +28,17 @@ public class Neo4jTemplateApiTest {
private static final DynamicRelationshipType KNOWS = DynamicRelationshipType.withName("knows");
private static final DynamicRelationshipType HAS = DynamicRelationshipType.withName("has");
private Neo4jTemplate template;
private static ImpermanentGraphDatabase graphDatabase;
private static GraphDatabaseService graphDatabase;
private static PlatformTransactionManager tm;
private Node referenceNode;
private Relationship relationship1;
private Node node1;
@BeforeClass
public static void startDb() throws Exception {
graphDatabase = new ImpermanentGraphDatabase();
// tm = new JtaTransactionManager(new SpringTransactionManager(graphDatabase));
}
@Before
@@ -86,9 +83,9 @@ public class Neo4jTemplateApiTest {
@Test
public void shouldExecuteCallbackInTransaction() throws Exception {
Node refNode = template.doInTransaction(new GraphTransactionCallback<Node>() {
Node refNode = template.update(new GraphCallback<Node>() {
@Override
public Node doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
public Node doWithGraph(GraphDatabaseService graph) throws Exception {
Node referenceNode = graph.getReferenceNode();
referenceNode.setProperty("test", "testDoInTransaction");
return referenceNode;
@@ -101,10 +98,10 @@ public class Neo4jTemplateApiTest {
@Test
public void shouldRollbackTransactionOnException() {
try {
template.doInTransaction(new GraphTransactionCallback.WithoutResult() {
template.update(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
graph.getReferenceNode().setProperty("test","shouldRollbackTransactionOnException");
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
graph.getReferenceNode().setProperty("test", "shouldRollbackTransactionOnException");
throw new RuntimeException("please rollback");
}
});
@@ -115,12 +112,18 @@ public class Neo4jTemplateApiTest {
}
@Test
@Ignore("until the ImpermanentGraphDatabase cast issue is resolved in neo4j")
public void shouldRollbackViaStatus() throws Exception {
template.doInTransaction(new GraphTransactionCallback.WithoutResult() {
new TransactionTemplate(tm).execute(new TransactionCallbackWithoutResult() {
@Override
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
graph.getReferenceNode().setProperty("test","shouldRollbackTransactionOnException");
status.mustRollback();
protected void doInTransactionWithoutResult(final TransactionStatus status) {
template.update(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
graph.getReferenceNode().setProperty("test", "shouldRollbackTransactionOnException");
status.setRollbackOnly();
}
});
}
});
Assert.assertThat((String) graphDatabase.getReferenceNode().getProperty("test","not set"), not("shouldRollbackTransactionOnException"));
@@ -128,7 +131,7 @@ public class Neo4jTemplateApiTest {
@Test(expected = RuntimeException.class)
public void shouldNotConvertUserRuntimeExceptionToDataAccessException() {
template.execute(new GraphCallback.WithoutResult() {
template.exec(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
throw new RuntimeException();
@@ -138,7 +141,7 @@ public class Neo4jTemplateApiTest {
@Test(expected = DataAccessException.class)
public void shouldConvertMissingTransactionExceptionToDataAccessException() {
template.execute(new GraphCallback.WithoutResult() {
template.exec(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
graph.createNode();
@@ -147,7 +150,7 @@ public class Neo4jTemplateApiTest {
}
@Test(expected = DataAccessException.class)
public void shouldConvertNotFoundExceptionToDataAccessException() {
template.execute(new GraphCallback.WithoutResult() {
template.exec(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
graph.getNodeById(Long.MAX_VALUE);
@@ -161,7 +164,7 @@ public class Neo4jTemplateApiTest {
@Test
public void shouldExecuteCallback() throws Exception {
Long refNodeId = template.execute(new GraphCallback<Long>() {
Long refNodeId = template.exec(new GraphCallback<Long>() {
@Override
public Long doWithGraph(GraphDatabaseService graph) throws Exception {
return graph.getReferenceNode().getId();
@@ -177,17 +180,13 @@ public class Neo4jTemplateApiTest {
@Test
public void testCreateNode() throws Exception {
Node node=template.createNode();
Node node=template.createNode(null);
assertNotNull("created node",node);
}
@Test(expected = InvalidDataAccessApiUsageException.class)
public void shouldFailForNullProperties() throws Exception {
template.createNode(null);
}
@Test
public void testCreateNodeWithProperties() throws Exception {
Node node=template.createNode(_("test", "testCreateNodeWithProperties"));
Node node=template.createNode(props().set("test", "testCreateNodeWithProperties").toMap());
assertTestPropertySet(node, "testCreateNodeWithProperties");
}
@@ -216,47 +215,47 @@ public class Neo4jTemplateApiTest {
@Test
public void testIndexNode() throws Exception {
template.index(node1,null,"name","node1");
template.index(null, node1, "name","node1");
Node lookedUpNode=graphDatabase.index().forNodes("node").get("name","node1").getSingle();
assertThat("same node from index",lookedUpNode,is(node1));
}
@Test
public void testQueryNodes() throws Exception {
assertSingleResult("node0", template.queryNodes(null, new TermQuery(new Term("name", "node0")), new NodeNameMapper()));
assertSingleResult("node0", template.query(null, new NodeNameMapper(), new TermQuery(new Term("name", "node0"))));
}
@Test
public void testRetrieveNodes() throws Exception {
assertSingleResult("node0", template.retrieveNodes(null, "name", "node0", new NodeNameMapper()));
assertSingleResult("node0", template.query(null, new NodeNameMapper(), "name", "node0"));
}
@Test
public void testQueryRelationships() throws Exception {
assertSingleResult("rel1", template.queryRelationships(null, new TermQuery(new Term("name", "rel1")), new RelationshipNameMapper()));
assertSingleResult("rel1", template.query("relationship", new RelationshipNameMapper(), new TermQuery(new Term("name", "rel1"))));
}
@Test
public void testRetrieveRelationships() throws Exception {
assertSingleResult("rel1",template.retrieveRelationships(null, "name", "rel1", new RelationshipNameMapper()));
assertSingleResult("rel1",template.query("relationship", new RelationshipNameMapper(), "name", "rel1"));
}
@Test
public void testTraverse() throws Exception {
assertSingleResult("node1",template.traverse(referenceNode, Traversal.description().relationships(KNOWS).prune(Traversal.pruneAfterDepth(1)).filter(Traversal.returnAllButStartNode()), new NodeNameMapper()));
assertSingleResult("node1",template.traverseGraph(referenceNode, new NodeNameMapper(), Traversal.description().relationships(KNOWS).prune(Traversal.pruneAfterDepth(1)).filter(Traversal.returnAllButStartNode())));
}
@Test
public void shouldGetDirectRelationship() throws Exception {
assertSingleResult("rel1", template.traverseDirectRelationships(referenceNode, new RelationshipNameMapper()));
assertSingleResult("rel1", template.traverseNext(referenceNode, new RelationshipNameMapper()));
}
@Test
public void shouldGetDirectRelationshipForType() throws Exception {
assertSingleResult("rel1", template.traverseDirectRelationships(referenceNode, new RelationshipNameMapper(),KNOWS));
assertSingleResult("rel1", template.traverseNext(referenceNode, new RelationshipNameMapper(), KNOWS));
}
@Test
public void shouldGetDirectRelationshipForTypeAndDirection() throws Exception {
assertSingleResult("rel1", template.traverseDirectRelationships(referenceNode, KNOWS, Direction.OUTGOING, new RelationshipNameMapper()));
assertSingleResult("rel1", template.traverseNext(referenceNode, new RelationshipNameMapper(), KNOWS, Direction.OUTGOING));
}
private <T> void assertSingleResult(T expected, Iterable<T> iterable) {

View File

@@ -15,7 +15,7 @@ public class Neo4jTemplateTest extends NeoApiTest {
@Test
public void testRefNode() {
Node refNodeById = new Neo4jTemplate(graph).execute(new GraphCallback<Node>() {
Node refNodeById = new Neo4jTemplate(graph).exec(new GraphCallback<Node>() {
public Node doWithGraph(GraphDatabaseService graph) throws Exception {
Node refNode = graph.getReferenceNode();
return graph.getNodeById(refNode.getId());
@@ -27,8 +27,8 @@ public class Neo4jTemplateTest extends NeoApiTest {
@Test
public void testSingleNode() {
final Neo4jOperations template = new Neo4jTemplate(graph);
template.doInTransaction(new GraphTransactionCallback<Void>() {
public Void doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
template.update(new GraphCallback<Void>() {
public Void doWithGraph(GraphDatabaseService graph) throws Exception {
Node refNode = graph.getReferenceNode();
// TODO easy API Node node = graph.createNode(Property._("name", "Test"), Property._("size", 100));
Node node = graph.createNode();
@@ -43,8 +43,8 @@ public class Neo4jTemplateTest extends NeoApiTest {
return null;
}
});
template.doInTransaction(new GraphTransactionCallback<Void>() {
public Void doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
template.update(new GraphCallback<Void>() {
public Void doWithGraph(GraphDatabaseService graph) throws Exception {
Node refNode = graph.getReferenceNode();
final Relationship toTestNode = refNode.getSingleRelationship(HAS, Direction.OUTGOING);
final Node nodeByRelationship = toTestNode.getEndNode();
@@ -58,16 +58,18 @@ public class Neo4jTemplateTest extends NeoApiTest {
@Test
public void testRollback() {
final Neo4jOperations template = new Neo4jTemplate(graph);
template.doInTransaction(new GraphTransactionCallback.WithoutResult() {
try {
template.update(new GraphCallback.WithoutResult() {
@Override
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception {
Node node = graph.getReferenceNode();
node.setProperty("test", "test");
assertEquals("test", node.getProperty("test"));
status.mustRollback();
throw new RuntimeException();
}
});
template.execute(new GraphCallback.WithoutResult() {
} catch(RuntimeException ignore) {}
template.exec(new GraphCallback.WithoutResult() {
public void doWithGraphWithoutResult(final GraphDatabaseService graph) throws Exception {
Node node = graph.getReferenceNode();
assertFalse(node.hasProperty("test"));

View File

@@ -7,10 +7,13 @@ import org.neo4j.kernel.EmbeddedGraphDatabase;
public abstract class NeoApiTest {
protected GraphDatabaseService graph;
protected Neo4jTemplate template;
@Before
public void setUp() {
graph = new EmbeddedGraphDatabase("target/template-db");
template = new Neo4jTemplate(graph);
}
@After
@@ -23,8 +26,8 @@ public abstract class NeoApiTest {
private void clear() {
try {
new Neo4jTemplate(graph).doInTransaction(new GraphTransactionCallback<Void>() {
public Void doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
template.update(new GraphCallback<Void>() {
public Void doWithGraph(GraphDatabaseService graph) throws Exception {
for (Node node : graph.getAllNodes()) {
for (Relationship relationship : node.getRelationships()) {
relationship.delete();

View File

@@ -1,68 +0,0 @@
package org.springframework.data.graph.neo4j.template;
import org.junit.Test;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.springframework.data.graph.neo4j.template.NeoGraphDescriptionTest.Type.HAS;
public class NeoGraphDescriptionTest extends NeoApiTest {
enum Type implements RelationshipType {
HAS
}
@Test
public void testLoadGraph() {
final Neo4jOperations template = new Neo4jTemplate(graph);
template.doInTransaction(new GraphTransactionCallback.WithoutResult() {
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
final GraphDescription heaven = new GraphDescription();
heaven.add("adam", "age", 1);
heaven.add("eve", "age", 0);
heaven.relate("adam", HAS, "eve");
heaven.addToGraph(graph);
checkHeaven(graph);
}
});
}
private void checkHeaven(final GraphDatabaseService graph) {
final Node adam = graph.getReferenceNode();
assertEquals("adam", adam.getProperty("name"));
assertEquals(1, adam.getProperty("age"));
final Node eve = adam.getSingleRelationship(HAS, Direction.OUTGOING).getEndNode();
assertEquals("eve", eve.getProperty("name"));
assertEquals(0, eve.getProperty("age"));
}
@Test
public void testLoadGraphProps() {
final Neo4jOperations template = new Neo4jTemplate(graph);
template.doInTransaction(new GraphTransactionCallback.WithoutResult() {
public void doWithGraphWithoutResult(Status status, GraphDatabaseService graph) throws Exception {
final GraphDescription heaven = new GraphDescription(createGraphProperties());
heaven.addToGraph(graph);
checkHeaven(graph);
}
});
}
private Properties createGraphProperties() throws IOException {
Properties props = new Properties();
props.load(new ByteArrayInputStream((
"adam.age=Integer:1\n" +
"eve.age=Integer:0\n" +
"adam->HAS=eve"
).getBytes("UTF-8")));
return props;
}
}

View File

@@ -1,18 +1,20 @@
package org.springframework.data.graph.neo4j.template;
import org.junit.Ignore;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.kernel.Traversal;
import java.util.HashSet;
import java.util.Set;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.neo4j.kernel.Traversal.returnAllButStartNode;
import static org.springframework.data.graph.neo4j.template.NeoTraversalTest.Type.HAS;
import static org.springframework.data.graph.neo4j.template.PropertyMap._;
public class NeoTraversalTest extends NeoApiTest {
@@ -22,73 +24,56 @@ public class NeoTraversalTest extends NeoApiTest {
@Test
public void testSimpleTraverse() {
runAndCheckTraverse(Traversal.description().filter(returnAllButStartNode()).relationships(HAS), "grandpa", "grandma","daughter","son","man","wife" );
}
@Ignore
@Test
public void testComplexTraversal() {
final TraversalDescription traversal = Traversal.description().relationships(HAS).prune(Traversal.pruneAfterDepth(1));
runAndCheckTraverse(traversal, "grandpa", "grandma", "daughter", "son", "man", "wife");
}
private void runAndCheckTraverse(final TraversalDescription traversal, final String... names) {
final Neo4jOperations template = new Neo4jTemplate(graph);
template.doInTransaction(new GraphTransactionCallback<Void>() {
public Void doWithGraph(Status status, GraphDatabaseService graph) throws Exception {
createFamily(graph);
template.update(new GraphCallback<Void>() {
public Void doWithGraph(GraphDatabaseService graph) throws Exception {
createFamily();
return null;
}});
Iterable<String> result=template.traverse(template.getReferenceNode(), traversal,
new PathMapper<String>() {
@Override
public String mapPath(Path path) {
return (String) path.endNode().getProperty("name", "");
}
});
assertEquals("all members", asList(names), IteratorUtil.<String>asCollection(result));
}
});
final Set<String> resultSet = new HashSet<String>();
template.traverseGraph(template.getReferenceNode(), new PathMapper.WithoutResult() {
@Override
public void eachPath(Path path) {
String nodeName = (String) path.endNode().getProperty("name", "");
resultSet.add(nodeName);
}
}, Traversal.description().filter(returnAllButStartNode()).relationships(HAS));
assertEquals("all members", new HashSet<String>(asList("grandpa", "grandma", "daughter", "son", "man", "wife", "family")), resultSet);
}
private void createFamily(final GraphDatabaseService graph) {
final GraphDescription family = new GraphDescription();
family.add("family", "type", "small");
family.relate("family", HAS, "wife");
family.relate("family", HAS, "man");
private void createFamily() {
family.add("man", "age", 35);
family.add("wife", "age", 30);
family.relate("man", Type.MARRIED, "wife");
family.relate("wife", Type.MARRIED, "man");
family.relate("man", Type.WIFE, "wife");
family.relate("wife", Type.HUSBAND, "man");
Node family = template.createNode(_("name", "family"));
Node man = template.createNode(_("name", "wife"));
Node wife = template.createNode(_("name", "man"));
family.createRelationshipTo(man, HAS);
family.createRelationshipTo(wife, HAS);
family.add("daughter", "age", 10);
family.add("son", "age", 8);
Node daughter = template.createNode(_("name", "daughter"));
family.createRelationshipTo(daughter, HAS);
Node son = template.createNode(_("name", "son"));
family.createRelationshipTo(son, HAS);
man.createRelationshipTo(son, Type.CHILD);
wife.createRelationshipTo(son, Type.CHILD);
man.createRelationshipTo(daughter, Type.CHILD);
wife.createRelationshipTo(daughter, Type.CHILD);
family.relate("family", HAS, "son");
family.relate("family", HAS, "daughter");
Node grandma = template.createNode(_("name", "grandma"));
Node grandpa = template.createNode(_("name", "grandpa"));
family.relate("man", Type.CHILD, "son");
family.relate("wife", Type.CHILD, "son");
family.relate("man", Type.CHILD, "daughter");
family.relate("wife", Type.CHILD, "daughter");
family.createRelationshipTo(grandma, HAS);
family.createRelationshipTo(grandpa, HAS);
family.add("grandma", "age", 60);
family.add("grandpa", "age", 75);
grandma.createRelationshipTo(man, Type.CHILD);
grandpa.createRelationshipTo(man, Type.CHILD);
family.relate("family", HAS, "grandma");
family.relate("family", HAS, "grandpa");
grandma.createRelationshipTo(son, Type.GRANDSON);
grandpa.createRelationshipTo(son, Type.GRANDSON);
grandma.createRelationshipTo(daughter, Type.GRANDDAUGHTER);
grandpa.createRelationshipTo(daughter, Type.GRANDDAUGHTER);
family.relate("grandpa", Type.CHILD, "man");
family.relate("grandma", Type.CHILD, "man");
family.relate("grandpa", Type.GRANDSON, "son");
family.relate("grandma", Type.GRANDSON, "son");
family.relate("grandpa", Type.GRANDDAUGHTER, "daughter");
family.relate("grandma", Type.GRANDDAUGHTER, "daughter");
family.addToGraph(graph);
graph.getReferenceNode().createRelationshipTo(family,HAS);
}
}