ES6 Async/Await,ExpressJS和Postgres事务

3

修改问题

我已经修改了问题,希望能够得到更清晰的答案。


我正在尝试在ExpressJS中处理数据,基于传入的req.body和表中的现有数据。

我收到一个包含更新字段JSON列表的req.body。其中一些字段在Postgres中存储为JSONB。如果传入的字段是JSONB,则生成补丁列表的是发出请求的表单(外部代码)已经运行了jsonpatch.compare(),并且传递的是这些补丁而不是完整值。对于任何非JSONB值,只需要将传入的值通过到UPDATE查询。

我有一个正在工作的版本,如下所示,它假装表中的现有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 = $1'

  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

我保证这是有效的代码,几乎实现了我需要的功能。但是,我想用从表格中同时获取的数据替换虚拟记录。我的问题在于,由于多个客户端可能同时更新一行(但是查看JSONB值时为正交元素),因此我需要将获取、计算和更新作为一个单一的事务进行。我的计划是:
  1. 开始一个事务
  2. 根据传入的id查询Postgres的当前行值
  3. 对于任何JSONB字段,应用补丁程序以生成UPDATE语句中该字段的正确值
  4. 使用适当的参数值运行UPDATE语句(根据字段是JSONB还是非JSONB,要么来自req.body,要么来自修补后的行)
  5. 提交事务,或在出现错误时回滚
我尝试了@midrizi的答案,也许只是我自己的问题,但是等待和纯测试res的组合使服务器进入了超空间......最终导致了超时。

为什么catch后面要加一个then - user9201277
无论发生什么事情,都要确保客户端被关闭。 - Dycey
1个回答

1

如果还有人清醒的话,这里是我的问题的一个可行解决方案。

简而言之:RTFM: 使用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 = $1'

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接