cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Path with two different relationship labels. One label option another required

Consider a simple graph like:

A-[:label1]->B-[:label2]->C-[:label1]->D-[:label1]->E-[:label2]->F

I'd like to find paths in the graph that have both label1 and label2, e.g.,

MATCH r=(n1)-[x:label1|label2*]->(n2)

In addition, if a node x has a relationship of type 'label2', e.g., x-[:label2]->y then if x is in the path, I need y to be included as well. This restriction doesn't apply to label1.

So for the graph in question, a path of form A->B->C is acceptable, but path A->B is not.

I've tried something like:

MATCH r=(n1)-[x:label1|label2*]->(n2)
WITH nodes(r) as allnodes, n1, n2
WHERE
none (y in allnodes where exists ((y)-[q:label2]->(z)) AND not (z in allnodes))

RETURN allnodes,n1,n2

But the syntax checker doesn't like it, saying:

Variable q not defined (line 6, column 40 (offset: 260))
"none (y in allnodes where exists ((y)-[q:label2]->(z)) AND not (z in allnodes))"

I'm sure there's a simple way to do this in Cypher, but I haven't yet figured it out.

Thanks in advance for your help.

5 REPLIES 5

The logic of your query may still need tweaking, but for the syntax error have you simply tried removing the q? Once you move into the conditional part of a query (WHERE..) I don't believe you can introduce new variables in this way (and probably don't need to).

Dear Terry:

Thanks for the help. I tried removing 'q' (and 'z', since that doesn't work either), and tried the following query:

MATCH r=(n1)-[x:label1|label2*]->(n2)
WITH r, nodes(r) as allnodes, n1, n2
WHERE
none (y in allnodes where exists ((y)-[:label2]->()) )

RETURN r

This query returns a single edge, C->D, which is not allowed since there's a 'label2' edge connected to C. A valid response would be B->C->D

Here's cypher code to create the sample graph:

CREATE (a:Node {name:'A' })
CREATE (b:Node {name:'B'})
CREATE (a)-[:label1 ]->(b)

CREATE (c:Node {name:'C' })
CREATE (b)-[:label2 ]->(c)
CREATE (d:Node {name:'D' })
CREATE (c)-[:label1 ]->(d)

CREATE (e:Node {name:'E' })
CREATE (d)-[:label1 ]->(e)
CREATE (f:Node {name:'F' })
CREATE (e)-[:label2 ]->(f)

Here's another unsuccessful attempt using apoc.path.expand:

MATCH (p:Node)
CALL apoc.path.expand(p, "label2>|label1>", null,0,10)
YIELD path
where length(path) > 0
return nodes(path) as allnodes

"allnodes" │
╞══════════════════════════════════════════════════════════════════════╡
│[{"name":"A"},{"name":"B"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"A"},{"name":"B"},{"name":"C"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"D"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"D"},{"name":"E"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"D"},{"name":"E"},{"na│
│me":"F"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"B"},{"name":"C"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"B"},{"name":"C"},{"name":"D"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"B"},{"name":"C"},{"name":"D"},{"name":"E"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"B"},{"name":"C"},{"name":"D"},{"name":"E"},{"name":"F"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"C"},{"name":"D"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"C"},{"name":"D"},{"name":"E"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"C"},{"name":"D"},{"name":"E"},{"name":"F"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"D"},{"name":"E"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"D"},{"name":"E"},{"name":"F"}] │
├──────────────────────────────────────────────────────────────────────┤
│[{"name":"E"},{"name":"F"}] │
└───────────────────────────


This is a superset of the desired response. The rows that need to be filtered out are:

A->B
A->B->C->D->E
C->D
C->D->E->F
D->E

The following query, using 'CALL' seems to work:

MATCH r=(n1)-[x:label1|label2*]->(n2)
WITH relationships(r) as allpaths, nodes(r) as allnodes,n1,n2
CALL {
    WITH allpaths, allnodes,n1,n2
    OPTIONAL MATCH (n2)-[:label2]->(n3)
    OPTIONAL MATCH (n4)-[:label2]->(n1)
    RETURN n1 as nx, n2 as ny, n3, n4, allnodes as allnodesx,allpaths as allpathsx
}
WITH nx,ny,n3, n4, allnodesx, allpathsx
WHERE n3 is null and n4 is null
RETURN n4,nx,ny,n3, allnodesx, allpathsx

Is there an easier way to do this?

Yes, there should be a much easier way to do this.

Sounds like all you need to make sure of is that your start and end nodes don't have any associated :label2 relationships. We can do this by excluding patterns like that in the WHERE clause:

MATCH path = (start:Node)-[:label1|label2*]->(end:Node)
WHERE NOT ()-[:label2]->(start) AND NOT (end)-[:label2]->()
RETURN [node in nodes(path) | node.name] as pathNodes 

You can also just return the path variable if that's easier.