Reputation: 83
I'm looking for an efficient way to do injective matching in Neo4j. If you're not sure what I mean by that; I simply want matches to be returned where every returned node in a match is unique (e.g. has a unique ID), and the same holds for paths.
Using the diagram from Wikipedia (above), the domain, X, of matching is the nodes and paths in the pattern, and the co-domain, Y, is the Database's internal Graph. The above diagram is injective as no 2 arrows from X point to the same element in Y (so no 2 nodes from the pattern are matched to the same node in the Graph, and the same holds for edges), whereas default Neo4J matching is non-injective and allows 2 nodes from the pattern to be matched to the same node in the Graph (you can visualise an example of non-injective matching as the arrows from 1 and 2 in X both pointing to D in Y in the above diagram). Conventional Graph Theory would call matching multiple items from the domain X to the same item in the co-domain Y "merging", but I can appreciate that that terminology may be confusing in this context.
I can simulate this for specific queries by specifying that matched nodes are distinct:
match (a), (b) where not id(a) = id(b) return a, b
But I want to do it in a general sense without having to be this explicit in every query. So for this example I would like to return matches where (a) and (b) are unique nodes but I would like to do this with some general behaviour rather than specifying the uniqueness based on ID.
It does seem that paths are already guaranteed to be unique when I query, but if someone could confirm that that would be great.
Upvotes: 1
Views: 121
Reputation: 83
Edit: @InverseFalcon has given a better solution using the APOC plugin but if you cannot use APOC for whatever reason or APOC support stops then you can check the uniqueness of IDs manually as described here.
Paths are unique by design so injective matching can be achieved by simple ensuring that nodes are unique. Taking inspiration from this answer, apparently you cannot wrap a query to achieve this, but a short ALL statement in the WHERE clause at least simplifies the id checks.
If the pattern you want to match is described by:
MATCH p1=(a)-[r]->(b), (c)
RETURN a, b, c, p1
Which is a very simple (any 3 nodes with any relationship from the 1st to the second) pattern, then you can ensure the uniqueness of those 3 nodes by checking each node's ID against the others:
MATCH p1=(a)-[r]->(b), (c)
WHERE ALL(n in [a, b, c] where
1=length(filter(m in [a, b, c] where id(m)=id(n))))
RETURN a, b, c, p1
This works by checking each node in the set of nodes we wish to be unique ([a, b, c]) and comparing its ID against every other node's ID in that set, making sure that there is only 1 matching ID (itself) in the set. This is the reason for the "where 1=length()" part.
So using this idea generally, returned nodes are guaranteed to be unique and returned paths from the pattern are guaranteed to be unique (by Neo4j's design) making the matching process injective.
It's not an ideal solution as the statement must be custom-written from the query but its at least a single WHERE condition which grows linearly to the number of nodes, instead of creating a condition for each pair of nodes where the number of conditions grows by a factorial order to the number of nodes.
Upvotes: 0
Reputation: 30407
If you just want to make sure the nodes of the variables in your match are not the same, you can install APOC Procedures and make use of some of collection helper functions, specifically apoc.coll.containsDuplicates()
.
An example usage might look like:
MATCH p1=(a)-[r]->(b), (c)
WHERE NOT apoc.coll.containsDuplicates([a, b, c])
RETURN a, b, c, p1
Upvotes: 1
Reputation: 6534
I am not really sure what exactly do you want, but I can upgrade your query to be more efficient.
Match (a),(b) where id(a) < id(b)
Return a,b
If you want to return distinct nodes or relationships cypher has a distinct
function. Example:
Match (a)-->(b)
Return distinct(a)
P.s. always use labels for nodes as it speeds up query execution
Upvotes: 1