Skip to content
This repository was archived by the owner on Mar 10, 2025. It is now read-only.

Commit 2b3618c

Browse files
Generate correct match strings for Neo4j relationships (#621)
* Add test to check many-to-many queries are generated properly * Generate correct match strings for Neo4j relationships
1 parent 9bc887d commit 2b3618c

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/CypherBuilder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ public void setStartNode(String startNode) {
7373

7474
public void addMatch(String match) {
7575
if(!matches.contains(match)) {
76-
matches.add(match);
76+
// Add empty match if the target entity is already defined
77+
final String lastNode = match.substring(match.lastIndexOf('('));
78+
final boolean alreadyDefined = matches.stream().anyMatch(m -> m.endsWith(lastNode));
79+
matches.add(alreadyDefined ? "" : match);
7780
}
7881
}
7982

@@ -174,6 +177,8 @@ public String build() {
174177
}
175178

176179
for (String m : matches) {
180+
// Skip empty matches
181+
if (m.isEmpty()) continue;
177182
cypher.append(COMMAND_SEPARATOR).append(m);
178183
}
179184

grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jQuery.groovy

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,18 @@ class Neo4jQuery extends Query {
712712
}
713713
else {
714714
String targetNodeName = "m_${builder.getNextMatchNumber()}"
715+
String nextPrefix = targetNodeName
716+
String relationship = ""
715717
Association association = (Association)aq.association
718+
if (criterion.entity instanceof RelationshipPersistentEntity) {
719+
String type = (criterion.entity as RelationshipPersistentEntity).type()
720+
relationship = "$nextPrefix:$type"
721+
targetNodeName = "m_${builder.getNextMatchNumber() + 1}"
722+
}
716723
builder.addMatch(
717-
entity.formatAssociationPatternFromExisting(association, "", prefix, targetNodeName)
724+
entity.formatAssociationPatternFromExisting(association, relationship, prefix, targetNodeName)
718725
)
719-
def s = CRITERION_HANDLERS.get(aq.criteria.getClass()).handle(entity, aq.criteria, builder, targetNodeName)
726+
def s = CRITERION_HANDLERS.get(aq.criteria.getClass()).handle(entity, aq.criteria, builder, nextPrefix)
720727
return new CypherExpression(s)
721728
}
722729

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package grails.gorm.tests
2+
3+
import grails.gorm.annotation.Entity
4+
import grails.neo4j.Relationship
5+
import grails.neo4j.mapping.MappingBuilder
6+
import spock.lang.Issue
7+
8+
class ManyToManyQuerySpec extends GormDatastoreSpec {
9+
10+
@Override
11+
List getDomainClasses() {
12+
[TestA, TestB, TestC]
13+
}
14+
15+
@Issue('309')
16+
def "many-to-many relationships are queried correctly"() {
17+
setup:
18+
def a = new TestA(testA: "aaa").save(flush: true, failOnError: true)
19+
def c = new TestC(testC: "ccc").save(flush: true, failOnError: true)
20+
new TestB(from: a, to: c, testB: "bbb").save(flush: true, failOnError: true)
21+
22+
when:
23+
def result = TestA.createCriteria().list {
24+
ccc {
25+
eq("testB", "bbb")
26+
to {
27+
eq("testC", "ccc")
28+
}
29+
}
30+
}
31+
32+
then:
33+
result.size() == 1
34+
result[0].testA == a.testA
35+
}
36+
}
37+
38+
@Entity
39+
class TestA {
40+
String testA
41+
static hasMany = [ccc: TestB]
42+
}
43+
44+
@Entity
45+
class TestC {
46+
String testC
47+
static hasMany = [aaa: TestB]
48+
}
49+
50+
@Entity
51+
class TestB implements Relationship<TestA, TestC> {
52+
String testB
53+
static mapping = MappingBuilder.relationship {
54+
type "A_REL_C"
55+
direction Direction.OUTGOING
56+
}
57+
}

0 commit comments

Comments
 (0)