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

时间:2017-08-15 18:42:47

标签: node.js neo4j transactions 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
    }
}
  

以下是逻辑的简化版本:

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
    })
}

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
   }
}

我很抱歉这篇长篇文章,但我找不到任何参考资料,所以社区需要这些数据。

1 个答案:

答案 0 :(得分:1)

经过更多的工作,这是我们为多语句事务确定的语法:

  1. 开始会话
  2. 开始交易
  3. 之后使用try / catch块(在catch块中启用适当的范围)
  4. 在try块中执行查询
  5. 在catch块中回滚
  6.  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
         }
     }
    

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

    我还包括了查询参数的示例以及如何使用它们。我发现它清理了一些代码。

    除此之外,您基本上可以根据需要在事务中链接尽可能多的查询,但请记住以下两点:

    1. Neo4j在事务期间对所有涉及的节点进行写锁定,因此如果您有多个进程都在同一节点上执行操作,您将看到一次只有一个进程可以完成一个事务。我们制定了自己的业务逻辑来处理写入问题,并选择甚至不使用事务。到目前为止,它运行良好,编写100,000个节点,并在大约30秒内创建100,000个关系,分布在10个进程中。在交易中花了10倍的时间。我们使用UNWIND没有遇到死锁或竞争条件。
    2. 您需要等待tx.commit()或者在核对会话之前不会提交。
    3. 我的观点是,如果你使用Polyglot(多个数据库)并且需要创建一个节点,然后将文档写入MongoDB然后在节点上设置Mongo ID,这种类型的事务会很有效。

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