rayman
rayman

Reputation: 21596

How to create new node only if node is not existed in neo4j

I am using neo4j and vertx.

I am using neo4j-jdbc.

I managed to create user but how I can "upgrade" this query to create new users(with unique Index) only if that user isnt existed. The userId should be the primary key.

 private final Logger logger = Logger.getLogger(Neo4jRepo.class);
    private Connection conn = null;

    public Neo4jRepo() {
        logger.debug("Neo4jRepo, Init");
        try {
            Class.forName("org.neo4j.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:neo4j://localhost:7474/");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);

        }
    }
..
public boolean addDistanceUserListByForUserId(String userId) {
        try {
            final PreparedStatement ps = conn.prepareStatement( "create (n:User {name:{1}})" );
            ps.setString( 1, "userId" );
            ps.execute();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

Thank you, ray.

Upvotes: 2

Views: 2251

Answers (2)

cybersam
cybersam

Reputation: 66999

Use MERGE instead of CREATE:

"MERGE (n:User {name:{1}})"

[UPDATE 1]

The following is an example of how to modify addDistanceUserListByForUserId() so that it returns a Map<String, Object> containing the properties of the created/existing User.

public Map<String, Object> addDistanceUserListByForUserId(String userId) {
    try {
        final PreparedStatement ps = conn.prepareStatement(
            "MERGE (n:User {name:{1}}) RETURN n" );
        ps.setString( 1, "userId" );
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            return (Map<String, Object>) rs.getObject("n");
        }
    } catch (SQLException e) { 
        throw new RuntimeException(e);
    } 
    return null; 
}

[UPDATE 2]

In your specific case, since you have a uniqueness constraint on :User(name), and existing User nodes can have properties other than name -- the simple query MERGE (n:User {name:{1}}) can cause a ConstraintViolation error if an existing User with the same name exists but also has other properties.

To get around that, try replacing the MERGE statement with this more complex query:

OPTIONAL MATCH (n:User { name:{1} })
WITH (CASE WHEN n IS NULL THEN [1] ELSE [] END ) AS todo
FOREACH (x IN todo | CREATE (:User { name:{1} }))
WITH todo
MATCH (n:User { name:{1} })
RETURN n;

Here is an explanation of this query:

  • It uses OPTIONAL MATCH so that if the User with the specified name is not found, the rest of the query is not skipped (but n will be NULL).
  • It then creates a todo collection that will tell the FOREACH clause whether it should create the new User node. The FOREACH clause does nothing if the todo collection is empty.
  • There needs to be a WITH clause between a modifying clause (like FOREACH) and a subsequent MATCH clause. We don't really need to pass anything forward, so we just use todo since we already have it handy.
  • We need another MATCH query because we want to return the node that was either found, or that we just created. But there is no way to get the node that was created by the FOREACH unless we do another MATCH.

Upvotes: 3

FylmTM
FylmTM

Reputation: 1997

You should use MERGE instead of CREATE. This will create node if it doesn't exists. If node already exists - then MERGE will act like normal MATCH operation.

Important: you should use only properties with unique constraint in {} properties syntax. Neo4j will merge nodes which have same properties in {}.

If you have additional data to be set on node after merge, then:

MERGE (user:User {name: "John", age: 20}) - WRONG way. Because there are no node in database with such name and age property combination and database will try to create that node. And this will result in ConstraintViolation because such name already exists.

Correct version:

MERGE (user:User {name: "John"}) 
SET user.age = 20

This version will search node (and create if needed) at first, and only then then set property on that node.

Upvotes: 4

Related Questions