variable length matching over paths in Cypher - neo4j

Is it possible to allow variable length matching over multiple paths in Cypher? By a path I mean something like two KNOWS traversals or three BLOCKS traversals, where KNOWS and BLOCKS are relationship types in my graph. For example, I'd like to be able to write something like:
MATCH (s)-[r*]->(t)
WHERE ALL (x in type(r) WHERE x=KNOWS/KNOWS OR x= BLOCKS/BLOCKS/BLOCKS)
RETURN s, t
where by "KNOWS/KNOWS" I mean something like (a)-[:KNOWS]->(b)-[:KNOWS]->(c). I want to do this without changing the data itself, by adding relationships such as KNOWS/KNOWS, but rather just as a cypher query.

Yes, you can do this. It's actually much easier than you think:
MATCH p=(s)-[r:KNOWS|BLOCKS*]->(t)
RETURN s, t;
When you specify the r, with a colon you can indicate which types you want to traverse, and separate them by a pipe for OR. The asterisk just operates the way you expect.
If you only want one type of relationship at a time you can do this:
OPTIONAL MATCH p1=(s1)-[r1:KNOWS*]->(t1)
OPTIONAL MATCH p2=(s2)-[r2:BLOCKS*]->(t2)
RETURN p1, p2;
If you want exactly two knows and 3 blocks, then it's:
OPTIONAL MATCH p1=(s1)-[:KNOWS]->()-[:KNOWS]->(t1)
OPTIONAL MATCH p2=(s2)-[:BLOCKS]->()-[:BLOCKS]->()-[:BLOCKS]->(t2)
RETURN p1, p2;

Related

Find nodes where property is not a specific value without WHERE

A node's property has 6 categories. I'd like to leave all the nodes with this property not equal to one of the categories.
It's easy to do with WHERE like this:
MATCH (a)
WHERE a.property <> "category"
RETURN a
I'd like to do it another way without where because it seems to be more efficient. I imagine it like this:
MATCH ( a {property <> "category"} )
RETURN a
Is it possible?
Neo4j MATCH does not have a syntax to inline WHERE NOT <property>=<value>. Furthermore, Cypher is declarative, meaning it only defines what to return, not how to return it. So MATCH (n{id:1}) is equivalent (in execution) to MATCH (n) WHERE n.id=1. The only time WHERE vs inline produces different execution plans is when you don't pair the WHERE clause with the MATCH. By trying to "optimize" your cypher for execution, most of the time you will just be getting in the Cypher planners way. (Unless your original cypher was over complicated)

How to return a path like p=(m)--(n)-[r]-(w), where r is optional in neo4j using cypher

I want to return a path in neo4j ogm to get a object graph. In the path like the title figured out: p=(m)--(n)-[r]-(w). Here r maybe exist or may be not. I want to return p not m, n, w.
You should be able to use a variable-length relationship between n and w with a minimum of 0.
Also, since you are only concerned with the path and aren't doing anything with the variables, you can omit them, they serve no purpose, though this kind of query, with no labels, properties, or relationship types restricting what you're looking for will match an awful lot of nodes on nearly any kind of graph.
MATCH p = ()--()-[*0..1]-()
RETURN p
Probably, something like
p=(m)--(n)-[r]-(w)
RETURN p
UNION
p=(m)--(n)
RETURN m,n,null,null

match a branching path of variable length

I have a graph which looks like this:
Here is the link to the graph in the neo4j console:
http://console.neo4j.org/?id=av3001
Basically, you have two branching paths, of variable length. I want to match the two paths between orange node and yellow nodes. I want to return one row of data for each path, including all traversed nodes. I also want to be able to include different WHERE clauses on different intermediate nodes.
At the end, i need to have a table of data, like this:
a - b - c - d
neo - morpheus - null - leo
neo - morpheus - trinity - cypher
How could i do that?
I have tried using OPTIONAL MATCH, but i can't get the two rows separately.
I have tried using variable length path, which returns the two paths but doesn't allow me to access and filter intermediate nodes. Plus it returns a list, and not a table of data.
I've seen this question:
Cypher - matching two different possible paths and return both
It's on the same subject but the example is very complex, a more generic solution to this simpler problem is what i'm looking for.
You can define what your end node by using WHERE statement. So in your case end node has no outgoing relationship. Not sure why you expect a null on return as you said neo - morpheus - null - leo
MATCH p=(n:Person{name:"Neo"})-[*]->(end) where not (end)-->()
RETURN extract(x IN nodes(p) | x.name)
Edit:
may not the the best option as I am not sure how to do this programmatically. If I use UNWIND I get back only one row. So this is a dummy solution
MATCH p=(n{name:"Neo"})-[*]->(end) where not (end)-->()
with nodes(p) as list
return list[0].name,list[1].name,list[2].name,list[3].name
You can use Cypher to match a path like this MATCH p=(:a)-[*]->(:d) RETURN p, and p will be a list of nodes/relationships in the path in the order it was traversed. You can apply WHERE to filter the path just like with node matching, and apply any list functions you need to it.
I will add these examples too
// Where on path
MATCH p=(:a)-[*]-(:d) WHERE NONE(n in NODES(p) WHERE n.name="Trinity") WITH NODES(p) as p RETURN p[0], p[1], p[2], p[3]
// Spit path into columns
MATCH p=(:a)-[*]-(:d) WITH NODES(p) as p RETURN p[0], p[1], p[2], p[3]
// Match path, filter on label
MATCH p=(:a)-[*]-(:d) WITH NODES(p) as p RETURN FILTER(n in p WHERE "a" in LABELS(n)) as a, FILTER(n in p WHERE "b" in LABELS(n)) as b, FILTER(n in p WHERE "c" in LABELS(n)) as c, FILTER(n in p WHERE "d" in LABELS(n)) as d
Unfortunately, you HAVE to explicitly set some logic for each column. You can't make dynamic columns (that I know of). In your table example, what is the rule for which column gets 'null'? In the last example, I set each column to be the set of nodes of a label.
I.m.o. you're asking for extensive post-processing of the results of a simply query (give me all the paths starting from Neo). I say this because :
You state you need to be able to specify specific WHERE clauses for each path (but you don't specify which clauses for which path ... indicating this might be a dynamic thing ?)
You don't know the size of the longest path beforehand ... but you still want the result to be a same-size-for-all-results table. And would any null columns then always be just before the end node ? Why (for that makes no real sense other then convenience) ?
...
Therefore (and again i.m.o.) you need to process the results in a (Java or whatever you prefer) program. There you'll have full control over the resultset and be able to slice and dice as you wish. Cypher (exactly like SQL in fact) can only do so much and it seems that you're going beyond that.
Hope this helps,
Regards,
Tom
P.S. This may seem like an easy opt-out, but look at how simple your query is as compared to the constructs that have to be wrought trying to answer your logic. So ... separate the concerns.

Combining collection results of multiple MATCH cypher queries

I have a graph in which, there can exist three patters of paths between (:srcType) and (:destType):
Pattern 1
(:srcType)<-[]-()<-[]-(srcParent)<-[]-(center)-[]->(destParent)-[]->()-[]->(:destType)
Notice that, here the direction of relationships reverses as path goes through (center):<-[]-(center)-[]->
Pattern 2
In this pattern (srcParent) it self is a center. Thus direction of relationships reverses across (srcParent):
(:srcType)<-[]-()<-[]-(srcParent)-[]->(destParent)-[]->()-[]->(:destType)
Pattern 3
In this pattern (destParent) it self is a center. Thus direction of relationships reverses across (destParent):
(:srcType)<-[]-()<-[]-(srcParent)<-[]-(destParent)-[]->()-[]->(:destType)
I am giving id of (:srcType) and trying to obtain all (:destType) nodes. Note that given one (:srcType) it can have one (:destType) node associated with it following first pattern, another following 2nd pattern and few more following third pattern. I am trying to retrieve single collection containing all these (:destType) nodes. So I have combined above queries as follows:
MATCH (src:srcType)<-[]-()<-[]-(srcParent)<-[]-(center)-[]->(destParent)-[]->()-[]->(dest1:destType)
WHERE id(src)=3
WITH dest1
MATCH (src:srcType)<-[]-()<-[]-(srcParent)-[]->(destParent)-[]->()-[]->(dest2:destType)
WHERE id(src)=3
WITH dest1, dest2
MATCH (src:srcType)<-[]-()<-[]-(srcParent)<-[]-(destParent)-[]->()-[]->(dest3:destType)
WHERE id(src)=3
RETURN dest1, dest2, dest3
So here I am matching each pattern one by one in MATCH clauses and feeding (:destType)s output of one MATCH to next one using WITH clause. At the end I am returning all destTypes.
Q1. But this is not executing. When I run one of the pattern (single WITH), it correctly returns whichever (:destType) that matches the path. But with above query it returns 0 rows. Why is it so?
Q2. Also instead of returning all destTypes, I want to return single collection containing elements of all of them. Knowing that collections can be merged using +, is it possible to return something like below?
RETURN destType1+destType2+destType2
Note
I will need to add different filters for each pattern afterwards. So the future query may look something like this:
MATCH (src:srcType)<-[]-()<-[]-(srcParent)<-[]-(center)-[]->(destParent)-[]->()-[]->(dest1:destType)
WHERE id(src)=3 AND srcParent.prop1='a'
WITH dest1
MATCH (src:srcType)<-[]-()<-[]-(srcParent)-[]->(destParent)-[]->()-[]->(dest2:destType)
WHERE id(src)=3 AND destParent.prop2='b'
WITH dest1, dest2
MATCH (src:srcType)<-[]-()<-[]-(srcParent)<-[]-(destParent)-[]->()-[]->(dest3:destType)
WHERE id(src)=3 AND srcParent.prop3='c'
RETURN dest1, dest2, dest3
Given that these patterns may or may not be present, and that you want a collection of all results at the end, a good approach would be to match on the src node first, then use OPTIONAL MATCHes, and collect the results along the way, adding new ones in.
If we modify your last query, it may look something like this:
MATCH (src:srcType)
WHERE id(src) = 3
OPTIONAL MATCH (src)<-[]-()<-[]-(srcParent)<-[]-(center)-[]->(destParent)-[]->()-[]->(dest1:destType)
WHERE srcParent.prop1='a'
WITH src, COLLECT(dest1) as dests
OPTIONAL MATCH (src)<-[]-()<-[]-(srcParent)-[]->(destParent)-[]->()-[]->(dest2:destType)
WHERE destParent.prop2='b'
WITH src, dests + COLLECT(dest2) as dests
OPTIONAL MATCH (src)<-[]-()<-[]-(srcParent)<-[]-(destParent)-[]->()-[]->(dest3:destType)
WHERE srcParent.prop3='c'
RETURN dests + COLLECT(dest3) as dests

Reusing path in multiple MATCH UNION queries cypher neo4j

I'd like to pull and combine data from several different paths that share a path at the beginning, not all of which might exist. For example, I'd like to do something like this:
MATCH (:Complex)-[:PATH]->(s:Somewhere)-[:FETCHING]->(data)
RETURN data.attribute
UNION ALL
MATCH (s)-[:OPTIONAL]->(o:OtherData)
RETURN o.attribute;
so that it doesn't retrace the path up to s. I can't actually do this, though, because UNION separates queries and the (s)-[:OPTIONAL] in the second part will match anything with an outgoing OPTIONAL relation; the s is a loose handle.
Is there a better way of doing this than repeating the path:
MATCH (:Complex)-[:PATH]->(s:Somewhere)-[:FETCHING]->(data)
RETURN data.attribute
UNION ALL
MATCH (:Complex)-[:PATH]->(s:Somewhere)-[:OPTIONAL]->(o:OtherData)
RETURN o.attribute;
I made a few attempts using WITH, but they all either caused the query to return nothing if any part failed, or I could not get them to line up into a single column and instead got rows with redundant data, or (with multiple, nested WITHs, which I'm not sure about the scoping of) just fetching everything.
Have you looked at the semantics of an optional match? So you can match to s, beyond s and your optional component. Something like:
MATCH (:Complex)-[:PATH]->(s:Somewhere)
MATCH (s)-[:FETCHING]->(data)
OPTIONAL MATCH (s)-[:OPTIONAL]->(otherData)
RETURN data.attribute, otherData.attribute
Sorry I missed the importance of a single column, is it really important?
You can gather the vaues into a single collection :
MATCH (:Complex)-[:PATH]->(s:Somewhere)
MATCH (s)-[:FETCHING]->(data)
OPTIONAL MATCH (s)-[:OPTIONAL]->(otherData)
RETURN [data.attribute] + COLLECT(otherData.attribute)
But doesn't this work for a single column:
MATCH (:Complex)-[:PATH]->(s:Somewhere)
MATCH (s)-[:FETCHING]->(data)
OPTIONAL MATCH (s)-[:OPTIONAL]->(otherData)
WITH [data.attribute] + COLLECT(otherData.attribute) as col
RETURN UNWIND col AS val

Resources