Cypher - neo4j find recursive end nodes - recursion

I'm trying to find all beginning nodes for a given node type, starting with any node in my database. These are nodes that have no relationships pointing to them.
I'm currently doing it manually, but need a recursive-type of statement to simplify and expand. Here is what I have so far, with a return statement describing what I need.
MATCH ((d1:Type1 {Name: "test1"}))
MATCH ((t1:Type2)-[:Rel1]->(h1:Type3))
MATCH ((d1)<-[ud1:Rel3]-(t1))
OPTIONAL MATCH ((h1)<-[:Rel1]-(t2:Type2))
OPTIONAL MATCH ((t2)<-[:Rel2]-(d2:Type1))
OPTIONAL MATCH ((d2)<-[ud2:Rel3]-(t3:Type2))
OPTIONAL MATCH ((t3)-[:Rel1]->(h2:Type3))
OPTIONAL MATCH ((h2)<-[:Rel1]-(t4:Type2))
OPTIONAL MATCH ((t4)<-[:Rel2]-(d3:Type1))
OPTIONAL MATCH ((d3)<-[ud3:Rel3]-(t5:Type2))
RETURN DISTINCT Type1.Name where there is no Rel3 relationship
The ask here is to navigate the recursive
Type1 < - Type2 -> Type3 <- Type2 < - Type1 < - Type2 -> Type3 <- Type2
path, until there are no Type2s pointing to the Type1, and return the names of these Type1s.

Your query example is quite complex and contains stand-in types that make it a bit hard to understand.
I think this boils down to something quite simple, you should try this:
MATCH (startingPoint:Type1 { name: "test1" })
MATCH (startingPoint)-[:relType*]->(headNode)
OPTIONAL MATCH (headNode)-[f:relType]->()
WHERE f is null;
So all this does is match from a startingPoint through any number of relationships to a headNode. How do we know that the headNode is really at the beginning? Because we insist with the OPTIONAL MATCH that it cannot be connected to anything else further upstream. We attempt to match what would be another upstream match, and then insist with the WHERE clause that it must be missing, hence headNode is really "at the top".
Customize this with your own relTypes and labels, and you should be able to follow this pattern.
More broadly, when you're asking about a beginning node and it not having some relationships, See also this related question.

FrobberOfBits has a great answer, but doesn't address the need to navigate through mutiple nodes types recursively. I've still yet to see a syntax that allows for this type of recursive querying in Cypher.
The work around is to simply create a new relationship using the complex traversal, then use FrobberOfBits solution to find the headnode.
Here is what I came up with:
MATCH ((d1:Type1))
MATCH ((q1:Type2)-[:Rel1]->(p1:Type3))
MATCH ((d1)<-[ud1:Rel2]-(q1))
MATCH ((p1)<-[:Rel1]-(q2:Type2))
MATCH ((q2)<-[:Rel3]-(d2:Type1))
WITH distinct d1 as child,d2 as parent
CREATE (child)<-[:ParentOf]-(parent)
This creates a new relationship type between two Type1s, removing the need to navigate through the different node types recursively, and simply allowing tree-traversal on a single node type (As FrobberOfBits masterfully explained)
Here is my resulting query using the types in the question and the above create statement, with an alternate syntax to Fibber's.
MATCH((d1:Type1 {Name: "test1"}))
OPTIONAL MATCH ((d1)<-[r1:Rel1*]-(d2:Type1))
WHERE NOT (d2)<--()
return distinct d2.Name
Sorry for the poor type masking! I should have been more creative with my aliasing.

Related

How to query hops using PGQL?

We are trying to write PGQL query to get multiple hop(s) for selected node.
To get nodes and edge for the selected node,
SELECT n0.id as n0id, e0.id as e0id, n1.id as n1id FROM MATCH (n0)->[e0]->(n1) WHERE n0.id=12345
To increase nodes and edges result, example 2 hops,
... FROM MATCH (n0)->[e0]->(n1)->[e1]->(n3) ...
However in this case, nodes with 1 hop will not be return.
I wonder if there is any way query required hop(s) for selected node?
Any solution would be appreciated.
It seems you are looking for the PGQL syntax for variable-length paths:
https://pgql-lang.org/spec/1.4/#variable-length-paths
Among these patterns, I suppose the reachability syntax is useful in your case.
SELECT n0.id as n0id, e0.id as e0id, n1.id as n1id
FROM MATCH (n0)->/:edge-label{1,3}/->(n1)
WHERE n0.id=12345
https://pgql-lang.org/spec/1.4/#between-n-and-m
Thanks!

Recursive multiple relationships

I'm attempting to recursively perform alternate match statements with 2 specific relationships.
For example, Pets are owned by a Person. Pets LIKE other people (not owner) Those people have pets owned by them, who like other people etc.
match (n.Person {id.123})<-[r.OwnedBy]-(p.Pet) Return n, r, p
match (p.Pet {id.123})-[r.Likes]->(n.Person) Return p, r, n
Notice the directional relationships involved - #1 is backwards, #2 is forwards.
What I want to do is to, given a person(id),
1. Display pets [OwnedBy] this person(id)
2. Display people [Liked] by those pets
3. Display pets [OwnedBy] the people in 2.
etc. recursively
Independently, these Match statements work. together, they do not.
I tried adding the 2nd match statement, using different variables, then it will go down 2 levels and stop.
In the real data set, there are dozens of nodes and relationships. I'm trying to limit the display to a 'tree' view of only these 2 relationships/nodes.
Thanks!
How about this?
match (n:Person {id:123})<-[:OwnedBy]-(p:Pet)-[:Likes]->(n2:Person)<-[:OwnedBy]-(p2:Pet)
return n, collect(distinct p) as pets, collect(distinct n2) as peopleLiked, collect(distinct p2) as petsOfPeopleLiked
Though if you're only interested in the graph display, this should work:
match path = (n:Person {id:123})<-[:OwnedBy]-(p:Pet)-[:Likes]->(n2:Person)<-[:OwnedBy]-(p2:Pet)
return path, n, p, n2, p2
You can also utilize APOC Procedures. This can handle showing these paths, using only these two types of relationships:
match (n:Person {id:123})
call apoc.path.expandConfig(n, {relationshipFilter:'<OwnedBy|Likes>'}) yield path
return path

Find path for N levels with repeating pattern of directional relationships in Neo4J

I'm trying to use Neo4j to analyze relationships in a family tree. I've modeled it like so:
(p1:Person)-[:CHILD]->(f:Family)<-[:FATHER|MOTHER]-(p2)
I know I could have left out the family label and just had children connected to each parent, but that's not practical for my purposes. Here's an example of my graph and the black line is the path I want it to generate:
I can query for it with
MATCH p=(n {personID:3})-[:CHILD]->()<-[:FATHER|MOTHER]-()-[:CHILD]->()<-[:FATHER|MOTHER]-()-[:CHILD]->()<-[:FATHER|MOTHER]-() RETURN p
but there's a repeating pattern to the relationships. Could I do something like:
MATCH p=(n {personID:3})(-[:CHILD]->()<-[:FATHER|MOTHER]-())* RETURN p
where the * means repeat the :CHILD then :FATHER|MOTHER relationships, with the directions being different? Obviously if the relationships were all the same direction, I could use
-[:CHILD|FATHER|MOTHER*]->
I want to be able to query it from Person #3 all the way to the top of the graph like a pedigree chart, but also be specific about how many levels if needed (like 3 generations, as opposed to end-of-line).
Another issue I'm having with this, is if I don't put directions on the relationships like -[:CHILD|FATHER|MOTHER*]-, then it will start at Person #3, and go both in the direction I want (alternating arrows), but also descend back down the chain finding all the other "cousins, aunts, uncles, etc.".
Any seasoned Cypher experts that an help me?
I am just on the same problem. And I found out that the APOC Expand path procedures are just accomplishing what you/we want.
Applied to your example, you could use apoc.path.subgraphNodes to get all ancestors of Person #3:
MATCH (p1:Person {personId:3})
CALL apoc.path.subgraphNodes(p1, {
sequence: '>Person,CHILD>,Family,<MOTHER|<FATHER'
}) YIELD node
RETURN node
Or if you want only ancestors up to the 3 generations from start person, add maxLevel: 6 to config (as one generation is defined by 2 relationships, 3 generations are 6 levels):
MATCH (p1:Person {personId:3})
CALL apoc.path.subgraphNodes(p1, {
sequence: '>Person,CHILD>,Family,<MOTHER|<FATHER',
maxLevel: 6
}) YIELD node
RETURN node
And if you want only ancestors of 3rd generation, i.e. only great-grandparents, you can also specify minLevel (using apoc.path.expandConfig):
MATCH (p1:Person {personId:3})
CALL apoc.path.expandConfig(p1, {
sequence: '>Person,CHILD>,Family,<MOTHER|<FATHER',
minLevel: 6,
maxLevel: 6
}) YIELD path
WITH last(nodes(path)) AS person
RETURN person
You could reverse the directionality of the CHILD relationships in your model, as in:
(p1:Person)<-[:CHILD]-(f:Family)<-[:FATHER|MOTHER]-(p2)
This way, you can use a simple -[:CHILD|FATHER|MOTHER*]-> pattern in your queries.
Reversing the directionality is actually intuitive as well, since you can then more naturally visualize the graph as a family tree, with all the arrows flowing "downwards" from ancestors to descendants.
Yeah, that's an interesting case. I'm pretty sure (though I'm open to correction) that this is just not possible. Would it be possible for you to have and maintain both? You could have a simple cypher query create the extra relationships:
MATCH (parent)-[:MOTHER|FATHER]->()<-[:CHILD]-(child)
CREATE (child)-[:CHILD_OF]->parent
Ok, so here's a thought:
MATCH path=(child:Person {personID: 3})-[:CHILD|FATHER|MOTHER*]-(ancestor:Person),
WHERE ancestor-[:MOTHER|FATHER]->()
RETURN path
Normally I'd use a second clause in the MATCH like this:
MATCH
path=(child:Person {personID: 3})-[:CHILD|FATHER|MOTHER*]-(ancestor:Person),
ancestor-[:MOTHER|FATHER]->()
RETURN path
But Neo4j (at least by default, I think) doesn't traverse back through the path. Maybe comma-separating would be fine and this would be a problem:
MATCH path=(child:Person {personID: 3})-[:CHILD|FATHER|MOTHER]-(ancestor:Person)-[:MOTHER|FATHER]->()
I'm curious to know what you find!

Path properties in Cypher

I have a following graph in Neo4j
(id:5,t:e)<--(id:4,t:w)<--(id:0;t:s)-->(id:1,t:w)-->(id:2,t:b)-->(id:3,t:e)
now I search paths from nodes with t:s to nodes with t:e such that only white-listed nodes with t:w are in-between.
So ideally i need a query to return only (0)-->(4)-->(5) but not (0)-->(1)-->(2)-->(3).
EDIT: i have forgotten to mention that paths may have variable length: from 0 to potentially infinity. It means that I may have an arbitrary number of "t:w" nodes
Best regards
Working just with the information that you have provided above you could use
MATCH p=({t:'s'})-->({t:'w'})-->({t:'e'}) RETURN p
Of course if an s could link directly to an e you will need to use variable length relationships matches.
MATCH p=({t:'s'})-[*0..1]->({t:'w'})-[]->({t:'e'})
RETURN DISTINCT p
EDIT - Paths of any length
MATCH p=({t:'s'})-[*0..1]->({t:'w'})-[*]->({t:'e'})
RETURN DISTINCT p
To match a path of any length use the * operator in the relationship path match. It is usually best to put some bounds on that match, an example of which is the *0..1 (length 0 to 1). You can leave either end open *..6 (length 1 to 6) or *2.. (length 2 to whatever).
The problem with this is that now you cannot guarantee the node types in the intervening nodes (so t:"b" will be matched). To avoid that I think you'll have to filter.
MATCH p=({t:'s'})-[*]->({t:'e'})
WHERE ALL (node IN NODES(p)
WHERE node.t = 's' OR node.t = 'w' OR node.t = 'e' )
RETURN p
End Edit
You should introduce labels to your nodes and use relationship types for traversal though as that is where Neo/Cypher is going to be able to help you out. You should also make sure that if you are matching on properties that they are indexed correctly.

neo4j logic gate simulation, how to?

I would like to create a bunch of "and" and "or" and "not" gates in a directed graph.
And then traverse from the inputs to see what they results are.
I assume there is a ready made traversal that would do that but I don't see it.
I don't know what the name of such a traversal would be.
Certainly breadth first will not do the job.
I need to get ALL the leaves, and go up toward the root.
In other words
A = (B & (C & Z))
I need to resolve C # Z first.
I need to put this type of thing in a graph and to traverse up.
You would probably create each of the operations as a node which has N incoming and one outgoing connection. You can of course also have more complex operations encapsuled as a node.
With Neo4j 2.0 I would use Labels for the 3 types of operations.
I assume your leaves would then be boolean values? Actually I think you have many roots and just a single leaf (the result expression)
(input1)-->(:AND {id:1})-->(:OR {id:2})-->(output)
(input2)-->(:AND {id:1})
(input3)------------------>(:OR {id:2})
Then you can use CASE when for decisions on the label type and use the collection predicates (ALL, ANY) for the computation
See: http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
Predicates: http://docs.neo4j.org/chunked/milestone/query-function.html
Labels: http://docs.neo4j.org/chunked/milestone/query-match.html#match-get-all-nodes-with-a-label

Resources