【问题标题】:Is this the proper way to write a multi-statement transaction with Neo4j?这是用 Neo4j 编写多语句事务的正确方法吗?
【发布时间】:2017-09-04 11:34:03
【问题描述】:

我很难解释 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
   }
}

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

【问题讨论】:

    标签: node.js neo4j transactions async-await


    【解决方案1】:

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

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

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

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

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

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

    我的看法是,如果你使用的是Polyglot(多个数据库)并且需要创建一个节点,然后将文档写入MongoDB,然后在节点上设置Mongo ID,这种类型的事务效果很好。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-02
      • 1970-01-01
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      相关资源
      最近更新 更多