Reputation: 63
In cypher, there isn't an 'official' conditional update. This is emulated using a 'case trick' IF, which is a CASE inside a FOREACH as demonstrated here and here.
Here's the query I'm working with:
MATCH (reaper:REAPER)-[:TO_REAP]->(doomed)
WHERE reaper <> doomed
WITH reaper, doomed
LIMIT 1
OPTIONAL MATCH (doomed)-[x]-(doomed)
DELETE x
WITH reaper, doomed
OPTIONAL MATCH (doomed)-[r]-(related)
DELETE r, doomed
WITH related, reaper
WHERE related <> reaper
FOREACH(ignore_me IN CASE WHEN
related IS NOT NULL
THEN [1] ELSE [] END | MERGE (reaper)-[:TO_REAP]->(related))
RETURN 1
The purpose of this REAPER is to delete nodes (subgraphs). We do this by deleting nodes directly connected to it ('doomed' nodes), then connecting all of the children of 'doomed' nodes directly to the REAPER (related) using the MERGE. Of course, if there are no children, we don't have anything to connect, which is where the FOREACH and CASE come in. If there are no children (related), then one would expect the FOREACH to NO-OP and move on to the RETURN.
The issue is when related IS null, the query craps out, and doesn't return ANYTHING, but also DOES NOT FAIL. This is a problem, because we cannot determine when there is nothing to delete VS. a 'leaf' (single node without children) node was deleted, due to this issue.
When trying to diagnose the issue, I removed the 'if' statement (FOREACH and CASE), and when related is null, it throws an error 'Other node is null'.
I am suspecting that the FOREACH loop is somehow eating this error, even though the MERGE shouldn't be executing at all in that condition.
I'm suggesting that there's a possible neo4j/cypher bug, so I would like someone to come in and tell me how wrong and inefficient my query is XD
edit: I forgot to talk about data staging, if you wanted to test this query yourself.
This will set you up to experience my issue:
CREATE (r:REAPER)
CREATE (r)-[:TO_REAP]->(n)-[:doesntmatter]->(m)
CREATE (r)-[:TO_REAP]->(p)
When you run the delete query, you'll notice that for the 'n' node, it will return a 1. But for the other 2 nodes, m and p (which are leaf nodes), they will get deleted, but nothing will be returned.
Upvotes: 1
Views: 932
Reputation: 67019
This info might help.
Cypher handles the following 2 contrived snippets differently. Let's assume that there is no Foo
node.
Snippet 1. (The WHERE
clause aborts the query, which returns nothing):
OPTIONAL MATCH (n:Foo)
WITH n
WHERE NULL = 123
RETURN 1;
Snippet 2. (The query completes and returns 1):
OPTIONAL MATCH (n:Foo)
WHERE NULL = 123
RETURN 1;
This difference in behavior may be what is causing your issue.
However, Cypher requires that you use a WITH
to separate a DELETE
from a subsequent MATCH
or WHERE
clause.
One way to work around this might be to just move the last WITH
clause up by 2 lines (right after its corresponding OPTIONAL MATCH
), so that it will not abort the query:
MATCH (reaper:REAPER)-[:TO_REAP]->(doomed)
WHERE reaper <> doomed
WITH reaper, doomed
LIMIT 1
OPTIONAL MATCH (doomed)-[x]-(doomed)
DELETE x
WITH reaper, doomed
OPTIONAL MATCH (doomed)-[r]-(related)
WHERE related <> reaper
DELETE r, doomed
WITH related, reaper
FOREACH(ignore_me IN CASE WHEN
related IS NOT NULL
THEN [1] ELSE [] END | MERGE (reaper)-[:TO_REAP]->(related))
RETURN 1
Upvotes: 2