Neo4j - Exclude node from results where it has a specific relationship - neo4j

I'm attempting to a set of nodes (p) where they have a relationship [:INCLUDE] to a specific node (ca) identified by its ID, but I also want to make sure I exclude any (p) node that also has an [:EXCLUDE] relationship to any other (ca) node.
I've tried the below...
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p) OR (a{id:"b9a97804-0c6b-4d83-8b35-84bda7f8b69c"})-[]-(p)
WITH p,a
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl)
WITH p,a,pl
RETURN *
Which gets me this...
And then tried to filter it with this...
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p) OR (a{id:"b9a97804-0c6b-4d83-8b35-84bda7f8b69c"})-[]-(p)
WITH p,a
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl) AND NOT (p)-[:EXCLUDE_CLOUDAPP]-(a)
WITH p,a,pl
RETURN *
But this results in the same 3 (p) nodes and just excludes the (a) node where that relationship exists. I've tried a few variations on the above query and always seem to get the same result...
I'm guessing that this is because it just excludes that relationship and the node remains because it has another valid relationship. I'm just not sure how to achieve what I want?

Just remove the variable a from the condition NOT (p)-[:EXCLUDE_CLOUDAPP]-(a) and add a node label, and try this:
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p) OR (a{id:"b9a97804-0c6b-4d83-8b35-84bda7f8b69c"})-[]-(p)
WITH p,a
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl) AND NOT (p)-[:EXCLUDE_CLOUDAPP]-(:CloudApp)
WITH p,a,pl
RETURN *
This basically checks whether the policy node is not linked to any CloudApp node via exclude relationship.

So the way I solved it was this
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (p)-[:EXCLUDE_CLOUDAPP]-(a)
WITH COLLECT(p) as excluded
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p:Policy{state: "enabled"})
WITH p,a,excluded
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl)
WITH COLLECT(p) as included, excluded
RETURN [n IN included WHERE not n IN excluded] as list
I found the nodes I didn't want, then the ones I did want and removed the excluded ones from those using the WHERE not n IN part

Related

Cypher: filter out nodes with certain paths

Suppose:
I have in my Neo4j database, nodes with SpecialFromLabel in them.
These nodes are connected to SpecialToLabel nodes with SPECIAL_REL relationship
The following paths, therefor, exist: p = (from: SpecialFromLabel)-[r:SPECIAL_REL]->(to: SpecialToLabel)
Nodes with SpecialToLabel have a property to_prop that can have different string values
How can I construct a query that gives me all from nodes that have a path: p=(from: SpecialFromLabel)-[:SPECIAL_REL]->(to: SpecialToLabel) - but those from nodes do not have a path to to nodes, where to.to_prop is in an unwanted values-set?
in pseudocode (not runnable) I want something like this:
MATCH (unwanted_from: SpecialFromLabel)-[r:SPECIAL_REL]->(to: SpecialToLabel)
WHERE (to.`to_prop` in $UnwantedValues)
with unwanted_from
MATCH (from: SpecialFromLabel)-[r:SPECIAL_REL]->(to: SpecialToLabel)
WHERE from not in unwanted_from
RETURN from
I would approach it like this
// collect the unwanted endpoints
MATCH (to: SpecialToLabel)
WHERE (to.`to_prop` in $UnwantedValues)
WITH COLLECT(to) AS unwantedEndpoints
// only use the 'from' nodes that do not have a relation to one of the unwantedEndpoints
MATCH p=(from: SpecialFromLabel)-[r:SPECIAL_REL]->(to: SpecialToLabel)
WHERE NONE( n IN [(from)-[:SPECIAL_REL]->(m) | m] WHERE n IN unwantedEndpoints)
RETURN p
==== UPDATED , and applied to Movie dataset====
// be careful not to re-use the bindings!
MATCH (persons:Person)-[a:ACTED_IN]->(m:Movie)
WHERE ((m.title in ["Speed Racer"]))
with collect(m) as excluded
MATCH (p:Person)-[:ACTED_IN]->(:Movie)
WHERE none(n in [(p)-[:ACTED_IN]->(m2:Movie)| m2] where n in excluded)
RETURN DISTINCT p.name,[(p)-[:ACTED_IN]->(m3:Movie)| m3.title]
ORDER BY p.name

Neo4J Matching Nodes Based On Relationships

I have the following statement:
MATCH path=(p:Person {person_id: '123'})-[:ASSOCIATED_WITH]-(:Person)
where exists((p)-[:BELONGS]-(:Face)-[:CORRESPONDS]-(:Image)-[:HAS_ACCESS_TO]-(:Dias {group_name: 'group'}))
RETURN path
It returns 3 nodes, 2 relationships. This is what I would like to happen but the group_name I will actually be passing is an array. How do I add the condition to check for the value in the array while still maintaining 3 nodes and two relationships? The only relationship I want to return back is ASSOCIATED_WITH. The 3 nodes being, the main person_id of "123" and the two associated persons.
I will assume you pass the list of group names in a names parameter.
If you want to get the paths in which the EXISTS test succeeds for ANY name in the list:
MATCH (p:Person {person_id: '123'})
WHERE ANY(x IN $names WHERE
EXISTS((p)-[:BELONGS]-(:Face)-[:CORRESPONDS]-(:Image)-[:HAS_ACCESS_TO]-(:Dias {group_name: x})))
MATCH path=(p)-[:ASSOCIATED_WITH]-(:Person)
RETURN path
Or, if you want to get the paths in which the EXISTS test succeeds for ALL names in the list, just replace ANY in the above request with ALL:
MATCH (p:Person {person_id: '123'})
WHERE ALL(x IN $names WHERE
EXISTS((p)-[:BELONGS]-(:Face)-[:CORRESPONDS]-(:Image)-[:HAS_ACCESS_TO]-(:Dias {group_name: x})))
MATCH path=(p)-[:ASSOCIATED_WITH]-(:Person)
RETURN path

How can branches get pruned in a query?

suppose I have a tree where certain nodes have a relationship of a give type, how would I return all nodes in the tree except for those with the given relationship and its descendants.
I've gotten half way there with something like this (the tree is built on has links):
match (root: {Name: 'Root'})-[:has*]->(n) where not (n)-[:Exemption]-() return n
but, of course, this excludes nodes that have a relationship of type Exemption but not its descendants, so the descendants show up as unconnected nodes
how do I structure the query?
This query should work:
MATCH p=({Name: 'Root'})-[:has*]->(n)
WHERE NONE(x IN NODES(p) WHERE (x)-[:Exemption]-())
RETURN n;
It filters out any :has relationship paths with a node that (also) has an :Exemption relationship.
I would probably split the path up into two parts:
MATCH p=(:Label {Name: 'Root'})-[:has*]->(n) WHERE NOT EXISTS ((n)-[:Exemption]->())
MATCH p2 = (n)-[:has*]->(m) WHERE NOT (m)-[:has]->()
RETURN p,p2;
after some experimentation, this worked:
match (root: {Name: 'Root'})-[:has*]->(n)
match (m)-[:has*]->(n)
where not (m)-[:Exemption]-()
return n
which essentially says: find a path, for any node on that path, traverse back up to make sure that none of the ancestors show an exception. so what I'm negating here is the subpath created with the second match

looking for a clean way to find relationships from a set of nodes

I have got a set of nodes and I would like to write a query to find every relationships which exist between these nodes.
Any recommendation?
My first attemp would be a MATCH a-[rel]-b WHERE... and my WHERE clause would be a OR of all the ids I have in my set of nodes.
Any better idea?
In these answers, I assume you will pass the ID list as a parameter named ids.
If you want to exclude relationships from a node to itself:
MATCH (a)-[r]-(b)
WHERE id(a) IN {ids} AND id(b) IN {ids} AND a <> b
RETURN r;
If you want to include relationships from a node to itself:
MATCH (a)-[r]-(b)
WHERE id(a) IN {ids} AND id(b) IN {ids}
RETURN r;
You could do something like this:
MATCH (a)-[rel]-()
WHERE ID(a) IN {node_ids}
RETURN rel, ID(rel), type(rel), ID(startNode(rel)), ID(endNode(rel))
Here you'd be passing in a node_ids parameter which would be an array.
Note also that using internal Neo4j IDs for long-term reference to nodes may break if Neo4j defragments the nodes.

What's the Cypher script to delete a node by ID?

In SQL:
Delete From Person Where ID = 1;
In Cypher, what's the script to delete a node by ID?
(Edited: ID = Neo4j's internal Node ID)
Assuming you're referring to Neo4j's internal node id:
MATCH (p:Person) where ID(p)=1
OPTIONAL MATCH (p)-[r]-() //drops p's relations
DELETE r,p
If you're referring to your own property 'id' on the node:
MATCH (p:Person {id:1})
OPTIONAL MATCH (p)-[r]-() //drops p's relations
DELETE r,p
The cleanest sweep for a node with id "x" is
MATCH (n) where id(n) = x
DETACH DELETE n
https://neo4j.com/docs/cypher-manual/current/clauses/delete/#delete-delete-a-node-with-all-its-relationships
https://neo4j.com/docs/cypher-manual/current/functions/scalar/#functions-id
Old question and answered, but to delete node when it has relationships, use DETACH
MATCH (n) where ID(n)=<your_id>
DETACH DELETE n
or otherwise you get this:
Neo.ClientError.Schema.ConstraintValidationFailed: Cannot delete node<21>, because it still has relationships. To delete this node, you must first delete its relationships.
It's like SQL's CASCADE
When the node is a orphan.
Start n=node(1)
Delete n;
Following the link provided by #saad-khan, here's an example for getting the nodes and relationships ids.
The code below shows the ids, so you can make sure that you're deleting everything related to the given ID.
MATCH (node)-[relation:HAS]->(value)
where ID(node)=1234
RETURN ID(instance), ID(value), ID(r)
Ps.: ":HAS" is an example of an relationship.

Resources