Cypher path needs to exclude a certain relation - neo4j

I have this graph:
A-[:X]->B-> a whole tree of badness
A-[:Y]->C-> a whole tree of goodness
I would like to know how to specify a path starting with A that excludes the :X relationship.
In this case "Y" could be any one of a number of different edge types. I do not want to specify them explicitly.
How do I write a path statement that includes A-[*]-B where * is not :X but can be anything else?

Solution for a fixed number of relationships between A and B
You can exclude a relationship type by matching all relationships from A to B and then filter out a specific type with WHERE NOT
MATCH p = (a:Label1)-[]-(b:Label2)
WHERE NOT (a)-[:X]-(b)
RETURN p
Solution for a variable length path between A and B
If you have a variable length path between A and B you cannot put the exact pattern in the WHERE NOT. Instead, you can use a NONE predicate on the path:
MATCH p = (a:Label1)-[*]-(b:Label2)
// this WHERE makes sure that none of the relationships in the
// returned path fulfill the criterion type(relationship) = 'X'
WHERE NONE (r in relationships(p) WHERE type(r) = 'X')
RETURN p

This Cypher query is simpler than the variable-length path query from #MartinPreusse, as it avoids using the RELATIONSHIPS function. Profiling shows that its execution plan is also a bit simpler, so it might be faster.
MATCH p=(a:Label1)-[rels*]-(b:Label2)
WHERE NONE (r IN rels WHERE type(r)= 'X')
RETURN p

Related

Cypher match path with intermediate nodes

I have the following graph with Stop (red) and Connection (green) nodes.
I want to find the shortest path from A to C using a cost property on Connection.
I would like to avoid making Connection a relationship because than I loose the CONTAINS relationship of Foo.
I can match a single hop like this
MATCH p=(:Stop {name:'A'})<-[:BEGINS_AT]-(:Connection)-[:ENDS_AT]->(:Stop {name:'B'}) RETURN p
but this does not work with an arbitrary number of Connections like it would with relationships and [*].
I also tried to make a projection down to simple relationships but it seems like I cannot do something with this without GDS.
MATCH (s1:Stop)<-[:BEGINS_AT]-(c:Connection)-[:ENDS_AT]->(s2:Stop) RETURN id(s1) AS source, id(s2) AS target, c.cost AS cost
Note that the connection is unidirectional, so it must not be possible to go from C to A.
Is there a way to do this without any Neo4j plugins?
This should get all usable paths (without plugins):
WITH ['BEGINS_AT', 'ENDS_AT'] AS types
MATCH p=(a:Stop)-[:BEGINS_AT|ENDS_AT*]-(b:Stop)
WHERE a.name = 'A' AND b.name = 'B' AND
ALL(i IN RANGE(0, LENGTH(p)-1) WHERE TYPE(RELATIONSHIPS(p)[i]) = types[i%2])
RETURN p
To get the shortest path:
WITH ['BEGINS_AT', 'ENDS_AT'] AS types
MATCH p=(a:Stop)-[:BEGINS_AT|ENDS_AT*]-(b:Stop)
WHERE a.name = 'A' AND b.name = 'B' AND
ALL(i IN RANGE(0, LENGTH(p)-1) WHERE TYPE(RELATIONSHIPS(p)[i]) = types[i%2])
RETURN p
ORDER BY LENGTH(p)
LIMIT 1;
or
WITH ['BEGINS_AT', 'ENDS_AT'] AS types
MATCH p=shortestpath((a:Stop)-[:BEGINS_AT|ENDS_AT*]-(b:Stop))
WHERE a.name = 'A' AND b.name = 'B' AND
ALL(i IN RANGE(0, LENGTH(p)-1) WHERE TYPE(RELATIONSHIPS(p)[i]) = types[i%2])
RETURN p
If you want to calculate the weighted shortest path, then it is the easiest to use GDS or even APOC plugin. You could probably create a shortest weighted path function with cypher but it would be not optimized. I can only think of finding all paths between the two nodes and suming the weights. In the next step you would filter the path with the minimum sum of weight. This would not scale well though.
As for the second part of your question I would need more information as I dont know exactly what you want.

traversing neo4j graph with conditional edge types

i have a fixed database that has the nodes connecting people and edges with six different types of relationships. to make it simple, I call in this post the types of relationships A, B, C, D, E and F. None of the relationships are directional. new at the syntax so thank you for help.
I need to get sets of relationships that traverses the graph based on a conditional path A to (B or C.D) to E to F. So this means that I first need the relationship that links two nodes ()-[:A]-(), but then I am confused about how to express a conditional relationship. To get to the next node, I need either B or C then D so that it is ()-[:B]-() OR ()-[:C]-()-[:D]-(). How to express this conditional traversal in the MATCH syntax?
Tried all of these and got syntax errors:
(node2:Node)-[rel2:B|rel3:C]-(node3:Node)
(node2:Node)-[rel2:B]OR[rel3:C]-(node3:Node)
This pure-Cypher query should return all matching paths:
MATCH p=()-[:A]-()-[r:B|C|D*1..2]-()-[:E]-()-[:F]-()
WHERE (SIZE(r) = 1 AND TYPE(r[0]) = 'B') OR
(SIZE(r) = 2 AND TYPE(r[0]) = 'C' AND TYPE(r[1]) = 'D')
RETURN p
The [r:B|C|D*1..2] pattern matches 1 or 2 relationships that have the types B, C, and/or D (which can include subpaths that you don't want); and the WHERE clause filters out subpaths that you don't want.
This isn't something that can really be expressed with Cypher, when the number of hops to traverse isn't the same.
The easiest way to do this would probably be to use apoc.cypher.run() from APOC Procedures to execute a UNION query to cover both paths, then work with the result of the call:
//assume `node2` is in scope
CALL apoc.cypher.run("MATCH (node2)-[:B]-(node3:Node) RETURN node3
UNION
MATCH (node2)-[:C]-()-[:D]-(node3:Node) RETURN node3",
{node2:node2}) YIELD value
WITH value.node3 as node3 // , <whatever else you want in scope>
...

How could I use a WHERE in an EXISTS() in Cypher

I am trying to find all path in a graph between two nodes, with the condition that any node in this path must be connected to another node with a label TIMESWITCH and valid endDate and startDate.
The purpose of the TIMESWITCH nodes is to indicate that the connected node can be used only for certain time ranges.
So I used this Cypher query :
MATCH p=(:MYNODE {myid:'node1'}) -[*]- (:MYNODE {myid:'node2'})
WHERE ALL(n in nodes(p) WHERE EXISTS(
(n)<--(ts:TIMESWITCH)
WHERE ts.startDate < datetime("2018-10-01T00:00:00") <= ts.endDate ))
RETURN p
But this query is incorrect because the WHERE clause is forbidden inside an EXISTS. That simply means that inside an exists, one can test equality but not inequality.
So I don't know what to do ... how am I supposed to write that exists ?
You should be able to use a pattern comprehension as a workaround, as pattern comprehensions allow you to introduce new variables for the comprehension and filter with a WHERE clause. You'll just need to check the size of the comprehension to ensure it's not empty.
MATCH p=(:MYNODE {myid:'node1'}) -[*]- (:MYNODE {myid:'node2'})
WHERE ALL(n in nodes(p) WHERE size([(n)<--(ts:TIMESWITCH)
WHERE ts.startDate < datetime("2018-10-01T00:00:00") <= ts.endDate | ts]) > 0)
RETURN p

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.

Resources