DATAGRAPH-181 fix for creating unique entities with numeric unique fields, unique relationships
This commit is contained in:
@@ -20,12 +20,15 @@ import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.index.RelationshipIndex;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.helpers.collection.MapUtil;
|
||||
import org.neo4j.rest.graphdb.ExecutingRestRequest;
|
||||
import org.neo4j.rest.graphdb.RequestResult;
|
||||
import org.neo4j.rest.graphdb.RestAPI;
|
||||
import org.neo4j.rest.graphdb.RestRequest;
|
||||
import org.neo4j.rest.graphdb.entity.RestNode;
|
||||
import org.neo4j.rest.graphdb.index.RestIndex;
|
||||
import org.neo4j.rest.graphdb.index.RestIndexManager;
|
||||
import org.neo4j.rest.graphdb.query.RestCypherQueryEngine;
|
||||
import org.neo4j.rest.graphdb.query.RestGremlinQueryEngine;
|
||||
@@ -66,21 +69,23 @@ public class SpringRestGraphDatabase extends org.neo4j.rest.graphdb.RestGraphDat
|
||||
return super.getRestAPI().createNode(props);
|
||||
}
|
||||
|
||||
// TODO move to RestAPI
|
||||
public Node getOrCreateNode(String index, String key, Object value, final Map<String,Object> properties) {
|
||||
if (index==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+index+" key "+key+" value must not be null");
|
||||
final RequestResult result = getRestAPI().getRestRequest().post("index/node/" + index + "?unique", MapUtil.map("key",key,"value",value,"properties",properties));
|
||||
if (result.statusIs(Response.Status.CREATED) || result.statusIs(Response.Status.OK)) {
|
||||
return (Node)getRestAPI().createExtractor().convertFromRepresentation(result);
|
||||
}
|
||||
throw new RuntimeException(String.format("Error retrieving or creating node for key %s and value %s with index %s", key, value, index));
|
||||
@Override
|
||||
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String,Object> properties) {
|
||||
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
|
||||
final RestIndex<Node> nodeIndex = index().forNodes(indexName);
|
||||
return getRestAPI().getOrCreateNode(nodeIndex, key, value, properties);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Relationship getOrCreateRelationship(String indexName, String key, Object value, Node startNode, Node endNode, String type, Map<String, Object> properties) {
|
||||
@SuppressWarnings("unchecked") final RestIndex<Relationship> relIndex = (RestIndex<Relationship>) index().forRelationships(indexName);
|
||||
return getRestAPI().getOrCreateRelationship(relIndex,key,value,(RestNode) startNode,(RestNode) endNode,type, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props) {
|
||||
return super.getRestAPI().createRelationship(startNode, endNode, type, props);
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> properties) {
|
||||
return super.getRestAPI().createRelationship(startNode, endNode, type, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,7 +53,7 @@ public interface GraphDatabase {
|
||||
* creates the node uniquely or returns an existing node with the same index-key-value combination.
|
||||
* properties are used to initialize the node.
|
||||
*/
|
||||
Node getOrCreateNode(String index, String key, Object value, final Map<String,Object> properties);
|
||||
Node getOrCreateNode(String indexName, String key, Object value, final Map<String,Object> properties);
|
||||
|
||||
/**
|
||||
* @param id relationship id
|
||||
@@ -62,7 +62,27 @@ public interface GraphDatabase {
|
||||
*/
|
||||
Relationship getRelationshipById(long id);
|
||||
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props);
|
||||
/**
|
||||
* creates the relationship between the startNode, endNode with the given type which will be populated with the provided properties
|
||||
*/
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> properties);
|
||||
|
||||
|
||||
/**
|
||||
* Creates the relationship uniquely, uses the given index,key,value to achieve that.
|
||||
* If the relationship for this combination already existed it is returned otherwise created and populated with the provided properties.
|
||||
*/
|
||||
Relationship getOrCreateRelationship(String indexName, String key, Object value, Node startNode, Node endNode, String type, Map<String, Object> properties);
|
||||
|
||||
/**
|
||||
* deletes the Node and its index entries
|
||||
*/
|
||||
void remove(Node node);
|
||||
|
||||
/**
|
||||
* deletes the relationship and its index entries
|
||||
*/
|
||||
void remove(Relationship relationship);
|
||||
|
||||
/**
|
||||
* @param indexName existing index name, not null
|
||||
@@ -86,18 +106,28 @@ public interface GraphDatabase {
|
||||
*/
|
||||
TraversalDescription traversalDescription();
|
||||
|
||||
/**
|
||||
* returns a query engine for the provided type (Cypher or Gremlin) which is initialized with the default result converter
|
||||
*/
|
||||
<T> QueryEngine<T> queryEngineFor(QueryType type);
|
||||
|
||||
void setConversionService(ConversionService conversionService);
|
||||
|
||||
/**
|
||||
* returns a query engine for the provided type (Cypher or Gremlin) which is initialized with the provided result converter
|
||||
*/
|
||||
<T> QueryEngine<T> queryEngineFor(QueryType type, ResultConverter resultConverter);
|
||||
|
||||
boolean transactionIsRunning();
|
||||
|
||||
void remove(Node node);
|
||||
|
||||
void remove(Relationship relationship);
|
||||
/**
|
||||
* @param conversionService the conversion service to be used for the default result converter of this database
|
||||
*/
|
||||
void setConversionService(ConversionService conversionService);
|
||||
|
||||
/**
|
||||
* @param resultConverter the default result converter to be used with this database
|
||||
*/
|
||||
void setResultConverter(ResultConverter resultConverter);
|
||||
|
||||
/**
|
||||
* @return true if a transaction is currently running
|
||||
*/
|
||||
boolean transactionIsRunning();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.data.neo4j.support;
|
||||
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
@@ -25,6 +26,7 @@ import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.graphdb.index.UniqueFactory;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.index.lucene.ValueContext;
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
import org.neo4j.kernel.GraphDatabaseAPI;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
@@ -122,8 +124,8 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props) {
|
||||
return setProperties(startNode.createRelationshipTo(endNode,type),props);
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> properties) {
|
||||
return setProperties(startNode.createRelationshipTo(endNode,type), properties);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -263,13 +265,29 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public Node getOrCreateNode(String index, String key, Object value, final Map<String,Object> nodeProperties) {
|
||||
if (index==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+index+" key "+key+" value must not be null");
|
||||
UniqueFactory.UniqueNodeFactory factory = new UniqueFactory.UniqueNodeFactory(delegate, index) {
|
||||
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String,Object> nodeProperties) {
|
||||
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
|
||||
if (value instanceof Number) value= ValueContext.numeric((Number)value);
|
||||
UniqueFactory.UniqueNodeFactory factory = new UniqueFactory.UniqueNodeFactory(delegate, indexName) {
|
||||
protected void initialize(Node node, Map<String, Object> _) {
|
||||
setProperties(node,nodeProperties);
|
||||
}
|
||||
};
|
||||
return factory.getOrCreate(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship getOrCreateRelationship(String indexName, String key, Object value, final Node startNode, final Node endNode, final String type, final Map<String, Object> properties) {
|
||||
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
|
||||
if (startNode ==null || endNode == null || type==null) throw new IllegalArgumentException("StartNode "+ startNode +" EndNode "+ endNode +" and type "+type+" must not be null");
|
||||
if (value instanceof Number) value= ValueContext.numeric((Number)value);
|
||||
UniqueFactory.UniqueRelationshipFactory factory = new UniqueFactory.UniqueRelationshipFactory(delegate, indexName) {
|
||||
@Override
|
||||
protected Relationship create(Map<String, Object> _) {
|
||||
final Relationship relationship = startNode.createRelationshipTo(endNode, DynamicRelationshipType.withName(type));
|
||||
return setProperties(relationship, properties);
|
||||
}
|
||||
};
|
||||
return factory.getOrCreate(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
*/
|
||||
@Override
|
||||
public Node createNode() {
|
||||
return infrastructure.getGraphDatabase().createNode(null);
|
||||
return getGraphDatabase().createNode(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,7 +247,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
*/
|
||||
@Override
|
||||
public Node createNode(final Map<String, Object> properties) {
|
||||
return infrastructure.getGraphDatabase().createNode(properties);
|
||||
return getGraphDatabase().createNode(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +256,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
*/
|
||||
@Override
|
||||
public Node getOrCreateNode(String index, String key, Object value, final Map<String, Object> properties) {
|
||||
return infrastructure.getGraphDatabase().getOrCreateNode(index, key, value, properties);
|
||||
return getGraphDatabase().getOrCreateNode(index, key, value, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -385,6 +385,11 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship getOrCreateRelationship(String indexName, String key, Object value, Node startNode, Node endNode, String type, Map<String, Object> properties) {
|
||||
return getGraphDatabase().getOrCreateRelationship(indexName,key,value,startNode,endNode,type,properties);
|
||||
}
|
||||
|
||||
private final Neo4jExceptionTranslator exceptionTranslator = new Neo4jExceptionTranslator();
|
||||
|
||||
public DataAccessException translateExceptionIfPossible(Exception ex) {
|
||||
|
||||
@@ -103,7 +103,13 @@ public interface Neo4jOperations {
|
||||
/**
|
||||
* Creates a relationship with the given initial properties.
|
||||
*/
|
||||
Relationship createRelationshipBetween(Node startNode, Node endNode, String type, Map<String, Object> props);
|
||||
Relationship createRelationshipBetween(Node startNode, Node endNode, String type, Map<String, Object> properties);
|
||||
|
||||
/**
|
||||
* Creates the relationship uniquely, uses the given index,key,value to achieve that.
|
||||
* If the relationship for this combination already existed it is returned otherwise created and populated with the provided properties.
|
||||
*/
|
||||
Relationship getOrCreateRelationship(String indexName, String key, Object value, Node startNode, Node endNode, String type, Map<String, Object> properties);
|
||||
|
||||
/**
|
||||
* Retrieves a single relationship entity between two node entities with the given relationship type projected to the provided
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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.support;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.Transaction;
|
||||
import org.neo4j.helpers.collection.MapUtil;
|
||||
import org.neo4j.test.ImpermanentGraphDatabase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.neo4j.helpers.collection.MapUtil.map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 11.04.12
|
||||
*/
|
||||
public class DelegatingGraphDatabaseTest {
|
||||
|
||||
private DelegatingGraphDatabase graphDatabase;
|
||||
private ImpermanentGraphDatabase gdb;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
gdb = new ImpermanentGraphDatabase();
|
||||
graphDatabase = new DelegatingGraphDatabase(gdb);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
graphDatabase.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOrCreateNode() throws Exception {
|
||||
final Node node = graphDatabase.getOrCreateNode("user", "name", "David", map("name", "David"));
|
||||
final Node node2 = graphDatabase.getOrCreateNode("user", "name", "David", map("name", "David"));
|
||||
assertEquals("David",node.getProperty("name"));
|
||||
assertEquals(node,node2);
|
||||
assertEquals(node,gdb.index().forNodes("user").get("name","David").getSingle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOrCreateRelationship() throws Exception {
|
||||
final Transaction tx = gdb.beginTx();
|
||||
final Node david = graphDatabase.createNode(map("name", "David"));
|
||||
final Node michael = graphDatabase.createNode(map("name", "Michael"));
|
||||
final Relationship rel1 = graphDatabase.getOrCreateRelationship("knows", "whom", "david_michael", david, michael, "KNOWS", map("whom", "david_michael"));
|
||||
final Relationship rel2 = graphDatabase.getOrCreateRelationship("knows", "whom", "david_michael", david, michael, "KNOWS", map("whom", "david_michael"));
|
||||
assertEquals("david_michael",rel1.getProperty("whom"));
|
||||
assertEquals("KNOWS",rel1.getType().name());
|
||||
assertEquals(david,rel1.getStartNode());
|
||||
assertEquals(michael,rel1.getEndNode());
|
||||
assertEquals(rel1,rel2);
|
||||
assertEquals(rel1,gdb.index().forRelationships("knows").get("whom","david_michael").getSingle());
|
||||
tx.success();
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user