Cypher query to check node connectivity - neo4j

I'm new to neo4j and would really appreciate your help for this.
I have following graph created in neo4j.
n1----n2----n3----n4---n5
n1,n2,n3,n4,n5 all are nodes
--- : relationship_type_1 (REL)
Now given any set of nodes (in any order), I want to check whether these nodes are connected or not.
E.g. Given n1, n2, n3 ==> Connected.
Given n1, n3, n2, n4, n5 ==> Connected.
How should I formulate my cypher query to check connectivity?
Following query is working even if I change the order,
MATCH p=_1--_2--_3
WHERE _1.name?="Node1" and ALL (n in nodes(p)
WHERE n.name IN ["Node1", "Node2", "Node4"])
RETURN nodes(p);
on http://console.neo4j.org/?id=xl8pnl
but if I provide start nodes instead of using _1,_2 and change the order then it's not returning me the path. :(
http://console.neo4j.org/?id=xl8pnl
for following query,
START p1=node(6),p2=node(5),p3=node(4) MATCH p=p1--p2--p3
WHERE p1.name?="Node1" AND ALL (n IN nodes(p) WHERE n.name IN ['Node1' ,'Node3', 'Node2' , 'Node4'])
RETURN nodes(p)
it doesn't return the path as nodes are connected in node(6)-node(5)-node(4) order.

You need to list the different patterns that constitute the "connectedness" to you for instant by listing them explicitly like
console.neo4j.org/r/2w3poz
START p1=node(0),p2=node(4),p3=node(5)
RETURN
CASE
WHEN p1--p2--p3 OR p2--p1--p3 OR p1--p3--p2 OR p3--p2--p1 OR p3--p1--p2
THEN 'Connected'
ELSE 'Not connected'
END

Case 1:
2 paths found
MATCH p=_1--_2--_3--_4
WHERE _1.name?="Node1" and
ALL (n in nodes(p)
WHERE n.name IN ["Node1", "Node2", "Node3", "Node4"])
RETURN nodes(p);
Check it out here:
http://console.neo4j.org/?id=nn9yl6
in this case order dosen't matter
Case 2
(no path found)
Use node 1,2,4
MATCH p=_1--_2--_3
WHERE _1.name?="Node1" and ALL (n in nodes(p)
WHERE n.name IN ["Node1", "Node2", "Node4"])
RETURN nodes(p);
Check it out here:
http://console.neo4j.org/?id=xl8pnl

Related

How Many Nodes Are Involved in a Match

How can I know how many nodes and edges are involved in a MATCH? Is there another way besides Explain / Profile Match?
If you mean how many nodes are matched in a path, such as a variable-length path, then you can assign a path variable for this:
MATCH p = (k:Person {name:'Keanu Reeves'})-[*..8]-(t:Person {name:'Tom Hanks'})
WITH p LIMIT 1
RETURN p, length(p) as pathLength, length(p) + 1 as numberOfNodesInPath
You can also use nodes(p) and relationships(p) to get the collection of nodes and relationships that make up the path, and you can use size() on those collections to get their size.
There exists the COUNT() function of Cypher that allows you to count the number of elements. As for example in this query:
MATCH (n)
RETURN COUNT(n);
This query will count all nodes in your database.
You can find more information in the cypher manual, under the aggregating functions. Check it out.
The following Cypher snippet should return the number of distinct nodes and relationships found by any given MATCH clause. Just replace <your code here> with your MATCH pattern.
MATCH <your code here>
WITH COLLECT(NODES(p)) AS ns, SUM(SIZE(RELATIONSHIPS(p))) AS relCount
UNWIND ns AS nodeList
UNWIND nodeList AS node
RETURN COUNT(DISTINCT node) AS nodeCount, relCount;

Match all nodes and return nodes + relationships

In the latest version of Cypher, I can use this query to get all nodes with relationships:
MATCH (n)-[r]-(m) RETURN n,r,m
However, I'm missing nodes without any relationships.
In trying to query the missing nodes, this attempt gives me the error: Variable 'r' not defined
MATCH (n) WHERE NOT (n)-[r]->() RETURN n
And, this attempt shows zero results:
MATCH (n)-[r]->() WHERE r is null RETURN n
I can see the stragglers with:
MATCH (n) RETURN n
But, then I'm missing the relationships.
How do I phrase my query to find all nodes and all relationships without duplicates?
You can try the OPTIONAL MATCH:
MATCH (n)
OPTIONAL MATCH (n)-[r]-(m)
RETURN n, r, m

Neo4j more efficient Cypher query for finding connected nodes with a relationship

I have a patch where I know a starting and end node with an unknown number of nodes between the 2. I wish to collect a collection of nodes and each nodes exiting relationship in the chain.
PROFILE MATCH p = (:Question{id:'1234'})-[:Answer*0..3]->(t)-
[:Answer]->(:Question{id:'5678'})
WHERE t:Set OR t:Read
OPTIONAL MATCH (x)-[v:Answer]->(y)
WHERE x.id <> '1234' and x IN nodes(p) AND v IN rels(p)
return x,v
This query is pretty inefficient as the OPTIONAL MATCH (x)-[v:Answer]->(y) requires a full nodes scan.
I know that t and as a result x will be of types Set or Read which would reduce the scan quite a bit but don't think there is a way to query this.
Is there any way to optimise this?
You can simply unwind the path you have already got:
MATCH p = (:Question{id:'1234'})-[:Answer*0..3]->(t)-[:Answer]->(:Question{id:'5678'})
WHERE t:Set OR t:Read
WITH nodes(p) AS nds,
rels(p) AS rls
UNWIND range(1, length(nds)-2) AS i
RETURN nds[i] AS x,
rls[i] AS v

cypher NOT IN query with Optional Match

NOT RELEVANT - SKIP TO Important Edit.
I have the following query:
MATCH (n)
WHERE (n:person) AND n.id in ['af97ab48544b'] // id is our system identifier
OPTIONAL MATCH (n)-[r:friend|connected|owner]-(m)
WHERE (m:person OR m:dog OR m:cat)
RETURN n,r,m
This query returns all the persons, dogs and cats that have a relationship with a specific person. I would like to turn it over to receive all the nodes & relationships that NOT includes in this query results.
If it was SQL it would be
select * from graph where id NOT IN (my_query)
I think that the OPTIONAL MATCH is the problematic part. I How can I do it?
Any advice?
Thanks.
-- Important Edit --
Hey guys, sorry for changing my question but my requirements has been changed. I need to get the entire graph (all nodes and relationships) connected and disconnected except specific nodes by ids. The following query is working but only for single id, in case of more ids it isn't working.
MATCH (n) WHERE (n:person)
OPTIONAL MATCH (n)-[r:friend|connected|owner]-(m) WHERE (m:person OR m:dog OR m:cat)
WITH n,r,m
MATCH (excludeNode) WHERE excludeNode.id IN ['af97ab48544b']
WITH n,r,m,excludeNode WHERE NOT n.id = excludeNode.id AND (NOT m.id = excludeNode.id OR m is null)
RETURN n,m,r
Alternatively I tried simpler query:
MATCH (n) WHERE (n:person) AND NOT n.id IN ['af97ab48544b'] return n
But this one does not returns the relationships (remember I need disconnected nodes also).
How can I get the entire graph exclude specific nodes? That includes nodes and relationships, connected nodes and disconnected as well.
try this:
match (n) where not n.id = 'id to remove' optional match (n)-[r]-(m)
where not n.id in ['id to remove'] and not m.id in ['id to remove']
return n,r,m
You've gotta switch the 'perspective' of your query... start by looping over every node, then prune the ones that connect to your person.
MATCH (bad:person) WHERE bad.id IN ['af97ab48544b']
WITH COLLECT(bad) AS bads
MATCH path = (n:person) - [r:friend|:connected|:owner] -> (m)
WHERE n._id = '' AND (m:person OR m:cat OR m:dog) AND NOT ANY(bad IN bads WHERE bad IN NODES(path))
RETURN path
That said, this is a problem much more suited to SQL than to a graph. Any time you have to loop over every node with a label, you're in relational territory, the graph will be less efficient.

How to eliminate a path where exists a relationship outside of the path, but between nodes in the path?

I have modified the 'vanilla' initial query in this console, and added one relationship type 'LOCKED' between the 'Morpheus' and 'Cypher' nodes.
How can I modify the existing (first-run) query, which is a variable length path so that it no longer reaches the Agent Smith node due to the additional Locked relationship I've added?
First-run query:
MATCH (n:Crew)-[r:KNOWS|LOVES*2..4]->m
WHERE n.name='Neo'
RETURN n AS Neo,r,m
I have tried this kind of thing:
MATCH p=(n:Crew)-[r:KNOWS|LOVES*2..4]->m
WHERE n.name='Neo'
AND none(rel IN rels(p) WHERE EXISTS (StartNode(rel)-[:LOCKED]->EndNode(rel)))
RETURN n AS Neo,r,m
..but it doesn't recognize the pattern inside the none() function.
I'm using Community 2.2.1
Thanks for reading
I'm pretty sure you can't use a function in a MATCHy type clause like that (though it's clever). What about this?
MATCH path=(neo:Crew)-[r:KNOWS|LOVES|LOCKED*2..4]->m
WHERE neo.name='Neo'
AND NOT('LOCKED' IN rels(path))
RETURN neo,r,m
EDIT:
Oops, looks like Dave might have beat me to the punch. Here's the solution I came up with anyway ;)
MATCH p=(neo:Crew)-[r:KNOWS|LOVES*2..4]->m
WHERE neo.name='Neo'
WITH p, neo, m
UNWIND rels(p) AS rel
MATCH (a)-[rel]->(b)
OPTIONAL MATCH a-[locked_rel:LOCKED]->b
WITH neo, m, collect(locked_rel) AS locked_rels
WHERE none(locked_rel IN locked_rels WHERE ()-[locked_rel]->())
RETURN neo, m
Ok, this is a little convoluted but i think it works. The approach is to take all of the paths and find the last known good nodes (ones that have LOCKED relationships leaving them). Then use that node(s) as a new ending point(s) and return the paths.
match p=(n:Crew)-[r:KNOWS|LOVES|LOCKED*2..4]->m
where n.name='Neo'
with n, relationships(p) as rels
unwind rels as r
with n
, case
when type(r) = 'LOCKED' then startNode(r)
else null
end as last_good_node
with n
, (collect( distinct last_good_node)) as last_good_nodes
unwind last_good_nodes as g
match p=n-[r:KNOWS|LOVES*]->g
return p
I think this would be simpler if there was a locked: true property on the KNOWS and LOVES relationships.

Resources