Neo4J variable-length pattern

515 Views Asked by At

Is it possible to write a Cypher query with a variable-length patter, that specifies that nodes between the start and end node can only be nodes with a certain property?

I think that a query like the following would match all of the below patterns:

MATCH p=(n:Node {foo:"True"})<-[:REL*0..2]-(:Node)<-[:REL]-(:Node {bar:"True"})

(n:Node {foo:"True"})                                        <-[:REL]-(:Node {bar:"True"})
(n:Node {foo:"True"})                    <-[:REL]-(:Node)    <-[:REL]-(:Node {bar:"True"})
(n:Node {foo:"True"})  <-[:REL]-(:Node)  <-[:REL]-(:Node)    <-[:REL]-(:Node {bar:"True"})

I imagine it "copying" the <-[:REL]-(:Node) part of the query 0 to 2 times in between the (n:Node {foo:"True"}) and <-[:REL]-(:Node {bar:"True"}) query parts and constructing some kind of UNION of the matches.

Is this the right way to think about it? And how would i make the variable-lenght query match only paths where the middle nodes have the property {bar:"True"} in the middle nodes like so:

(n:Node {foo:"True"})                                                    <-[:REL]-(:Node {bar:"True"})
(n:Node {foo:"True"})                          <-[:REL]-({bar:"True})    <-[:REL]-(:Node {bar:"True"})
(n:Node {foo:"True"})  <-[:REL]-({bar:"True})  <-[:REL]-({bar:"True})    <-[:REL]-(:Node {bar:"True"})

Would the query below be the right approach?

MATCH p=(n:Node {foo:"True"})<-[:REL*0..2]-(:Node {bar:"True"})<-[:REL]-(:Node {bar:"True"})

It appears to me that this is not the case. Can somebody clearify on where I am going wrong in my thinking process?

2

There are 2 best solutions below

1
On

I think it'd be easy if foo were a relationship property, instead of a node property.

Create a simple example:

MERGE (a:Node {name:"a", foo:True})
MERGE (b:Node {name:"b", bar:True})
MERGE (c:Node {name:"c", bar:True})
MERGE (d:Node {name:"d", bar:False})
MERGE (e:Node {name:"e", bar:True})
MERGE (a)<-[:REL]-(b)
MERGE (b)<-[:REL]-(c)
MERGE (a)<-[:REL]-(d)
MERGE (d)<-[:REL]-(e)

I think you want to match nodes b and c, but not e because we'd have to traverse d which has foo:False.

Make bar a property on the relationships:

MATCH ()<-[r:REL]-(n:Node)
SET r.bar = n.bar

Now you can write:

MATCH (:Node {foo:True})<-[:REL*0..2 {bar:True}]-(n:Node)
RETURN n.name

Which returns b and c as required.

0
On
MATCH path=(:Node {foo:True})-[:REL*0..2]-(n:Node {bar:True})
WHERE all(x in nodes(path) WHERE x.bar=True OR x.foo=True)
RETURN n.name