neo4j delete a node in linked list

347 Views Asked by At

I have a list of posts that belong to a user. The posts are structured as linked list that belongs to a given user.

I am having deleting the a given node while preserving the data structure.

Here is an illustration of the structure.

enter image description here

In my list attempt I have written the query below, however it does not work since the MATCH does not return anything in the sub-queries.

I thought about using OPTIONAL MATCH in the sub-queries, however that results in exceptions.

MATCH(node:Post)
WHERE ID(node) = 2749
WITH node
MATCH(user:User)-[:OWNS]->(node)-[:PREV]->(nextNode:Post)
CREATE(user)-[:OWNS]->(nextNode)
WITH node
MATCH(prevNode:Post)-[:PREV]->(node)-[:PREV]->(nextNode:Post)
CREATE(prevNode)-[:PREV]->(nextNode)
WITH node
DETACH DELETE node

I assumed that if a pattern in a sub-query fails than the rest of the sub-query will be skipped and the next sub-query will still execute. But it seems that the whole thing does not get executed.

How do we perform something like in the following in sub-queries without halting the execution.

IF pattern matches
THEN 
   CREATE EDGE 
IF pattern matches
THEN
   Do something else

Solution

stdob--'s answer is the closest to the right solution, however it fails to delete nodes at the end of the list and throws an exception as it does not check if the nextNode exists in the second FOREACH statement.

It needed tweaking to work:

MATCH(node:Post) WHERE ID(node) = 2813

OPTIONAL MATCH (user:User)-[:OWNS]->(node)
OPTIONAL MATCH (node)-[:PREV]->(nextNode:Post)
OPTIONAL MATCH (prevNode:Post)-[:PREV]->(node)

FOREACH (ith in CASE 
    WHEN user IS NOT NULL AND nextNode IS NOT NULL 
    THEN [1] 
    ELSE [] 
    END | CREATE (user)-[:OWNS]->(nextNode) 
)

FOREACH (ith in CASE WHEN 
    prevNode IS NOT NULL AND nextNode IS NOT NULL 
    THEN [1] ELSE [] 
    END | CREATE (prevNode)-[:PREV]->(nextNode)
)

DETACH DELETE node
2

There are 2 best solutions below

2
On BEST ANSWER

You can use combination of OPTIONAL MATCH, FOREACH and CASE:

MATCH(node:Post) WHERE ID(node) = 2749

OPTIONAL MATCH (user:User)-[:OWNS]->(node)
OPTIONAL MATCH (node)-[:PREV]->(nextNode:Post)
OPTIONAL MATCH (prevNode:Post)-[:PREV]->(node)

FOREACH (ith in CASE WHEN 
                     NOT user IS NULL AND 
                     NOT nextNode IS NULL 
                THEN [1] ELSE [] END |
    CREATE (user)-[:OWNS]->(nextNode) 
)

FOREACH (ith in CASE WHEN 
                     NOT prevNode IS NULL AND
                     NOT nextNode IS NULL
                THEN [1] ELSE [] END |
    CREATE (prevNode)-[:PREV]->(nextNode)
)

DETACH DELETE node

Update: Also you can use the procedure apoc.do.when from the apoc.library for the conditional query execution.

3
On

I recreated your data set with the following query:

CREATE
  (u:User {name: 'Alice'})-[:OWNS]->
  (p1:Post {number: 1})-[:PREV]->
  (p2:Post {number: 2})-[:PREV]->
  (p3:Post {number: 3})-[:PREV]->
  (p4:Post {number: 4})-[:PREV]->
  (p5:Post)

As you mentioned, using OPTIONAL MATCH can result in exceptions, something along the lines of:

Expected to find a node at prevNode but found nothing Some(NO_VALUE)

However, you can work around this, by first doing the DELETE, then passing on the value using a WITH clause and only running the CREATE if the variable introduced by the OPTIONAL MATCH clause IS NOT NULL.

MATCH (user:User)-[:OWNS]->(node)-[:PREV]->(nextNode:Post)
WHERE node.number = 1
CREATE (user)-[:OWNS]->(nextNode)
WITH node, nextNode
OPTIONAL MATCH (prevNode:Post)-[:PREV]->(node)
DETACH DELETE node
WITH prevNode, nextNode
WHERE prevNode IS NOT NULL
CREATE (prevNode)-[:PREV]->(nextNode)