ES6 Async/Await、ExpressJS 和 Postgres 事务

ES6 Async/Await, ExpressJS and Postgres transactions

修改后的问题

我修改了问题,希望得到更清楚的答案。


我正在尝试根据传入的 req.body 和 table 中的现有数据在 ExpressJS 中处理数据。

我收到 req.body,其中包含 JSON 更新字段列表。其中一些字段在 Postgres 中存储为 JSONB。如果一个传入字段是JSONB,那么发出请求的表单(外部代码)已经有运行一个jsonpatch.compare()来生成patches列表,就是这些patches和不是传入的完整值。对于任何非 JSONB 值,传入值只需要传递给 UPDATE 查询。

我有一个工作版本,如下所示,它假装 table 中现有的 JSONB 值为 NULL。显然,这不是所需要的。我需要从数据库中提取值。非查询当前值版本和最低限度的路由器,如下所示:

const express = require('express')
const bodyParser = require('body-parser')
const SQL = require('sql-template-strings')
const { Client } = require('pg')
const dbConfig = require('../db')
const jsonpatch = require('fast-json-patch')

const FormRouter = express.Router()

I have some update code:

````javascript
const patchFormsRoute = (req, res) => {
  const client = new Client(dbConfig)
  const { id } = req.body
  const parts = []
  const params = [id]

  // list of JSONB fields for the 'forms' table
  const jsonFields = [
    'sections',
    'editors',
    'descriptions',
  ]

  // list of all fields, including JSONB fields in the 'forms' table
  const possibleFields = [
    'status',
    'version',
    'detail',
    'materials',
    ...jsonFields,
  ]

  // this is a DUMMY RECORD instead of the result of a client.query 
  let currentRecord = { 'sections':[], 'editors':[], 'descriptions':[] }

  possibleFields.forEach(myProp => {
    if (req.body[myProp] != undefined) {
      parts.push(`${myProp} = $${params.length + 1}`)
      if (jsonFields.indexOf(myProp) > -1) {
        val = currentRecord[myProp]
        jsonpatch.applyPatch(val, req.body[myProp])
        params.push(JSON.stringify(val))
      } else {
        params.push(req.body[myProp])
      }
    }
  })

  const updateQuery = 'UPDATE forms SET ' + parts.join(', ') + ' WHERE id = '

  client.connect()
  return client
    .query(updateQuery, params)
    .then(result => res.status(200).json(result.rowCount))
    .catch(err => res.status(400).json(err.severity))
    .then(() => client.end())
}

FormRouter.route('/')
  .patch(bodyParser.json({ limit: '50mb' }), patchFormsRoute)

exports.FormRouter = FormRouter

我保证,这是工作代码,几乎 我需要的。但是,我想用同时获取的 table 中的数据替换虚拟记录。我的问题是,因为多个客户端可能同时更新一行(但查看 JSONB 值的正交元素),我需要获取、计算和更新作为单个事务发生。我的计划是:

  1. 开始交易

  2. 查询Postgres当前行值,根据传入id

  3. 对于任何 JSONB 字段,应用补丁以在 UPDATE 语句中为该字段生成正确的值。

  4. 运行 具有适当参数值的 UPDATE 语句(来自 req.body 或修补的行,取决于字段是 JSONB 还是不)

  5. 提交事务,或在出错时回滚。

我已经尝试实现@midrizi 的答案;也许这只是我,但是 res 的等待和简单测试的组合将服务器发送到超空间...并以超时结束。

如果有人还醒着,这里有一个解决我的问题的有效方法。

TLDR; RTFM:A pooled client with async/await 减去池化(目前)。

const patchFormsRoute = (req, res) => {
  const { id } = req.body
  // list of JSONB fields for the 'forms' table
  const jsonFields = [
    'sections',
    'editors',
    'descriptions',
  ]

  // list of all fields, including JSONB fields in the 'forms' table
  const possibleFields = [
    'status',
    'version',
    'detail',
    'materials',
    ...jsonFields,
  ]
  const parts = []
  const params = [id]

  ;(async () => {
    const client = await new Client(dbConfig)
    await client.connect()
    try {
      // begin a transaction
      await client.query('BEGIN')

      // get the current form data from DB
      const fetchResult = await client.query(
        SQL`SELECT * FROM forms WHERE id = ${id}`,
      )

      if (fetchResult.rowCount === 0) {
        res.status(400).json(0)
        await client.query('ROLLBACK')
      } else {
        const currentRecord = fetchResult.rows[0]

        // patch JSONB values or update non-JSONB values
        let val = []

        possibleFields.forEach(myProp => {
          if (req.body[myProp] != undefined) {
            parts.push(`${myProp} = $${params.length + 1}`)
            if (jsonFields.indexOf(myProp) > -1) {
              val = currentRecord[myProp]
              jsonpatch.applyPatch(val, req.body[myProp])
              params.push(JSON.stringify(val))
            } else {
              params.push(req.body[myProp])
            }
          }
        })

        const updateQuery =
          'UPDATE forms SET ' + parts.join(', ') + ' WHERE id = '

        // update record in DB
        const result = await client.query(updateQuery, params)

        // commit transaction
        await client.query('COMMIT')

        res.status(200).json(result.rowCount)
      }
    } catch (err) {
      await client.query('ROLLBACK')
      res.status(400).json(err.severity)
      throw err
    } finally {
      client.end()
    }
  })().catch(err => console.error(err.stack))
}