Skip to content

Commit 67b9019

Browse files
authored
Use existential sub-queries (#179)
* Add tests for #27 * use existential subqueries
1 parent d5f89cb commit 67b9019

File tree

4 files changed

+157
-8
lines changed

4 files changed

+157
-8
lines changed

core/src/main/kotlin/org/neo4j/graphql/parser/QueryParser.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ class RelationPredicate(
6666
fun createExistsCondition(propertyContainer: PropertyContainer): Condition {
6767
val relation = createRelation(propertyContainer as? Node
6868
?: throw IllegalStateException("""propertyContainer is expected to be a Node but was ${propertyContainer.javaClass.name}"""))
69+
val condition = CypherDSL.match(relation).asCondition()
6970
return when (op) {
70-
RelationOperator.NOT -> relation.asCondition()
71-
RelationOperator.EQ_OR_NOT_EXISTS -> Conditions.not(relation)
71+
RelationOperator.NOT -> condition
72+
RelationOperator.EQ_OR_NOT_EXISTS -> condition.not()
7273
else -> throw IllegalStateException("$op should not be set for Null value")
7374
}
7475

core/src/test/resources/filter-tests.adoc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,9 @@ RETURN person {
21722172
[source,cypher]
21732173
----
21742174
MATCH (person:Person)
2175-
WHERE NOT (person)-[:WORKS_AT]->(:Company)
2175+
WHERE NOT (EXISTS {
2176+
MATCH (person)-[:WORKS_AT]->(:Company)
2177+
})
21762178
RETURN person {
21772179
.name
21782180
} AS person
@@ -2204,7 +2206,9 @@ RETURN person {
22042206
[source,cypher]
22052207
----
22062208
MATCH (person:Person)
2207-
WHERE (person)-[:WORKS_AT]->(:Company)
2209+
WHERE EXISTS {
2210+
MATCH (person)-[:WORKS_AT]->(:Company)
2211+
}
22082212
RETURN person {
22092213
.name
22102214
} AS person
@@ -2698,7 +2702,9 @@ RETURN p {
26982702
[source,cypher]
26992703
----
27002704
MATCH (p:Person)
2701-
WHERE NOT (p)-[:WORKS_AT]->(:Company)
2705+
WHERE NOT (EXISTS {
2706+
MATCH (p)-[:WORKS_AT]->(:Company)
2707+
})
27022708
RETURN p {
27032709
.name
27042710
} AS p
@@ -2736,7 +2742,9 @@ RETURN p {
27362742
[source,cypher]
27372743
----
27382744
MATCH (p:Person)
2739-
WHERE (p)-[:WORKS_AT]->(:Company)
2745+
WHERE EXISTS {
2746+
MATCH (p)-[:WORKS_AT]->(:Company)
2747+
}
27402748
RETURN p {
27412749
.name
27422750
} AS p
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
:toc:
2+
3+
= Github Issue #27: Filtering on multi-relationship existence
4+
5+
== Schema
6+
7+
[source,graphql,schema=true]
8+
----
9+
type Person {
10+
name: String!
11+
friends: [Person!]! @relation(name:"knows",direction:OUT)
12+
pets: [Pet!] @relation(name:"knows",direction:OUT)
13+
}
14+
15+
type Pet {
16+
name: String!
17+
}
18+
----
19+
20+
== query friends name not null
21+
22+
.GraphQL-Query
23+
[source,graphql]
24+
----
25+
{
26+
person (filter: { friends: { name_not: null } }) {
27+
name
28+
}
29+
}
30+
----
31+
32+
.Cypher Params
33+
[source,json]
34+
----
35+
{ }
36+
----
37+
38+
.Cypher
39+
[source,cypher]
40+
----
41+
MATCH (person:Person)
42+
WHERE all(filterPersonPersonCond IN [(person)-[:knows]->(filterPersonPerson:Person) | filterPersonPerson.name IS NOT NULL]
43+
WHERE filterPersonPersonCond)
44+
RETURN person {
45+
.name
46+
} AS person
47+
----
48+
49+
== query friends not null
50+
51+
.GraphQL-Query
52+
[source,graphql]
53+
----
54+
{
55+
person (filter: { friends_not: null }) {
56+
name
57+
}
58+
}
59+
----
60+
61+
.Cypher Params
62+
[source,json]
63+
----
64+
{ }
65+
----
66+
67+
.Cypher
68+
[source,cypher]
69+
----
70+
MATCH (person:Person)
71+
WHERE EXISTS {
72+
MATCH (person)-[:knows]->(:Person)
73+
}
74+
RETURN person {
75+
.name
76+
} AS person
77+
----
78+
79+
== query friends null
80+
81+
.GraphQL-Query
82+
[source,graphql]
83+
----
84+
{
85+
person (filter: { friends: null }) {
86+
name
87+
}
88+
}
89+
----
90+
91+
.Cypher Params
92+
[source,json]
93+
----
94+
{ }
95+
----
96+
97+
.Cypher
98+
[source,cypher]
99+
----
100+
MATCH (person:Person)
101+
WHERE NOT (EXISTS {
102+
MATCH (person)-[:knows]->(:Person)
103+
})
104+
RETURN person {
105+
.name
106+
} AS person
107+
----
108+
109+
== query friends name not null
110+
111+
.GraphQL-Query
112+
[source,graphql]
113+
----
114+
{
115+
person (filter: { friends: { name_not: null } }) {
116+
name
117+
}
118+
}
119+
----
120+
121+
.Cypher Params
122+
[source,json]
123+
----
124+
{ }
125+
----
126+
127+
.Cypher
128+
[source,cypher]
129+
----
130+
MATCH (person:Person)
131+
WHERE all(filterPersonPersonCond IN [(person)-[:knows]->(filterPersonPerson:Person) | filterPersonPerson.name IS NOT NULL]
132+
WHERE filterPersonPersonCond)
133+
RETURN person {
134+
.name
135+
} AS person
136+
----

core/src/test/resources/optimized-query-for-filter.adoc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,9 @@ RETURN movie {
783783
----
784784
MATCH (movie:Movie)
785785
WITH movie
786-
WHERE (movie)<-[:REVIEWED]-(:Person)
786+
WHERE EXISTS {
787+
MATCH (movie)<-[:REVIEWED]-(:Person)
788+
}
787789
WITH DISTINCT movie
788790
RETURN movie {
789791
.title
@@ -833,7 +835,9 @@ RETURN movie {
833835
----
834836
MATCH (movie:Movie)
835837
WITH movie
836-
WHERE NOT (movie)<-[:REVIEWED]-(:Person)
838+
WHERE NOT (EXISTS {
839+
MATCH (movie)<-[:REVIEWED]-(:Person)
840+
})
837841
WITH DISTINCT movie
838842
RETURN movie {
839843
.title

0 commit comments

Comments
 (0)