这是使用 Neo4j 编写多语句事务的正确方法吗?

agm*_*984 5 transactions neo4j node.js async-await

我很难解释 Neo4j 关于交易的文档。他们的文档似乎表明倾向于这样做,而不是明确声明tx.commit()tx.rollback()

对于多语句事务来说,这看起来是最佳实践吗neo4j-driver

const register = async (container, user) => {
    const session = driver.session()
    const timestamp = Date.now()

    const saltRounds = 10
    const pwd = await utils.bcrypt.hash(user.password, saltRounds)

    try {
        //Start registration transaction
            const registerUser = session.writeTransaction(async (transaction) => {
            const initialCommit = await transaction
                .run(`
                    CREATE (p:Person {
                        email: '${user.email}',
                        tel: '${user.tel}',
                        pwd: '${pwd}',
                        created: '${timestamp}'
                    })
                    RETURN p AS Person
                `)

            const initialResult = initialCommit.records
                .map((x) => {
                    return {
                        id: x.get('Person').identity.low,
                        created: x.get('Person').properties.created
                    }
                })
                .shift()

            //Generate serial
            const data = `${initialResult.id}${initialResult.created}`
            const serial = crypto.sha256(data)

            const finalCommit = await transaction
                .run(`
                    MATCH (p:Person)
                    WHERE p.email = '${user.email}'
                    SET p.serialNumber = '${serial}'
                    RETURN p AS Person
                `)

            const finalResult = finalCommit.records
                .map((x) => {
                    return {
                        serialNumber: x.get('Person').properties.serialNumber,
                        email: x.get('Person').properties.email,
                        tel: x.get('Person').properties.tel
                    }
                })
                .shift()

            //Merge both results for complete person data
            return Object.assign({}, initialResult, finalResult)
        })

        //Commit or rollback transaction
        return registerUser
            .then((commit) => {
                session.close()
                return commit
            })
            .catch((rollback) => {
                console.log(`Transaction problem: ${JSON.stringify(rollback, null, 2)}`)
                throw [`reg1`]
            })
    } catch (error) {
    session.close()
        throw error
    }
}
Run Code Online (Sandbox Code Playgroud)

这是逻辑的简化版本:

const register = (user) => {
    const session = driver.session()
    const performTransaction = session.writeTransaction(async (tx) => {

        const statementOne = await tx.run(queryOne)
        const resultOne = statementOne.records.map((x) => x.get('node')).slice()

        // Do some work that uses data from statementOne

        const statementTwo = await tx.run(queryTwo)
        const resultTwo = statementTwo.records.map((x) => x.get('node')).slice()

        // Do final processing

        return finalResult
    })

    return performTransaction.then((commit) => {
           session.close()
           return commit
    }).catch((rollback) => {
            throw rollback
    })
}
Run Code Online (Sandbox Code Playgroud)

Neo4j专家,上面的代码使用正确吗neo4j-driver

我宁愿这样做,因为它更加线性和同步:

const register = (user) => {
    const session = driver.session()
    const tx = session.beginTransaction()

    const statementOne = await tx.run(queryOne)
    const resultOne = statementOne.records.map((x) => x.get('node')).slice()

    // Do some work that uses data from statementOne

    const statementTwo = await tx.run(queryTwo)
    const resultTwo = statementTwo.records.map((x) => x.get('node')).slice()

    // Do final processing
    const finalResult = { obj1, ...obj2 }
    let success = true

   if (success) {
       tx.commit()
       session.close()
       return finalResult
   } else {
       tx.rollback()
       session.close()
       return false
   }
}
Run Code Online (Sandbox Code Playgroud)

对于这么长的帖子,我感到很抱歉,但我在任何地方都找不到任何参考资料,因此社区需要这些数据。

agm*_*984 6

经过大量工作后,我们为多语句事务确定了以下语法:

  1. 开始会话
  2. 开始交易
  3. 之后使用 try/catch 块(以在 catch 块中启用适当的范围)
  4. 在 try 块中执行查询
  5. 在 catch 块中回滚

 const someQuery = async () => {
     const session = Neo4J.session()
     const tx = session.beginTransaction()
     try {
         const props = {
             one: 'Bob',
             two: 'Alice'
         }
         const tx1 = await tx
             .run(`
                 MATCH (n:Node)-[r:REL]-(o:Other)
                 WHERE n.one = $props.one
                 AND n.two = $props.two
                 RETURN n AS One, o AS Two
             `, { props })
             .then((result) => {
                 return {
                     data: '...'
                 }
             })
             .catch((err) => {
                 throw 'Problem in first query. ' + e
             })

         // Do some work using tx1
         const updatedProps = {
             _id: 3,
             four: 'excellent'
         }

         const tx2 = await tx
             .run(`
                 MATCH (n:Node)
                 WHERE id(n) = toInteger($updatedProps._id)
                 SET n.four = $updatedProps.four
                 RETURN n AS One, o AS Two
             `, { updatedProps })
             .then((result) => {
                 return {
                     data: '...'
                 }
             })
             .catch((err) => {
                 throw 'Problem in second query. ' + e
             })

         // Do some work using tx2
         if (problem) throw 'Rollback ASAP.'

         await tx.commit
         session.close()
         return Object.assign({}, tx1, { tx2 })
     } catch (e) {
         tx.rollback()
         session.close()
         throw 'someQuery# ' + e
     }
 }
Run Code Online (Sandbox Code Playgroud)

我只是注意到,如果您将数字传递到 Neo4j,您应该使用 toInteger() 将它们包装在 Cypher 查询中,以便正确解析它们。

我还提供了查询参数以及如何使用它们的示例。我发现它稍微清理了代码。

除此之外,您基本上可以根据需要在事务中链接任意数量的查询,但请记住两件事:

  1. Neo4j 在事务期间写入锁定所有涉及的节点,因此如果您有多个进程都在同一节点上执行操作,您将看到一次只有一个进程可以完成事务。我们制定了自己的业务逻辑来处理写入问题,甚至选择不使用事务。到目前为止,它运行得非常好,在大约 30 秒内编写了 100,000 个节点并创建了 100,000 个关系,分布在 10 个进程中。一笔交易花费了 10 倍的时间。我们使用时不会遇到死锁或竞争条件UNWIND
  2. 你必须等待,tx.commit()否则它不会在它破坏会话之前提交。

我的观点是,如果您使用 Polyglot(多个数据库)并且需要创建一个节点,然后将文档写入 MongoDB,然后在节点上设置 Mongo ID,那么这种类型的事务非常有效。

它很容易推理,并根据需要进行扩展。