6
votes

I am trying to figure out how to efficiently add a new node using a cypher query. I am trying to merge multiple data sources, so need to look for possible matching data. I have 3 data points that may or may not exist. If any of the data points match, I want do reuse the existing node. If none of the data points match, I want to create a new node.

Creating the node if it does not exist is the exact use case for MERGE. MERGE does not allow a WHERE clause otherwise this would be pretty simple. Since I am matching data-point-a OR data-point-b OR data-point-c, I can't figure out how to use MERGE as it AND's all the properties.

This is not valid, but should express my goal:

MERGE (n:TYPE)
WHERE n.propertyA = "A" OR n.propertyB = "B" OR n.propertyC = "C"
ON MATCH SET n.propertyA = "A", n.propertyB = "B", n.propertyC = "C"  
ON CREATE SET n.timestamp = <now>, n.propertyA = "A", n.propertyB = "B", n.propertyC = "C"
RETURN n;

I was thinking I might be able to use a batch or transaction. I would appreciate any insight or guidance as I am still learning Cypher.

2
Trying to accomplish the same. Have you find a solution you like?Ricky Brown

2 Answers

2
votes

Not sure if you can do it in a single statement, I'll be following this question to see if there is a more optimal route. You can do it in two where first will find and update existing nodes, and the second will find and create the missing ones:

OPTIONAL MATCH (existing:TYPE) WHERE existing.propertyA = 'A' OR existing.propertyB = 'B' OR existing.propertyC = 'C'
WITH existing
WHERE existing IS NOT NULL SET existing.propertyA = 'A', existing.propertyB = 'B', existing.propertyC = 'C'
RETURN existing;

OPTIONAL MATCH (existing:TYPE) WHERE existing.propertyA = 'ZZ' OR existing.propertyB = 'ZZ' OR existing.propertyC = 'ZZ'
WITH existing
WHERE existing IS NULL MERGE (newNode:TYPE {propertyA: 'ZZ', propertyB: 'ZZ', propertyC: 'ZZ'})
RETURN newNode

However, note that this does not preserve existing values - e.g. if you load from one source properties A & B (and C is null) and then you load from second source and A is null but B & C are set, then the update statement would match the node, but would reset A to null (unless, of course, you make sure your update only sets the values which are not-null).

-2
votes

I don't know if I understood the question perfectly, but this should help you:

Merge, how it works

According to neo4j's documentation merge can be used to create a node if it doesn't exists, using the node property you specified to know if the node already exists or not.

In fact, you don't need the WHERE clause here to match your node's properties.

So this:

MERGE (n:TYPE)
WHERE n.propertyA = "A" OR n.propertyB = "B" OR n.propertyC = "C"
ON MATCH SET n.propertyA = "A", n.propertyB = "B", n.propertyC = "C"  
ON CREATE SET n.timestamp = timestamp(), n.propertyA = "A", n.propertyB = "B", n.propertyC = "C"
RETURN n;

Which is, as you said, wrong. Can be almost achieved like this:

MERGE (n:TYPE {propertyA : "A"})
ON MATCH SET n.propertyA = "A", n.propertyB = "B", n.propertyC = "C"  
ON CREATE SET n.timestamp = timestamp(), n.propertyA = "A", n.propertyB = "B", n.propertyC = "C"
RETURN n;

If I understand what you want to do exactly, it's impossible. Because MERGEIs either matching or creating the node. You want to merge using OR, which can't work because you can't create a node doing this:

CREATE (a:Node) SET a.propertyA = "A" OR a.propertyB = "B"

To do what you want to do, you have to split your merge in multiple parts I guess, but I don't see how actually, will edit the answer if I find how.