MERGE

Introduction

MERGE either matches existing nodes and binds them, or it creates new data and binds that. It’s like a combination of MATCH and CREATE that additionally allows you to specify what happens if the data was matched or created.

For example, you can specify that the graph must contain a node for a user with a certain name. If there isn’t a node with the correct name, a new node will be created and its name property set.

For performance reasons, creating a schema index on the label or property is highly recommended when using MERGE. See Indexes for search performance for more information.

When using MERGE on full patterns, the behavior is that either the whole pattern matches, or the whole pattern is created. MERGE will not partially use existing patterns — it is all or nothing. If partial matches are needed, this can be accomplished by splitting a pattern up into multiple MERGE clauses.

Under concurrent updates, MERGE only guarantees existence of the MERGE pattern, but not uniqueness. To guarantee uniqueness of nodes with certain properties, a unique constraint should be used. See Using unique constraints with MERGE to see how MERGE can be used in combination with a unique constraint.

As with MATCH, MERGE can match multiple occurrences of a pattern. If there are multiple matches, they will all be passed on to later stages of the query.

The last part of MERGE is the ON CREATE and ON MATCH. These allow a query to express additional changes to the properties of a node or relationship, depending on if the element was matched (MATCH) in the database or if it was created (CREATE).

The following graph is used for the examples below:

graph merge clause

Merge nodes

Merge single node with a label

Merging a single node with the given label.

Query
MERGE (robert:Critic)
RETURN robert, labels(robert)

A new node is created because there are no nodes labeled Critic in the database.

Table 1. Result
robert labels(robert)

Node[7]{}

["Critic"]

Rows: 1
Nodes created: 1
Labels added: 1

Merge single node with properties

Merging a single node with properties where not all properties match any existing node.

Query
MERGE (charlie {name: 'Charlie Sheen', age: 10})
RETURN charlie

A new node with the name Charlie Sheen is created since not all properties matched those set to the pre-existing Charlie Sheen node:

Table 2. Result
charlie

Node[7]{age:10,name:"Charlie Sheen"}

Rows: 1
Nodes created: 1
Properties set: 2

MERGE cannot be used for nodes with property values that are null. For example, the following query will throw an error:

Query
MERGE (martin:Person {name: 'Martin Sheen', age: null})
RETURN martin
Cannot merge the following node because of null property value for 'age': (:Person {age: null})

Merge single node specifying both label and property

Merging a single node with both label and property matching an existing node.

Query
MERGE (michael:Person {name: 'Michael Douglas'})
RETURN michael.name, michael.bornIn

Michael Douglas is matched and the name and bornIn properties are returned:

Table 3. Result
michael.name michael.bornIn

"Michael Douglas"

"New Jersey"

Rows: 1

As mentioned previously, MERGE queries can greatly benefit from schema indexes. In this example, the following would significantly improve the performance of the MERGE clause:

Query
CREATE INDEX PersonIndex FOR (n:Person) ON (n.name)

Merge single node derived from an existing node property

For some property p in each bound node in a set of nodes, a single new node is created for each unique value for p.

Query
MATCH (person:Person)
MERGE (city:City {name: person.bornIn})
RETURN person.name, person.bornIn, city

In the above query, three nodes labeled Location are created, each of which contains a name property with the value of New York, Ohio, and New Jersey respectively. Note that even though the MATCH clause results in three bound nodes having the value New York for the bornIn property, only a single New York node (i.e. a Location node with a name of New York) is created. As the New York node is not matched for the first bound node, it is created. However, the newly-created New York node is matched and bound for the second and third bound nodes.

Table 4. Result
person.name person.bornIn city

"Charlie Sheen"

"New York"

Node[7]{name:"New York"}

"Martin Sheen"

"Ohio"

Node[8]{name:"Ohio"}

"Michael Douglas"

"New Jersey"

Node[9]{name:"New Jersey"}

"Oliver Stone"

"New York"

Node[7]{name:"New York"}

"Rob Reiner"

"New York"

Node[7]{name:"New York"}

Rows: 5
Nodes created: 3
Properties set: 3
Labels added: 3

Use ON CREATE and ON MATCH

Merge with ON CREATE

Merge a node and set properties if the node needs to be created.

Query
MERGE (keanu:Person {name: 'Keanu Reeves'})
ON CREATE
  SET keanu.created = timestamp()
RETURN keanu.name, keanu.created

The query creates the 'keanu' node and sets a timestamp on creation time.

Table 5. Result
keanu.name keanu.created

"Keanu Reeves"

1655200898563

Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1

Merge with ON MATCH

Merging nodes and setting properties on found nodes.

Query
MERGE (person:Person)
ON MATCH
  SET person.found = true
RETURN person.name, person.found

The query finds all the Person nodes, sets a property on them, and returns them.

Table 6. Result
person.name person.found

"Charlie Sheen"

true

"Martin Sheen"

true

"Michael Douglas"

true

"Oliver Stone"

true

"Rob Reiner"

true

Rows: 5
Properties set: 5

Merge with ON CREATE and ON MATCH

Query
MERGE (keanu:Person {name: 'Keanu Reeves'})
ON CREATE
  SET keanu.created = timestamp()
ON MATCH
  SET keanu.lastSeen = timestamp()
RETURN keanu.name, keanu.created, keanu.lastSeen

The query creates the 'keanu' node, and sets a timestamp on creation time. If 'keanu' had already existed, a different property would have been set.

Table 7. Result
keanu.name keanu.created keanu.lastSeen

"Keanu Reeves"

1655200902354

<null>

Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1

Merge with ON MATCH setting multiple properties

If multiple properties should be set, simply separate them with commas.

Query
MERGE (person:Person)
ON MATCH
  SET
    person.found = true,
    person.lastAccessed = timestamp()
RETURN person.name, person.found, person.lastAccessed
Table 8. Result
person.name person.found person.lastAccessed

"Charlie Sheen"

true

1655200903558

"Martin Sheen"

true

1655200903558

"Michael Douglas"

true

1655200903558

"Oliver Stone"

true

1655200903558

"Rob Reiner"

true

1655200903558

Rows: 5
Properties set: 10

Merge relationships

Merge on a relationship

MERGE can be used to match or create a relationship.

Query
MATCH
  (charlie:Person {name: 'Charlie Sheen'}),
  (wallStreet:Movie {title: 'Wall Street'})
MERGE (charlie)-[r:ACTED_IN]->(wallStreet)
RETURN charlie.name, type(r), wallStreet.title

Charlie Sheen had already been marked as acting in Wall Street, so the existing relationship is found and returned. Note that in order to match or create a relationship when using MERGE, at least one bound node must be specified, which is done via the MATCH clause in the above example.

Table 9. Result
charlie.name type(r) wallStreet.title

"Charlie Sheen"

"ACTED_IN"

"Wall Street"

Rows: 1

MERGE cannot be used for relationships with property values that are null. For example, the following query will throw an error:

Query
MERGE (martin:Person {name: 'Martin Sheen'})-[r:FATHER_OF {since: null}]->(charlie:Person {name: 'Charlie Sheen'})
RETURN type(r)
Cannot merge the following relationship because of null property value for 'since': (martin)-[:FATHER_OF {since: null}]->(charlie)

Merge on multiple relationships

Query
MATCH
  (oliver:Person {name: 'Oliver Stone'}),
  (reiner:Person {name: 'Rob Reiner'})
MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:ACTED_IN]-(reiner)
RETURN movie

In the example graph, Oliver Stone and Rob Reiner have never worked together. When trying to MERGE a Movie node between them, Neo4j will not use any of the existing Movie nodes already connected to either person. Instead, a new Movie node is created.

Table 10. Result
movie

Node[7]{}

Rows: 1
Nodes created: 1
Relationships created: 2
Labels added: 1

Merge on an undirected relationship

MERGE can also be used with an undirected relationship. When it needs to create a new one, it will pick a direction.

Query
MATCH
  (charlie:Person {name: 'Charlie Sheen'}),
  (oliver:Person {name: 'Oliver Stone'})
MERGE (charlie)-[r:KNOWS]-(oliver)
RETURN r

As Charlie Sheen and Oliver Stone do not know each other in the example graph, this MERGE query will create a KNOWS relationship between them. The direction of the created relationship is left to right.

Table 11. Result
r

:KNOWS[8]{}

Rows: 1
Relationships created: 1

Merge on a relationship between two existing nodes

MERGE can be used in conjunction with preceding MATCH and MERGE clauses to create a relationship between two bound nodes m and n, where m is returned by MATCH and n is created or matched by the earlier MERGE.

Query
MATCH (person:Person)
MERGE (city:City {name: person.bornIn})
MERGE (person)-[r:BORN_IN]->(city)
RETURN person.name, person.bornIn, city

This builds on the example from Merge single node derived from an existing node property. The second MERGE creates a BORN_IN relationship between each person and a location corresponding to the value of the person’s bornIn property. Charlie Sheen, Rob Reiner, and Oliver Stone all have a BORN_IN relationship to the same Location node (New York).

Table 12. Result
person.name person.bornIn city

"Charlie Sheen"

"New York"

Node[7]{name:"New York"}

"Martin Sheen"

"Ohio"

Node[8]{name:"Ohio"}

"Michael Douglas"

"New Jersey"

Node[9]{name:"New Jersey"}

"Oliver Stone"

"New York"

Node[7]{name:"New York"}

"Rob Reiner"

"New York"

Node[7]{name:"New York"}

Rows: 5
Nodes created: 3
Relationships created: 5
Properties set: 3
Labels added: 3

Merge on a relationship between an existing node and a merged node derived from a node property

MERGE can be used to simultaneously create both a new node n and a relationship between a bound node m and n.

Query
MATCH (person:Person)
MERGE (person)-[r:HAS_CHAUFFEUR]->(chauffeur:Chauffeur {name: person.chauffeurName})
RETURN person.name, person.chauffeurName, chauffeur

As MERGE found no matches — in our example graph, there are no nodes labeled with Chauffeur and no HAS_CHAUFFEUR relationships — MERGE creates five nodes labeled with Chauffeur, each of which contains a name property whose value corresponds to each matched Person node’s chauffeurName property value. MERGE also creates a HAS_CHAUFFEUR relationship between each Person node and the newly-created corresponding Chauffeur node. As 'Charlie Sheen' and 'Michael Douglas' both have a chauffeur with the same name — 'John Brown' — a new node is created in each case, resulting in two Chauffeur nodes having a name of 'John Brown', correctly denoting the fact that even though the name property may be identical, these are two separate people. This is in contrast to the example shown above in Merge on a relationship between two existing nodes, where we used the first MERGE to bind the City nodes to prevent them from being recreated (and thus duplicated) in the second MERGE.

Table 13. Result
person.name person.chauffeurName chauffeur

"Charlie Sheen"

"John Brown"

Node[7]{name:"John Brown"}

"Martin Sheen"

"Bob Brown"

Node[8]{name:"Bob Brown"}

"Michael Douglas"

"John Brown"

Node[9]{name:"John Brown"}

"Oliver Stone"

"Bill White"

Node[10]{name:"Bill White"}

"Rob Reiner"

"Ted Green"

Node[11]{name:"Ted Green"}

Rows: 5
Nodes created: 5
Relationships created: 5
Properties set: 5
Labels added: 5

Using unique constraints with MERGE

Cypher® prevents getting conflicting results from MERGE when using patterns that involve unique constraints. In this case, there must be at most one node that matches that pattern.

For example, given two unique constraints on :Person(id) and :Person(ssn), a query such as MERGE (n:Person {id: 12, ssn: 437}) will fail, if there are two different nodes (one with id 12 and one with ssn 437) or if there is only one node with only one of the properties. In other words, there must be exactly one node that matches the pattern, or no matching nodes.

Note that the following examples assume the existence of unique constraints that have been created using:

CREATE CONSTRAINT FOR (n:Person) REQUIRE n.name IS UNIQUE;
CREATE CONSTRAINT FOR (n:Person) REQUIRE n.role IS UNIQUE;

Merge using unique constraints creates a new node if no node is found

Merge using unique constraints creates a new node if no node is found.

Query
MERGE (laurence:Person {name: 'Laurence Fishburne'})
RETURN laurence.name

The query creates the 'laurence' node. If 'laurence' had already existed, MERGE would just match the existing node.

Table 14. Result
laurence.name

"Laurence Fishburne"

Rows: 1
Nodes created: 1
Properties set: 1
Labels added: 1

Merge using unique constraints matches an existing node

Merge using unique constraints matches an existing node.

Query
MERGE (oliver:Person {name: 'Oliver Stone'})
RETURN oliver.name, oliver.bornIn

The 'oliver' node already exists, so MERGE just matches it.

Table 15. Result
oliver.name oliver.bornIn

"Oliver Stone"

"New York"

Rows: 1

Merge with unique constraints and partial matches

Merge using unique constraints fails when finding partial matches.

Query
MERGE (michael:Person {name: 'Michael Douglas', role: 'Gordon Gekko'})
RETURN michael

While there is a matching unique Person node with the name Michael Douglas, there is no unique node with the role of Gordon Gekko and MERGE, therefore, fails to match.

Error message
Merge did not find a matching node michael and can not create a new node due to
conflicts with existing unique nodes

To set the role of Gordon Gekko to Michael Douglas, use the SET clause instead:

Query
MERGE (michael:Person {name: 'Michael Douglas'})
SET michael.role = 'Gordon Gekko'

Merge with unique constraints and conflicting matches

Merge using unique constraints fails when finding conflicting matches.

Query
MERGE (oliver:Person {name: 'Oliver Stone', role: 'Gordon Gekko'})
RETURN oliver

While there is a matching unique Person node with the name Oliver Stone, there is also another unique Person node with the role of Gordon Gekko and MERGE fails to match.

Error message
Merge did not find a matching node oliver and can not create a new node due to
conflicts with existing unique nodes

Using map parameters with MERGE

MERGE does not support map parameters the same way CREATE does. To use map parameters with MERGE, it is necessary to explicitly use the expected properties, such as in the following example. For more information on parameters, see Parameters.

Parameters
{
  "param": {
    "name": "Keanu Reeves",
    "role": "Neo"
  }
}
Query
MERGE (person:Person {name: $param.name, role: $param.role})
RETURN person.name, person.role
Table 16. Result
person.name person.role

"Keanu Reeves"

"Neo"

Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1