2.9. Getting or creating a unique node using Cypher and uniqueness constraints

This section describes how to ensure uniqueness of a property when creating nodes.

For an overview of unique nodes, see Section 3.7, “Creating unique nodes”.

Create a unique constraint:

        try ( Transaction tx = graphdb.beginTx() )
        {
            tx.schema()
                    .constraintFor( Label.label( "User" ) )
                    .assertPropertyIsUnique( "name" )
                    .withName( "usernames" )
                    .create();
            tx.commit();
        }

Use MERGE to create a unique node:

        Node result = null;
        ResourceIterator<Node> resultIterator = null;
        try ( Transaction tx = graphDb.beginTx() )
        {
            String queryString = "MERGE (n:User {name: $name}) RETURN n";
            Map<String, Object> parameters = new HashMap<>();
            parameters.put( "name", username );
            resultIterator = tx.execute( queryString, parameters ).columnAs( "n" );
            result = resultIterator.next();
            tx.commit();
            return result;
        }

The MERGE clause will either take a read-lock on the matching node already exists, or it will take a write-lock and ensure that the current transaction is the only one creating the node.

It is technically possible to use a "lock node" or a "lock property" but this should be avoided if possible. Using the "lock node" pattern is both difficult to do correctly, and it has worse performance characteristics since it always involves a write-lock.

One might also be tempted to use Java synchronization for pessimistic locking, but this is dangerous. By mixing locks in Neo4j and in the Java runtime, it is possible to produce deadlocks that are not detectable by Neo4j. As long as all locking is done by Neo4j, all deadlocks will be detected and avoided.