Cypher: variable length path with condition on each node - path

If I want to find a path of varibale length in my graph from node A to node B where all nodes in between have a certain label, and A and B have a different label -- how would I do that?
MATCH (A)-[*0..]->(B) WHERE ??

This query should work if all nodes have only a single label:
MATCH p=(a)-[*]->(b)
WITH p, LABELS(a) AS la, LABELS(b) AS lb, NODES(p)[1..-1] AS nodes
WITH p, la, lb, nodes, LABELS(nodes[0]) AS label
WHERE label <> la AND label <> lb AND ALL(x IN nodes[1..] WHERE LABELS(x) = label)
RETURN p;
The query requires all the middle nodes to have the same label, and the end nodes to have a different label (or labels) than the middle nodes.

Related

How to find the count of distinct nodes between a single node of particular label to all other nodes in a different label in neo4j?

I have 4 different types of labels and multiple nodes in each. Let's say Node A of label P is connected to multiple nodes of label Q and those nodes are connected to multiple nodes of label R. Now I'm trying to figure out the count of distinct nodes in label R that is connected to Node A either directly or through label Q.
This should work:
MATCH path = (a:P)-[*1..2]->(r:R)
WHERE a.id = 'A' AND (LENGTH(path) = 1 OR 'Q' IN LABELS(NODES(path)[1]))
RETURN DISTINCT r
This simple example assumes a consistent "forward" relationship directionality for both relationships.
[ADDENDUM]
This should be a better query than that one asked about in the comments:
MATCH path = allShortestPaths((a:P)-[*1..2]->(r:R))
WHERE a.id = $resource_key AND (LENGTH(path) = 1 OR 'Q' IN LABELS(NODES(path)[1]))
RETURN COUNT(DISTINCT r)

cypher query to return or keep only the final sequence when variable length relationship identifiers are used

Is there a way to keep or return only the final full sequences of nodes instead of all subpaths when variable length identifiers are used in order to do further operations on each of the final full sequence path.
MATCH path = (S:Person)-[rels:NEXT*]->(E:Person)................
eg: find all sequences of nodes with their names in the given list , say ['graph','server','db'] with same 'seqid' property exists in the relationship in between.
i.e.
(graph)->(server)-(db) with same seqid :1
(graph)->(db)->(server) with same seqid :1 //there can be another matching
sequence with same seqid
(graph)->(db)->(server) with same seqid :2
Is there a way to keep only the final sequence of nodes say ' (graph)->(server)->(db)' for each sequences instead of each of the subpath of a large sequence like (graph)->(server) or (server)->(db)
pls help me to solve this.........
(I am using neo4j 2.3.6 community edition via java api in embedded mode..)
What we could really use here is a longestSequences() function that would do exactly what you want it to do, expand the pattern such that a and b would always be matched to start and end points in the sequence such that the pattern is not a subset of any other matched pattern.
I created a feature request on neo4j for exactly this: https://github.com/neo4j/neo4j/issues/7760
And until that gets implemented, we'll have to make do with some alternate approach. I think what we'll have to do is add additional matching to restrict a and b to start and end nodes of full sequences.
Here's my proposed query:
WITH ['graph', 'server' ,'db'] as names
MATCH p=(a)-[rels:NEXT*]->(b)
WHERE ALL(n in nodes(p) WHERE n.name in names)
AND ALL( r in rels WHERE rels[0]['seqid'] = r.seqid )
WITH names, p, a, rels, b
// check if b is a subsequence node instead of an end node
OPTIONAL MATCH (b)-[rel:NEXT]->(c)
WHERE c.name in names
AND rel.seqid = rels[0]['seqid']
// remove any existing matches where b is a subsequence node
WITH names, p, a, rels, b, c
WHERE c IS NULL
WITH names, p, a, rels, b
// check if a is a subsequence node instead of a start node
OPTIONAL MATCH (d)-[rel:NEXT]->(a)
WHERE d.name in names
AND rel.seqid = rels[0]['seqid']
// remove any existing matches where a is a subsequence node
WITH p, a, b, d
WHERE d IS NULL
RETURN p, a as startNode, b as endNode
MATCH (S:Person)-[r:NEXT]->(:Person)
// Possible starting node
WHERE NOT ( (:Person)-[:NEXT {seqid: r.seqid}]->(S) )
WITH S,
// Collect all possible values of `seqid`
collect (distinct r.seqid) as seqids
UNWIND seqids as seqid
// Possible terminal node
MATCH (:Person)-[r:NEXT {seqid: seqid}]->(E:Person)
WHERE NOT ( (E)-[:NEXT {seqid: seqid}]->(:Person) )
WITH S,
seqid,
collect(distinct E) as ES
UNWIND ES as E
MATCH path = (S)-[rels:NEXT* {seqid: seqid}]->(E)
RETURN S,
seqid,
path
[EDITED]
This query might do what you want:
MATCH (p1:Person)-[rel:NEXT]->(:Person)
WHERE NOT (:Person)-[:NEXT {seqid: rel.seqid}]->(p1)
WITH DISTINCT p1, rel.seqid AS seqid
MATCH path = (p1)-[:NEXT* {seqid: seqid}]->(p2:Person)
WHERE NOT (p2)-[:NEXT {seqid: seqid}]->(:Person)
RETURN path;
It first identifies all Person nodes (p1) with at least one outgoing NEXT relationship that have no incoming NEXT relationships (with the same seqid), and their distinct outgoing seqid values. Then it finds all "complete" paths (i.e., paths whose start and end nodes have no incoming or outgoing NEXT relationships with the desired seqid, respectively) starting at each p1 node and having relationships all sharing the same seqid. Finally, it returns each complete path.
If you just want to get the name property of all the Person nodes in each path, try this query (with a different RETURN clause):
MATCH (p1:Person)-[rel:NEXT]->(:Person)
WHERE NOT (:Person)-[:NEXT {seqid: rel.seqid}]->(p1)
WITH DISTINCT p1, rel.seqid AS seqid
MATCH path = (p1)-[:NEXT* {seqid: seqid}]->(p2:Person)
WHERE NOT (p2)-[:NEXT {seqid: seqid}]->(:Person)
RETURN EXTRACT(n IN NODES(path) | n.name);

Find connected groups over levels

A node A has 3 connected Nodes B1, B2, B3. Those Bx Nodes have again connected Nodes C1,C2,C3 and C4. Also Node A have 2 connected nodes C5 and C6.
Starting with node A I want to collect all C-nodes. I did a query for the A node, collect the two C-Nodes, then a query for the B-nodes, collect again all C-nodes and merge both arrays. Work but is not very clever.
I tried (Pseudocode)
MATCH (g)<-[:IS_SUBGROUP_OF*1]-(i)-[:HAS_C_NODES]->(c) WHERE g = A.uuid RETURN C_NODES
But I get either all c-nodes for A or for the B-nodes
How would I do a query that collects all C-Nodes starting with Node A?
* edited *
Here is some example data:
CREATE (a:A), (b1:B1), (b2:B2), (b3:B3), (c1:C1), (c2:C2), (c3:C3), (c4:C4), (a)-[r:HAS]->(c4), (a)-[r1:HAS]->(b1), (a)-[r2:HAS]->(b2), (a)-[r3:HAS]->(b3), (b1)-[r4:HAS]->(c1), (b1)-[r5:HAS]->(c2), (b2)-[r6:HAS]->(c3)
A query should return all nodes starting with C, no matter to which node they are connected (A or B).
You can add multiple labels for each node. You should use this to your advantage and segregate all the B and C nodes into a second label.
Eg:
CREATE (a:A), (b1:B1:BType), (b2:B2:BType), (b3:B3:BType), (c1:C1:CType), (c2:C2:CType), (c3:C3:CType), (c4:C4:CType), (a)-[r:HAS]->(c4), (a)-[r1:HAS]->(b1), (a)-[r2:HAS]->(b2), (a)-[r3:HAS]->(b3), (b1)-[r4:HAS]->(c1), (b1)-[r5:HAS]->(c2), (b2)-[r6:HAS]->(c3)
I have modified your create statement to group all the B nodes as :BType label and all the C nodes as :CType label.
You can simply use the optional match keyword to selectively traverse through the relationships if they exist and obtain the results you want.
match (a:A)-[:HAS]->(b:BType)-[:HAS]->(c:CType) optional match (a:A)-[:HAS]->(xc:CType) return c,xc
If you would like both sets of nodes to be grouped together you could try this statement instead which uses collect().
match (a:A)-[:HAS]->(b:BType)-[:HAS]->(c:CType) with a,collect (distinct c) as set1 optional match (a:A)-[:HAS]->(xc:CType) return set1 + collect (distinct xc) as output

Neo4j - Paths having all nodes with specific label (Operations on labels)

I want to retrieve paths between nodes with constraint on node label. All nodes in the path should hold a specific label. "amps" is my label and
My query:
MATCH p=(a:amps{word:"review"})-->()-->()-->(b:amps{word:"nothing"})
RETURN p
In the above query, I have two intermediate nodes. Now, I want these two nodes should also hold the label "amps".
How to make comparisons on labels?
If ALL the nodes in the path should have the amps label and with you current query you can do this in two ways :
1) Specify the labels in the () :
MATCH p=(a:amps{word:"review"})-->(:amps)-->(:amps)-->(b:amps{word:"nothing"})
RETURN p
2) Specify it with the ALL predicate :
MATCH p=(a:amps{word:"review"})-->()-->()-->(b:amps{word:"nothing"})
WHERE ALL ( n IN nodes(p) WHERE "amps" IN labels(n) )
RETURN p

neo4j/cypher: Find all nodes adjacent to all nodes in some set

Given a node, s, adjacent to a set, C, of >1 nodes of some label. I want to find all nodes that are also adjacent to every element in C.
More generally:
how does one define sets or groups in cypher?
how does one find nodes that are adjacent to all the nodes in a set?
Is this even doable in cypher?
Yes, absolutely, this is doable in cypher. Here is an example.
// start with finding the start node 's' and the adjacent nodes
match (s:Node {name: 's'} )-[:ADJ]->(C:Node)
// match only the adjacent nodes that have 'set C'
where C.set = 'C'
// pass C onto the remainder of the query
with s,C
// match the nodes that are adjacent to the nodes in 'set C'
match C-[:ADJ]->(adjacent)
// return all of the nodes in set C and a collection of the nodes
// adjacent to the set C nodes
return s.name, C.name, collect(adjacent.name)

Resources