Deleting a tree of nodes, using cypher? - neo4j

I've got an arbitrary depth tree of nodes which I'd like to delete.
I'd expected this to work:
MATCH (a)<-[rels*]-(t)
WHERE ID(a)=135
FOREACH(r in rels | DELETE r)
DELETE t,a
but it fails:
javax.transaction.HeuristicRollbackException: Failed to commit transaction
Transaction(32, owner:"qtp1200654940-73")
[STATUS_NO_TRANSACTION,Resources=1], transaction rolled back --->
Node record Node[137,used=false,rel=347,prop=-1,labels=Inline(0x0:
[]),light] still has relationships
Why is this? Shouldn't the relationships be removed first? I imagine that it's iterating over all the found paths and processing the shortest ones first.
Has anyone got an idea of how to work around this?

The a node may have outgoing relationships, and any of the t nodes may also have incoming/outgoing relationships not in the path leading to a. Your query does not delete such relationships.
This query should delete the same nodes that your query attempted to delete, but it should also delete all the relationships that need to be deleted to allow that:
MATCH ()<-[r1*0..1]-(a)<-[rels*]-(t)-[r2*0..1]-()
WHERE ID(a)=135
FOREACH (x IN r1 | DELETE x)
FOREACH (x IN r2 | DELETE x)
FOREACH (x IN rels | DELETE x)
DELETE a, t

Related

Cypher delete a node and its child node based on condition

I have recently started using Neo4j (version 3.4.1) and still learning the nuances. I have the following the node relationship in my application.
What I am trying to achieve is the following.
I can delete nodes C1 or C2. I am able to delete their corresponding relationships as well (i.e HAS_X or HAS_Y).
However, when I delete both C1 and C2, node L1 and its other related nodes (M1, M2 and M3) become orphans. Hence, what I want is that whenever I am deleting C1 or C2, if it is the only node that has HAS_Y relationship with node L1, then in that case node L1 and its related nodes (M1, M2 and M3) should also be deleted. If it is not the only node that has HAS_Y relationship with L1, in that case we just delete that specific node (i.e C1/C2). Node L1 and rest of the nodes are left unotuched.
Nodes U1 and U2 remain unaffected in both the scenarios.
I am not sure how I can achieve this using a single cypher query.
Note : I was able to achieve my goal by running 2 separate queries (1 for deleting node C1/C2 and another one for deleting orphan node L1). However, it isn't the most performant as I have to make 2 roundtrips to db.
Is anyone able to give me some inputs on how I can achieve this task? I am looking for a cyper query solution (I am avoiding APOC procedures atm as I hear it requires some modification to neo4j db config)
Regards,
V
You should be able to do this with just Cypher:
...// above is your match to 'c', the node to delete
OPTIONAL MATCH (c)-[:HAS_Y]->(l)
DETACH DELETE c
WITH DISTINCT l
WHERE size(()-[:HAS_Y]->(l)) = 0
OPTIONAL MATCH (l)-[:HAS_Z*0..1]->(toDelete)
DETACH DELETE toDelete
We first match to l, then we delete c. At this point, we only have to take action for any l nodes that no longer have any incoming :HAS_Y relationships. We filter just for these, and then use an optional match with a 0..1 variable relationship to capture both the l nodes and any children they have down :HAS_Z relationships, then delete all of those nodes (both l and all of its possible children will be addressed via toDelete).

Cypher: Node not found in same query

I have this test database:
I want to remove that path with two +B nodes to the right. This case generally can be described as a path, that contains PS nodes (+B nodes are also PS nodes, that do not have an incoming :SOURCE edge. The sub path I want to delete is the one between (writer) (excluding) and and that node that has no incoming :SOURCE edge (including).
For that I have this query:
MATCH p1=(writer:A {type:'writer'})-[*]->(Q:PS)-[:TARGET*]->(T)
WITH (Q), (writer)
MATCH (Q)
WHERE NOT ()-[:SOURCE]->(Q)
WITH (Q), (writer)
MATCH p2=(writer)-[*]->(Q)
WHERE ANY (x IN NODES(p2)[1..] WHERE x:PS AND NOT ()-[:SOURCE]->(x))
WITH REDUCE(s = [], y IN NODES(p2)[1..] | CASE
WHEN y:PS THEN s + y
ELSE s END) AS todo
FOREACH (z IN todo | DETACH DELETE z);
It first identifies the said node(s) and then passes them on to make a new path selection, that ends in that node. This all works correctly. What does not work is the very last part beginning with WITH REDUCE. It says it does not find Q, but Q does not even occur in that part.
The error is Node <some ID> not found. Why is that? Why does it not find the node again and why does it even try to in the last part? Cutting the last part off and the query works as intended up to that point.
[UPDATED]
That error typically means that you are trying to delete a node that has already been deleted, or to delete a relationship that has an endpoint node that has already been deleted.
Try replacing your FOREACH clause with the following snippet, which eliminates duplicate nodes before attempting to delete them, and also deletes the relationships before the nodes:
UNWIND todo AS node
WITH DISTINCT node
MATCH (node)-[r]-()
WITH COLLECT(DISTINCT node) AS nodes, COLLECT(DISTINCT r) AS rels
FOREACH(r IN rels | DELETE r)
FOREACH(n IN nodes | DELETE n);
Also, your query seems to very inefficient. Here is a simpler version, which includes the above fix:
MATCH p=(:A {type:'writer'})-[*]->(:PS)-[:TARGET]->()
WHERE ANY (x IN NODES(p)[1..-1] WHERE x:PS AND NOT ()-[:SOURCE]->(x))
WITH REDUCE(s = [], y IN NODES(p)[1..] | CASE
WHEN y:PS THEN s + y
ELSE s END) AS todo
UNWIND todo AS node
WITH DISTINCT node
MATCH (node)-[r]-()
WITH COLLECT(DISTINCT node) AS nodes, COLLECT(DISTINCT r) AS rels
FOREACH(r IN rels | DELETE r)
FOREACH(n IN nodes | DELETE n);

Delete entire subgraph from a start node - subgraph is a connected component

I am trying to delete a whole subgraph, using the following query:
match
(n:StartNode {id:'id1'})-[r*1..6]-(m)
foreach(rel in r|delete rel) with n, collect(distinct m) as del_nodes2
foreach(node in del_nodes2|delete node);
All components in the subgraph are connected. The start node does exist. The maximum chain length is 6. However, i am getting the following error:
javax.transaction.HeuristicRollbackException: Failed to commit transaction Transaction(6, owner:"qtp1905632138-213")[STATUS_NO_TRANSACTION,Resources=1], transaction rolled back ---> javax.transaction.xa.XAException
2 suggestions:
Specify relationship directionality in your MATCH clause, or else you could end up deleting not just the descendants of your start node, but all its ancestors as well! Also, this may be why your deletion is failing -- some of the ancestor nodes may have other relationships that your query does not try to delete.
You should be able to simplify you query.
Try this:
MATCH (:StartNode {id:'id1'})-[r*1..6]->(m)
FOREACH(rel in r | DELETE rel)
DELETE m;

Fastest way to remove reciprocal relationships in Neo4j?

I imported a thesaurus with duplicate "Related Term" relationships so when A and B are related, my graph contains (A)-[:RT]->(B) as well as (B)-[:RT]->(A)
To clean this up, because Neo4j allows me to traverse the graph in both directions, I did
match (t0)-[r0:RT]->(t1)-[r1:RT]->(t2)
where t0=t2 AND id(r0) > id(r1)
delete r0
Is this the fastest way ? Answer : No, because it can be simplified.
Edited into
match (t0)-[r0:RT]->(t1)-[r1:RT]->(t0)
where id(r0)>id(r1)
delete r0
In Cypher the relationships are unique in each path so unless you break up the query into two separate matches, r1 and r2 will never bind the same relationship.
MATCH (t0)-[r:RT]->(t1)-[:RT]->(t0)
DELETE r
Besides, the relationships declared in your pattern have different direction with regards to (t0), so they can't bind the same relationship for that reason also. You can see this if you break the query up.
MATCH (t0)-[r1:RT]->(t1), (t0)<-[r2:RT]-(t1)
Addendum
As you pointed out in a comment, a pattern like this ends up deleting both relationships after all. This is incidental–each individual match will behave as above and only one relationship is deleted. The reason the query as a whole deletes both relationships is that the pattern is symmetric, i.e. a node will satisfy the pattern in the place designated t0 if and only if it also satisfies the pattern in the place designated t1, or (semi-formally)
(t0)-[:RT]->(t1)-[:RT]->(t0) iff (t1)-[:RT]->(t0)-[:RT]->(t1)
Perhaps I should have said that r1 and r2 can never bind the same relationship at the same time, or in the same match or path. The solution is to break the symmetry. I imagined a local query with a discriminating property
(t0 {name:"t0"})-[r:RT]->(t1)-[:RT]->(t0)
DELETE r
but for a global query, if you want to do it all at once, comparing id on t0 and t1 is great. Which is exactly the answer you had arrived at already.
I use something like this:
match (s:Node)-[r]-(n:Node)
with s,n,type(r) as t, tail(collect(r)) as coll
foreach(x in coll | delete x)
it will remove duplicate and reciprocal relationships regardless of the direction of the edges; However, if you want to specify direction, you can also try:
match (s:Node)-[r]->(n:Node)
with s,n,type(r) as t, tail(collect(r)) as coll
foreach(x in coll | delete x)
or the other way around.
Hope this helps.
I think where clause wont be required.
match (t0)-[r0:RT]->(t1)-[r1:RT]->(t0)
delete r0
I usually use something like this:
match (t0)-[r:RT]-(t1)
with t0,t1, collect(r) as rels
forach (r in tail(rels) | delete r)

In Neo4J, while removing a relationship from a node, how do you also delete the node itself it doesn't have other relationships?

While removing a relationship, I'd like to understand if a node will be orphan and, if so, delete the node aswell.
example input graph:
(a)-[r1]-(b)-[r2]-(c)-[r3]-(d)
expected graph after removing relationship r3: (d) would be orphan, so delete it.
(a)-[r1]-(b)-[r2]-(c)
expected graph after removing relationship r2: two splitted graphs, no node deleted.
(a)-[r1]-(b)
(c)-[r3]-(d)
Is it possible to achieve that in cypher?
If you want to delete relationships together with their then orphaned nodes individually try first fetching the start and end nodes for the relationship you are about to delete, delete the relationship, then test if the nodes are 'orphaned' and conditionally delete them. You can do this by putting the start/end nodes in a collection, filter the collection on NOT n--() as a predicate, then delete the nodes in the filtered collection in a FOREACH loop.
MATCH (a {name:"Federico"})-[r1]-(b)-[r2]-(c)-[r3]-(d)
WITH r3, [startNode(r3), endNode(r3)] as ns
DELETE r3
WITH FILTER(n IN ns WHERE NOT n--()) as orphans
FOREACH (n IN orphans | DELETE n)
If you want to delete several relationships at once you can try binding your pattern as a path
MATCH path=(a {name:"Federico"})-[r1]-(b)-[r2]-(c)-[r3]-(d)
then after you have deleted some relationships
DELETE r1, r3
you can filter all nodes in the path and delete the orphans as above
WITH FILTER(n IN nodes(path) WHERE NOT n--()) as orphans
FOREACH (n IN orphans | DELETE n)
As long as the relationships you delete are in the path, any orphaned nodes they leave behind will also be deleted.
(I can't test these queries at the moment but I think I got the grammar right.)
So in short you are trying to delete any orphan node in your graph that wont have any relationship.
MATCH (a)-[r1]-(b)-[r2]-(c)-[r3]-(d)
WITH r3, startNode(r3) AS sR, endNode(r3) AS eR
DELETE r3
WITH sR,eR
MATCH n
WHERE n IN [sR,eR] AND NOT n--()
DELETE n
The above will delete all the nodes that are orphaned after relation r3 is deleted

Resources