Permalink
Browse files

allowed two-way relationships: (A)<-[:KNOWS]->(B)

  • Loading branch information...
1 parent a6dcef9 commit d20bd6fac24d29754d46a5e20c4da3dd8bc02112 @technige technige committed Mar 3, 2012
@@ -122,21 +122,32 @@ public void merge(Subgraph subgraph) throws SubgraphError {
NodeToken.anon(),
(RelationshipToken) rule.getDescriptor().getToken(0),
NodeToken.anon(),
- rule.getData()
+ rule.getData(),
+ false
);
} else if ("N-R->N".equals(pattern)) {
mergeRelationships(
(NodeToken) rule.getDescriptor().getToken(0),
(RelationshipToken) rule.getDescriptor().getToken(2),
(NodeToken) rule.getDescriptor().getToken(5),
- rule.getData()
+ rule.getData(),
+ false
);
} else if ("N<-R-N".equals(pattern)) {
mergeRelationships(
(NodeToken) rule.getDescriptor().getToken(5),
(RelationshipToken) rule.getDescriptor().getToken(3),
(NodeToken) rule.getDescriptor().getToken(0),
- rule.getData()
+ rule.getData(),
+ false
+ );
+ } else if ("N<-R->N".equals(pattern)) {
+ mergeRelationships(
+ (NodeToken) rule.getDescriptor().getToken(0),
+ (RelationshipToken) rule.getDescriptor().getToken(3),
+ (NodeToken) rule.getDescriptor().getToken(6),
+ rule.getData(),
+ true
);
} else if ("N^I".equals(pattern)) {
mergeIndexEntries(
@@ -177,21 +188,32 @@ public void insert(Subgraph subgraph) throws SubgraphError {
NodeToken.anon(),
(RelationshipToken) rule.getDescriptor().getToken(0),
NodeToken.anon(),
- rule.getData()
+ rule.getData(),
+ false
);
} else if ("N-R->N".equals(pattern)) {
insertRelationships(
(NodeToken) rule.getDescriptor().getToken(0),
(RelationshipToken) rule.getDescriptor().getToken(2),
(NodeToken) rule.getDescriptor().getToken(5),
- rule.getData()
+ rule.getData(),
+ false
);
} else if ("N<-R-N".equals(pattern)) {
insertRelationships(
(NodeToken) rule.getDescriptor().getToken(5),
(RelationshipToken) rule.getDescriptor().getToken(3),
(NodeToken) rule.getDescriptor().getToken(0),
- rule.getData()
+ rule.getData(),
+ false
+ );
+ } else if ("N<-R->N".equals(pattern)) {
+ insertRelationships(
+ (NodeToken) rule.getDescriptor().getToken(0),
+ (RelationshipToken) rule.getDescriptor().getToken(3),
+ (NodeToken) rule.getDescriptor().getToken(6),
+ rule.getData(),
+ true
);
} else if ("N^I".equals(pattern)) {
insertIndexEntries(
@@ -232,21 +254,32 @@ public void delete(Subgraph subgraph) throws SubgraphError {
NodeToken.anon(),
(RelationshipToken) rule.getDescriptor().getToken(0),
NodeToken.anon(),
- rule.getData()
+ rule.getData(),
+ false
);
} else if ("N-R->N".equals(pattern)) {
deleteRelationships(
(NodeToken) rule.getDescriptor().getToken(0),
(RelationshipToken) rule.getDescriptor().getToken(2),
(NodeToken) rule.getDescriptor().getToken(5),
- rule.getData()
+ rule.getData(),
+ false
);
} else if ("N<-R-N".equals(pattern)) {
deleteRelationships(
(NodeToken) rule.getDescriptor().getToken(5),
(RelationshipToken) rule.getDescriptor().getToken(3),
(NodeToken) rule.getDescriptor().getToken(0),
- rule.getData()
+ rule.getData(),
+ false
+ );
+ } else if ("N<-R->N".equals(pattern)) {
+ deleteRelationships(
+ (NodeToken) rule.getDescriptor().getToken(0),
+ (RelationshipToken) rule.getDescriptor().getToken(3),
+ (NodeToken) rule.getDescriptor().getToken(6),
+ rule.getData(),
+ true
);
} else if ("N^I".equals(pattern)) {
deleteIndexEntries(
@@ -284,7 +317,7 @@ public void delete(Subgraph subgraph) throws SubgraphError {
return nodes;
}
- private Set<Relationship> createRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties)
+ private Set<Relationship> createRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties, boolean bothWays)
throws SubgraphError
{
assert !relationshipStore.contains(r);
@@ -298,14 +331,17 @@ public void delete(Subgraph subgraph) throws SubgraphError {
for (Node startNode : startNodes) {
for (Node endNode : endNodes) {
relationships.add(startNode.createRelationshipTo(endNode, type));
+ if (bothWays) {
+ relationships.add(endNode.createRelationshipTo(startNode, type));
+ }
}
}
setProperties(relationships, properties);
relationshipStore.put(r, relationships);
return relationships;
}
- private Set<Relationship> updateRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties)
+ private Set<Relationship> updateRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties, boolean bothWays)
{
assert relationshipStore.contains(r);
Set<Relationship> relationships = relationshipStore.get(r);
@@ -320,6 +356,7 @@ public void delete(Subgraph subgraph) throws SubgraphError {
Node startNode = relationship.getStartNode();
Node endNode = relationship.getEndNode();
if (type == null || relationship.isType(type)) {
+ // TODO: adapt mismatch checking for two-way relationships
if ((aIsDefined && !startNodes.contains(startNode)) || (bIsDefined && !endNodes.contains(endNode))) {
relationshipIterator.remove(); // start or end node mismatch
} else {
@@ -336,17 +373,23 @@ public void delete(Subgraph subgraph) throws SubgraphError {
return relationships;
}
- private void mergeRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties)
+ private void mergeRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties, boolean bothWays)
throws SubgraphError
{
if (relationshipStore.contains(r)) {
- updateRelationships(a, r, b, properties);
+ updateRelationships(a, r, b, properties, bothWays);
} else {
TreeSet<Relationship> relationships;
if (r.hasType()) {
relationships = match(a, b, DynamicRelationshipType.withName(r.getType()));
+ if (bothWays) {
+ relationships.addAll(match(b, a, DynamicRelationshipType.withName(r.getType())));
+ }
} else {
relationships = match(a, b);
+ if (bothWays) {
+ relationships.addAll(match(b, a));
+ }
}
int index = r.getIndex();
int currentIndex = 0;
@@ -359,7 +402,7 @@ private void mergeRelationships(NodeToken a, RelationshipToken r, NodeToken b, M
}
}
if (!found) {
- relationships.addAll(createRelationships(a, r, b, properties));
+ relationships.addAll(createRelationships(a, r, b, properties, bothWays));
}
relationshipStore.put(r, relationships);
}
@@ -415,7 +458,7 @@ private void mergeIndexEntries(RelationshipToken r, IndexToken i, Map<String, Ob
} else if (r.hasType()) {
IndexHits<Relationship> hits = index.get(key, value);
if (hits.size() == 0) {
- for (Relationship relationship : createRelationships(NodeToken.anon(), RelationshipToken.anon(r.getType()), NodeToken.anon(), null)) {
+ for (Relationship relationship : createRelationships(NodeToken.anon(), RelationshipToken.anon(r.getType()), NodeToken.anon(), null, false)) {
index.putIfAbsent(relationship, key, value);
relationships.add(relationship);
}
@@ -436,13 +479,13 @@ private void mergeIndexEntries(RelationshipToken r, IndexToken i, Map<String, Ob
relationshipStore.put(r, relationships);
}
- private Set<Relationship> insertRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties)
+ private Set<Relationship> insertRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties, boolean bothWays)
throws SubgraphError
{
if (relationshipStore.contains(r)) {
- return updateRelationships(a, r, b, properties);
+ return updateRelationships(a, r, b, properties, bothWays);
} else {
- return createRelationships(a, r, b, properties);
+ return createRelationships(a, r, b, properties, bothWays);
}
}
@@ -478,7 +521,7 @@ private void insertIndexEntries(RelationshipToken r, IndexToken i, Map<String, O
if (relationshipStore.contains(r)) {
relationships = relationshipStore.get(r);
} else {
- relationships = createRelationships(NodeToken.anon(), r, NodeToken.anon(), null);
+ relationships = createRelationships(NodeToken.anon(), r, NodeToken.anon(), null, false);
}
for (Map.Entry<String, Object> entry : keyValuePairs.entrySet()) {
String key = entry.getKey();
@@ -507,20 +550,30 @@ private void deleteNodes(NodeToken a, Map<String, Object> properties)
* @param b end node token
* @param properties
*/
- private void deleteRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties) {
+ private void deleteRelationships(NodeToken a, RelationshipToken r, NodeToken b, Map<String, Object> properties, boolean bothWays) {
Set<Relationship> relationships;
if (relationshipStore.contains(r)) {
relationships = relationshipStore.remove(r);
} else if (r.hasType()) {
relationships = match(a, b, DynamicRelationshipType.withName(r.getType()));
+ if (bothWays) {
+ relationships.addAll(match(b, a, DynamicRelationshipType.withName(r.getType())));
+ }
} else {
relationships = match(a, b);
+ if (bothWays) {
+ relationships.addAll(match(b, a));
+ }
}
+ TreeSet<Node> startNodes = new TreeSet<Node>();
+ TreeSet<Node> endNodes = new TreeSet<Node>();
for (Relationship relationship : relationships) {
- this.nodeStore.put(a, relationship.getStartNode());
- this.nodeStore.put(b, relationship.getEndNode());
+ startNodes.add(relationship.getStartNode());
+ endNodes.add(relationship.getEndNode());
relationship.delete();
}
+ this.nodeStore.put(a, startNodes);
+ this.nodeStore.put(b, endNodes);
}
private void deleteIndexEntries(NodeToken a, IndexToken i, Map<String, Object> keyValuePairs)
@@ -637,6 +637,36 @@ public void canCreateMultipleRelationshipsWithExplicitNodeCreationInReverse() th
}
@Test
+ public void canCreateTwoWayRelationships() throws Exception {
+ Subgraph geoff = new Subgraph();
+ geoff.add(ALICE);
+ geoff.add(BOB);
+ geoff.add(CAROL);
+ geoff.add("(A)<-[AB:KNOWS]->(B)");
+ geoff.add("(A)<-[BC:KNOWS]->(C)");
+ geoff.add("(B)<-[AC:KNOWS]->(C)");
+ Map<String, PropertyContainer> out = Geoff.insertIntoNeo4j(geoff, db, null);
+ for (String name : out.keySet()) {
+ System.out.println(name);
+ }
+ db.assertCounts(4, 6);
+ assertRelationshipsExist(out, "[AB.1]", "[AC.1]", "[BC.1]", "[AB.2]", "[AC.2]", "[BC.2]");
+ assertNodesExist(out, "(A)", "(B)", "(C)");
+ Assert.assertEquals(
+ ((Relationship) out.get("[AB.1]")).getStartNode(),
+ ((Relationship) out.get("[AB.2]")).getEndNode()
+ );
+ Assert.assertEquals(
+ ((Relationship) out.get("[AB.1]")).getEndNode(),
+ ((Relationship) out.get("[AB.2]")).getStartNode()
+ );
+ Assert.assertEquals(
+ ((Relationship) out.get("[AB.1]")).getType(),
+ ((Relationship) out.get("[AB.2]")).getType()
+ );
+ }
+
+ @Test
public void canCreateRelationshipsFromNodeSets() throws Exception {
Subgraph geoff = new Subgraph();
geoff.add("(A.1) {\"name\": \"Alice Allison\"}");
@@ -629,6 +629,36 @@ public void canCreateMultipleRelationshipsWithExplicitNodeCreation() throws Exce
}
@Test
+ public void canMergeTwoWayRelationships() throws Exception {
+ Subgraph geoff = new Subgraph();
+ geoff.add(ALICE);
+ geoff.add(BOB);
+ geoff.add(CAROL);
+ Map<String, PropertyContainer> in = Geoff.mergeIntoNeo4j(geoff, db, null);
+ geoff.add("(A)<-[AB:KNOWS]->(B)");
+ geoff.add("(A)<-[BC:KNOWS]->(C)");
+ geoff.add("(B)<-[AC:KNOWS]->(C)");
+ for (int i = 0; i < 10; i++) {
+ Map<String, PropertyContainer> out = Geoff.mergeIntoNeo4j(geoff, db, in);
+ db.assertCounts(4, 6);
+ assertRelationshipsExist(out, "[AB.1]", "[AC.1]", "[BC.1]", "[AB.2]", "[AC.2]", "[BC.2]");
+ assertNodesExist(out, "(A)", "(B)", "(C)");
+ Assert.assertEquals(
+ ((Relationship) out.get("[AB.1]")).getStartNode(),
+ ((Relationship) out.get("[AB.2]")).getEndNode()
+ );
+ Assert.assertEquals(
+ ((Relationship) out.get("[AB.1]")).getEndNode(),
+ ((Relationship) out.get("[AB.2]")).getStartNode()
+ );
+ Assert.assertEquals(
+ ((Relationship) out.get("[AB.1]")).getType(),
+ ((Relationship) out.get("[AB.2]")).getType()
+ );
+ }
+ }
+
+ @Test
public void canCreateRelationshipsFromNodeSets() throws Exception {
Subgraph geoff = new Subgraph();
geoff.add("(A.1) {\"name\": \"Alice Allison\"}");
@@ -42,10 +42,10 @@ public void canLoadGeoffRuleList() {
"\"(doc)<=|People| {\\\"name\\\": \\\"The Doctor\\\"}\"" +
"]";
String payload = "{\"subgraph\":" + geoff + "}";
- RESTDocsGenerator rdgen = gen.get();
- rdgen.expectedStatus(ClientResponse.Status.OK);
- rdgen.payload(payload);
- RESTDocsGenerator.ResponseEntity re = rdgen.post(GEOFF_INSERT);
+ RESTDocsGenerator generator = gen.get();
+ generator.expectedStatus(ClientResponse.Status.OK);
+ generator.payload(payload);
+ RESTDocsGenerator.ResponseEntity re = generator.post(GEOFF_INSERT);
String response = re.entity();
assertTrue(db.index().existsForNodes("People"));
assertTrue(db.index().forNodes("People").get("name", "The Doctor").hasNext());

0 comments on commit d20bd6f

Please sign in to comment.