Path properties in Cypher - graph

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.

Related

Cypher allShortestPaths just return one path?

Background statement:
I have a graph like bellow:
I want to find all the path between Node A and Node F (something like how many ways I can reach F from A), then my Cypher like this bellow:
MATCH (start:kg:test), (end:kg:test), p = allShortestPaths((start)-[*..8]-(end))
where start.value = 'A' and end.value = 'F'
RETURN start, end, p
As I expected, this query will return the whole graph, but it just returns A->F (return the same thing with using the shortestPath function), like bellow:
Problems
Why that query won't return all the different paths in the graph?
Do I misuse the allShortestPaths function?
How can I get all the path from Node A to Node F?
thanks
shortestPath() returns the single shortest path between the nodes (and if there are multiple of the same size it just returns the first that it finds).
If there are multiple paths that could have been returned by shortestPath() (they will all have the same size), then allShortesPaths() will return them.
If you just want to find all possible paths between two nodes (the length of the path doesn't matter, and you don't care about shortest paths at all), then you don't need to use either of these functions.
MATCH p=(start:kg:test)-[*..8]-(end:kg:test)
where start.value = 'A' and end.value = 'F'
RETURN start, end, p

Zero or more length of path in Cypher

For example I have a path:
1-[:A]->2-[:B]->3
And we can use the * operator to define if a particular edge can be repeated. I would like to use the * operator on the entire path, or both edges combined. I would like to follow: (A AND B) zero or more times.
Example:
1-[:A]->2-[:B]->3-[:A]->4-[:B]->5...
I am not sure how to apply the * operator for the entire path in Cypher. My intent is to express a pattern that allows a specific path to be repeated 0 or more times.
This is something variable-length patterns cannot do in Cypher. However, because of this, we added repeating sequences functionality to path expander procs in APOC Procedures.
As an example:
MATCH (n)
WHERE id(n) = 123
CALL apoc.path.expandConfig(n, {relationshipFilter:'A>, B>'}) YIELD path
RETURN path
This expands from a start node (n) expanding out only a repeating sequence of outgoing :A and :B relationships. No minLevel or maxLevel properties were provided, so this has a minimum of 0 length and no bounds on max length.

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

Cypher query to stop graph traversal when reaching a hub

I have a graph database that contains highly connected nodes (hubs). These nodes can have more than 40000 relationships.
When I want to traverse the graph starting from a node, I would like to stop traversal at these hubs not to retrieve too many nodes.
I think I should use aggregation function and conditional stop based on the count of relationship for each node, but I didn't manage to write the good cypher query.
I tried:
MATCH p=(n)-[r*..10]-(m)
WHERE n.name='MyNodeName' AND ALL (x IN nodes(p) WHERE count(x) < 10)
RETURN p;
and also:
MATCH (n)-[r*..10]-(m) WHERE n.name='MyNodeName' AND COUNT(r) < 10 RETURN p;
I think you can't stop the query at some node if you MATCH a path of length 10. You could count the number of relationships for all nodes in the path, but only after the path is matched.
You could solve this by adding an additional label to the hub nodes and filter that in your query:
MATCH (a:YourLabel)
OPTIONAL MATCH (a)-[r]-()
WITH a, count(r) as count_rels
CASE
WHEN count_rels > 20000
THEN SET a :Hub
END
Your query:
MATCH p=(n)-[r*..10]-(m)
WHERE n.name='MyNodeName' AND NONE (x IN nodes(p) WHERE x:Hub)
RETURN p
I used this approach in a similar case.
Since Neo4j 2.2 there is a cool trick to use the internal getDegree() function to determine if a node is a dense node.
You also forgot the label (and probably index) for n
For your case that would mean:
MATCH p=(n:Label)-[r*..10]-(m)
WHERE n.name='MyNodeName' AND size((m)--()) < 10
RETURN p;

Cypher - neo4j find recursive end nodes

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.

Resources