bhspencer
bhspencer

Reputation: 13590

Neo4j delete a nodes existing relationships and create new relationship

I want a query that will delete all of the existing relationships from a particular node and then create a new relationship. This is what I have so far:

MATCH (m:Movie  {
  _id: 'test/55de0539eb1e14f26a04'
})
MATCH (existingNode:Person  {
  _id: 'test/41126fc03289a05d8621'
})
MATCH (existingNode)-[existingRelationships]->()
DELETE existingRelationships
WITH existingNode, m
CREATE (existingNode)-[:ACTED_IN]->(m)
RETURN existingNode;

And this works as I would like when the existingNode has at least one existing relationship. However if the existingNode has zero existingRelationships when I run this query the new relationship is not created. It is as if nothing after the DELETE statement is run if there was nothing to delete.

How can I chain on the CREATE even when there was nothing to delete?

Upvotes: 1

Views: 94

Answers (2)

nimrod serok
nimrod serok

Reputation: 16033

Try using OPTIONAL and also distinct.

Explanation: According to neo4j concept of cardinality, if we have several matches, we will get a row for each one of them. Therefore, if several nodes are connected to existingNode we will get several rows as a result, one for each connected node. The existingNode will repeat in all of them, causing next steps regarding to it, to be duplicated. In our case the new connection step will be preformed multiple times.

Without the OPTIONAL if there are zero nodes connected to it, we will get zero rows, which is the other problem.

So when using the OPTIONAL, after the DELETE step we have multiple existingNode, as the number of existingRelationships, but at least one, thanks to the OPTIONAL. We still need to use distinct to avoid creating multiple relationships to m:

MATCH (existingNode:Person  {
  key: 'A'
}) 
WITH existingNode
OPTIONAL MATCH (existingNode)-[existingRelationships]->()
DELETE existingRelationships
WITH distinct existingNode as existingNode
MATCH (m:Movie  {key: 'B'})
WITH existingNode, m
CREATE (existingNode)-[:ACTED_IN]->(m)
RETURN existingNode, m

It works well for the sample data with relationships to delete:

MERGE (a:Person{key: 'A'})  
MERGE (b:Movie{key: 'B'})
MERGE (c:Node{key: 'C'})
MERGE (d:Node{key: 'D'})
MERGE (e:Node{key: 'E'})

MERGE (a)-[:POINTS]-(c)
MERGE (a)-[:POINTS]-(d) 
MERGE (a)-[:POINTS]-(e) 

And another one without relationships to delete:

MERGE (a:Person{key: 'A'})  
MERGE (b:Movie{key: 'B'})

For both cases it returns:

╒══════════════╤═══════════╕
│"existingNode"│"m"        │
╞══════════════╪═══════════╡
│{"key":"A"}   │{"key":"B"}│
└──────────────┴───────────┘

Upvotes: 1

bhspencer
bhspencer

Reputation: 13590

The solution was to use an OPTIONAL MATCH before the DELETE e.g.

MATCH (m:Movie  {
  _id: 'test/55de0539eb1e14f26a04'
})
MATCH (existingNode:Person  {
  _id: 'test/41126fc03289a05d8621'
})
OPTIONAL MATCH (existingNode)-[existingRelationships]->()
DELETE existingRelationships
WITH existingNode, m
CREATE (existingNode)-[:ACTED_IN]->(m)
RETURN existingNode;

I believe the behavior of Neo4j is to break out of the query if any MATCH statement fails to return any items. The OPTIONAL MATCH allows the query to continue even when no items match:

https://neo4j.com/docs/cypher-manual/current/clauses/optional-match/

Upvotes: 0

Related Questions