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 值的正交元素),我需要获取、计算和更新作为单个事务发生。我的计划是:
开始交易
查询Postgres当前行值,根据传入id
对于任何 JSONB 字段,应用补丁以在 UPDATE 语句中为该字段生成正确的值。
运行 具有适当参数值的 UPDATE 语句(来自 req.body
或修补的行,取决于字段是 JSONB 还是不)
提交事务,或在出错时回滚。
我已经尝试实现@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))
}
修改后的问题
我修改了问题,希望得到更清楚的答案。
我正在尝试根据传入的 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 值的正交元素),我需要获取、计算和更新作为单个事务发生。我的计划是:
开始交易
查询Postgres当前行值,根据传入
id
对于任何 JSONB 字段,应用补丁以在 UPDATE 语句中为该字段生成正确的值。
运行 具有适当参数值的 UPDATE 语句(来自
req.body
或修补的行,取决于字段是 JSONB 还是不)提交事务,或在出错时回滚。
我已经尝试实现@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))
}