Search code examples
javascriptnode.jsneo4jcypher

Why does the 'MERGE' statement in Node.js still create duplicate nodes in Neo4j?


I'm currently working on a Node.js project that involves creating nodes in Neo4j using the 'MERGE' statement. However, I've noticed that even when I use 'MERGE' to ensure that duplicate nodes are not created, the statement sometimes still creates duplicates.

I've checked the Neo4j documentation and searched for similar issues online, but I haven't found a solution to this problem. Can anyone help me understand why this is happening and how I can prevent it from happening in the future? I provide the code repository on here.

Any suggestions or insights would be greatly appreciated. Thank you!

Here's the code I'm using:

const neo4j = require('neo4j-driver')

const url = `neo4j://test-neo4j:7687`;

(async ()=>{
    console.log(`start connect to neo4j : ${url}`);
    const driver = neo4j.driver(
        url,
        neo4j.auth.basic('neo4j', 'nodejs_to_neo4j')
    )


    const endFn = (session)=>{
        setTimeout(()=>{
            session.close();
            driver.close();

            console.log(`end connect to neo4j : ${url}`);
        }, 3000);
    };

    setTimeout(async ()=>{

        for(let i= 0; i < 10 ; i++){
            const session = driver.session();
            let userId = '123';
            let nameParam = 'John Doe';

            const query = 'MERGE (n:Person {user_id: $userId,  name: $nameParam}) RETURN n';
            console.log(
                `query(${i + 1}): ${query}`
                .replace('$userId', userId)
                .replace('$nameParam', nameParam)
            );

            let dictResult = session.run(query, { userId, nameParam });

            if(i === 9) {
                dictResult.then(endFn(session));
            } else {
                dictResult.then();
            }
        }
    }, 1000);

})();

I expect this code to only create a new node if a node with the same properties does not already exist in the database. However, in some cases, the code creates a new node even if a node with the same name already exists.


Solution

  • It is important to understand that MERGE on a single node pattern may create duplicates unless there is a uniqueness constraint.

    Therefore, you need to make sure you have a uniqueness constraint on :Person(user_id):

    CREATE CONSTRAINT Person_user_id FOR (p:Person) REQUIRE p.user_id IS UNIQUE;
    

    You should also change your query so that you use MERGE on only the constrained property, user_id:

    MERGE (n:Person {user_id: $userId}) ON CREATE SET n.name = $nameParam RETURN n
    

    Finally, you need to close every session, not just the last one. When you do not close a session it leaks resources. The recommended approach is to use try context blocks to ensure that every session is always closed.