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

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)

Related

Neo4J: How can I find if a path traversing multiple nodes given in a list exist?

I have a graph of nodes with a relationship NEXT with 2 properties sequence (s) and position (p). For example:
N1-[NEXT{s:1, p:2}]-> N2-[NEXT{s:1, p:3}]-> N3-[NEXT{s:1, p:4}]-> N4
A node N might have multiple outgoing Next relationships with different property values.
Given a list of node names, e.g. [N2,N3,N4] representing a sequential path, I want to check if the graph contains the nodes and that the nodes are connected with relationship Next in order.
For example, if the list contains [N2,N3,N4], then check if there is a relationship Next between nodes N2,N3 and between N3,N4.
In addition, I want to make sure that the nodes are part of the same sequence, thus the property s is the same for each relationship Next. To ensure that the order maintained, I need to verify if the property p is incremental. Meaning, the value of p in the relationship between N2 -> N3 is 3 and the value p between N3->N4 is (3+1) = 4 and so on.
I tried using APOC to retrieve the possible paths from an initial node N using python (library: neo4jrestclient) and then process the paths manually to check if a sequence exists using the following query:
q = "MATCH (n:Node) WHERE n.name = 'N' CALL apoc.path.expandConfig(n {relationshipFilter:'NEXT>', maxLevel:4}) YIELD path RETURN path"
results = db.query(q,data_contents=True)
However, running the query took some time that I eventually stopped the query. Any ideas?
This one is a bit tough.
First, pre-match to the nodes in the path. We can use the collected nodes here to be a whitelist for nodes in the path
Assuming the start node is included in the list, a query might go like:
UNWIND $names as name
MATCH (n:Node {name:name})
WITH collect(n) as nodes
WITH nodes, nodes[0] as start, tail(nodes) as tail, size(nodes)-1 as depth
CALL apoc.path.expandConfig(start, {whitelistNodes:nodes, minLevel:depth, maxLevel:depth, relationshipFilter:'NEXT>'}) YIELD path
WHERE all(index in range(0, size(nodes)-1) WHERE nodes[index] = nodes(path)[index])
// we now have only paths with the given nodes in order
WITH path, relationships(path)[0].s as sequence
WHERE all(rel in tail(relationships(path)) WHERE rel.s = sequence)
// now each path only has relationships of common sequence
WITH path, apoc.coll.pairsMin([rel in relationships(path) | rel.p]) as pairs
WHERE all(pair in pairs WHERE pair[0] + 1 = pair[1])
RETURN path

Neo4j: Return only root nodes from possible child paths

I have a set of (n) values which all have corresponding nodes in my graph. I start with unknown relationships to each other. (see start nodes in blue)
I want to find, as simply as possible, is if any of the value/nodes are children of any of the others then applying these rules to filter the results:
If the node is a child then discard it. (white nodes)
If the node is a root then return it. (green nodes)
If the node does not have any children also return it. (green node 673)
There can be up to 50 starting nodes. I've tried iterating through them comparing two at a time discarding them if they are a child - but the number of iterations quickly gets out of hand in larger sets. I'm hoping there is some graph magic I've overlooked. Cypher please!
Thanks!
Let's say that you have an input parameter nids - set of values for the id property of node, target nodes have the label Node, the relationship between nodes is of type hasChild.
Then you need to find such nodes corresponding to the input set, and which do not have parents from the nodes corresponding to the input set:
UNWIND {nids} as nid
MATCH (N:Node {id: nid})
OPTIONAL MATCH (N:Node {id: nid})<-[:hasChild]-(P:Node) WHERE P.id IN {nids}
WITH N, collect(P) AS ts WHERE size(ts) = 0
RETURN N
And do not forget to add an index to the id property for the node:
CREATE INDEX ON :Node(id)

Neo4j get nodes which do not have another similarly labelled node in it's path to the ancestor

I have a tree-like structure in neo4j and I want to find the first nodes with a specific label which has a path to the ancestor and does not have a node of the same type within that path.
For example:
/ (:Y) <- (a:X)
A <- (:Y) <- (c:X) <- (:Y) <- (d:X) <- (e:X)
\ (b:X)<- (f:X)
\ (g:X)
In this case 'A' is the ancestor, and I want the nodes a,b,c,g because they have the label 'X' and are not separated from the ancestor by another node with the label 'X'
I started with the query:
match (c :X)-[:X*1]->(A) return c
but it only returns [b,g] and not [c,g] because there are other labelled nodes between the ancestor and 'X' labelled nodes
You're going to want APOC Procedures for this, there are configuration options in some of their path expander procedures that do exactly what you want.
You'll need to use the labelFilter using the termination operator /, which means path expansion will stop at the first node of the given label, and that only nodes of those labels will be returned.
Something like:
...
// assume you've already matched to ancester 'a'
CALL apoc.path.subgraphNodes(a, {labelFilter:'/X'}) YIELD node
RETURN node
That should return the first :X nodes along each path from the starting node.
First, get your ancestor node (I pretend it's going to have a :Ancestor label in this case, you might want to adjust the query to reflect reality).
MATCH (a:Ancestor), then get all connected nodes with :X label for up to 2 levels deep: MATCH (a)<-[*0..2]-(n:X) and assure that those n nodes are not connected to other X in between: WHERE NOT (n)-->(:X) and return: RETURN n.
So the whole cypher query would look like this:
MATCH (a:Ancestor) MATCH (a)<-[*0..2]-(n:X) WHERE NOT (n)-->(:X) RETURN n.

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

Resources