Hi,
I'm not sure if this is the right place to submit this. I was going to open an FR ticket on github but changed my mind because I thought maybe it's better to have a discussion here first.
I've run into a couple of places where being able to say something like
match (n ....) /* criteria doesn't involve binding an identifier n2 */
where n.prop > threshold or ( (n)-->(n2:label{qualifier:"value"}) and n2.prop < threshold )
would make the query a lot easier to read. I'm aware that, in the case of 'OR', I could use a union after using 2 separate match clauses. And that's what I've been going along with, until now when I need to dynamically translate a user query into Cypher. Here using union can become very complex, as the relationships can nest arbitrary levels deep. But if we had a syntax that can bind new identifiers in predicates, it would be very easy and, more importantly, very readable.
I've prepared a few simple use cases below. (See attached image of the model)
* Data Set
Below creates a set of 7 nodes consisting of 4 circles, 2 squares, and 1 triangle.
2 circles point to 2 squares, 1 circle point to the triangle, and another circle is dangling.
create (:circle{id:1})-[:uses]->(:square{id:1});
create (:circle{id:2})-[:uses]->(:square{id:2});
create (:circle{id:3})-[:uses]->(:triangle{id:3});
create (:circle{id:4});
* Verification
neo4j-sh (?)$ match (c:circle) optional match (c)-[r]->(n) return c, labels(c), r, n, labels(n);
+--------------------------------------------------------------------------+
| c | labels(c) | r | n | labels(n) |
+--------------------------------------------------------------------------+
| Node[10]{id:1} | ["circle"] | :uses[7]{} | Node[11]{id:1} | ["square"] |
| Node[12]{id:2} | ["circle"] | :uses[8]{} | Node[13]{id:2} | ["square"] |
| Node[14]{id:3} | ["circle"] | :uses[9]{} | Node[15]{id:3} | ["triangle"] |
| Node[16]{id:4} | ["circle"] | <null> | <null> | <null> |
+--------------------------------------------------------------------------+
* Queries
1) Circles that don't point to any Squares
This is easy and can be supported with the current syntax.
neo4j-sh (?)$ match (c:circle) where not (c)-->(:square) return c;
+----------------+
| c |
+----------------+
| Node[14]{id:3} |
| Node[16]{id:4} |
+----------------+
2) Circles that don't point Square(1)
This can also be accomplished using the current syntax. So path is already supported; the only thing missing is to bind an identifier which would allow filtering with additional predicate expressions.
neo4j-sh (?)$ match (c:circle) where not (c)-->(:square{id:1}) return c;
+----------------+
| c |
+----------------+
| Node[12]{id:2} |
| Node[14]{id:3} |
| Node[16]{id:4} |
+----------------+
3) Circles that point to Square(1) or with id(4)
Here it's starting to get hairy. Union queries may also become grossly inefficient if the result sets are large. This is where identifier-binding in predicates can help make query more efficient and maybe easier to read also.
neo4j-sh (?)$ match (c:circle{id:4}) return c
> union match (c:circle)-->(s:square{id:1}) return c;
+----------------+
| c |
+----------------+
| Node[16]{id:4} |
| Node[10]{id:1} |
+----------------+
Would like to say
match (c:circle) where (c.id=4) or ( (c)-->(s:square{id:1}) ) return c
4) (Circle, Square) where Circle is either id(4) or points to Squares with id < 4
This is where the union query is beginning to deteriorate in comprehensibility. One has to remember to use optional match. And I don't know what it would look like, if the optional match is 2 or 3 levels deep. Now imagine this is a portion of a larger query, where the 'c' nodes are found by matching in another pattern. Using the union would require one to duplicate that code in all the subsets. Having more shared predicates would have the same effect.
neo4j-sh (?)$ match (c:circle)-->(s:square) where
s.id < 4 return c,s
> union match (c:circle{id:4}) optional match (c)-->(s) return c,s;
+---------------------------------+
| c | s |
+---------------------------------+
| Node[10]{id:1} | Node[11]{id:1} |
| Node[12]{id:2} | Node[13]{id:2} |
| Node[16]{id:4} | <null> |
+---------------------------------+
Hope that was clear. And sorry for the long post.
I also would be more than happy to help implement this if it's not too difficult and someone can point me to the right place to start -- it'd be a feature that I'd really use a lot.
Cheers,
Kai