prees
prees

Reputation: 82

Cypher - matching two different possible paths and return both

I have a a data set that I have represented here as an example: http://console.neo4j.org/?id=3dq78v

What I am trying to do is for every Z node in my graph (the example only has one but I have many) I want to capture a set of properties that cover all all of the associated A, B, C and D nodes and relationships.

I have come across two issues trying to do this. The first is that the C nodes are either connected to the to the B nodes OR the A nodes. Second the D nodes are connected to C nodes but not always present.

I am looking to output a table that looks something like this:

Z.prop | A.prop | B.prop | (A-B rel).prop | c.prop | (c-d rel).prop | d.prop

I have tried a lot of combinations of using OPTIONAL MATCH and using WITH but I can not get it. Everything I have tried with OPTIONAL MATCH will find that match if it exists and then not allow me to maintain the items that did not match this optional match (Which I realize is what it is supposed to do). I can share more specific queries I have tried if that will help.

Any insight would be great!

Edit: using version Neo4j 2.0.3 Edit: updated console link with a small correction

Here is a query I have tried. I realize why this doesn't work, but maybe you can see my 'logic' I want the optional match to not remove nodes if they dont match if it finds some matches. I want the ones that dont match and do.

MATCH (z:Z)-[:has]->(a:A)
OPTIONAL MATCH (a)-[:has*1..2]->(c:C)
OPTIONAL MATCH (a)-[:has]->(b:B)-[:CONTAINS]->(c)
OPTIONAL MATCH (c)-[cd:knows]->(d:D)
RETURN z.name, a.name, b.name, c.name, d.name, cd.score;

EDIT: What I am trying to do is use the results from the 4 queries below but get the results with 1 query

#1
MATCH (z:Z)-[:has]->(a:A)
MATCH (A)-[mr:has]->(b:B)-[:has]->(c:C)
MATCH (c)-[ds:knows]->(d:d)
RETURN  a.name, b.name, mr.order, c.name, d.name, ds.score;

#2
MATCH (z:Z)-[:has]->(a:A)
MATCH (A)-[mr:has]->(b:B)-[:has]->(c:C)
WHERE NOT (c)-[:knows]->(:d)
RETURN a.name, mr.order, b.name, c.name;

#3
MATCH (z:Z)-[:has]->(a:A)
MATCH (A)-[:has]->(c:C)
WHERE NOT (c)-[:knows]->(:d)
RETURN  a.name, c.name;

#4
MATCH (z:Z)-[:has]->(a:A)
MATCH (A)-[:has]->(c:C)
MATCH (c)-[ds:knows]->(d:d)
RETURN a.name, c.name, d.name, ds.score;

These don't work the way I was expecting though because some results. For example I was expecting query 3 to only return:

A2  C4  d4  8
A2  C4  d5  6
A2  C4  d6  9

EDIT - EXACT output I am aiming for:

a.name  mr.order b.name c.name  d.name  d.score
A1      1        B3
A1      2        B1     C2
A1      2        B1     C5
A1      2        B1     C1      d1      1
A1      2        B1     C1      d3      4
A1      2        B1     C1      d2      3
A2      1        B4
A2      2        B2     C3
A2                      C4      d4      8
A2                      C4      d5      6
A2                      C4      d6      9

This matches 9 of the 11 lines I am looking for, it misses the lines with B3 and B4

MATCH (z:Z)-[:has]->(a:A)
WITH a, z
MATCH (a)-[*1..2]-(c:C)
OPTIONAL MATCH (a)-[mr:has]->(b:B)-[:has]-(c:C)
WITH a, b, c, z, mr
OPTIONAL MATCH (c)-[cd:knows]->(d:d)
RETURN z.name, a.name, mr.order, b.name, c.name, d.name, cd.score;

Upvotes: 2

Views: 2252

Answers (1)

Javier de la Rosa
Javier de la Rosa

Reputation: 669

Not completely sure about what you would expect as the ouput, but this one might work:

MATCH (z:Z)-[:has]->(a:A)
WITH a, z
MATCH (b:B), (a)-[*1..2]-(c:C)
WHERE (a)-[:has]->(b)-[:CONTAINS]->(c) OR (a)-[:has*1..2]->(c)
WITH a, b, c, z
OPTIONAL MATCH (c)-[cd:knows]->(d:d)
RETURN z.name, a.name, b.name, c.name, d.name, cd.score;

From there you could optimize it.

Edit After your adding, I think that the only one to achieve that is by using a UNION of two queries

MATCH (a:A)-[mr:has]->(b:B)
OPTIONAL MATCH (b)-->(c:C)
OPTIONAL MATCH (c)-[cd]->(d:d)
RETURN a.name, mr.order, b.name, c.name, d.name, cd.score
UNION
MATCH (a:A)-[mr:has]->(c:C)
OPTIONAL MATCH (c:C)-[cd]->(d:d)
OPTIONAL MATCH (b:B)-->(c:C)
RETURN a.name, mr.order, b.name, c.name, d.name, cd.score

Note that in the second query after the UNION, the OPTIONAL MATCH (b:B)-->(c:C) is only there to make the UNION possible, since Neo4j doesn't allow to just return an empty string instead of b.name. The good news is that UNION will remove duplicates, therefore resulting in the expected result.

a.name  mr.order    b.name  c.name  d.name  cd.score
A1      1           B3          
A1      2           B1      C5      
A1      2           B1      C1      d1      1
A1      2           B1      C1      d2      4
A1      2           B1      C1      d3      3
A1      2           B1      C2      
A2      1           B4          
A2      2           B2      C3      
A2                          C4      d4      8
A2                          C4      d5      6
A2                          C4      d6      9
Showing 1 to 11 of 11 entries

Query took 9 ms and returned 11 rows.

Upvotes: 3

Related Questions