从 React->ExpressJS->Postgres 修补 Postgres JSONB 列的最佳方法是什么?
What's the best way to PATCH Postgres JSONB column from React->ExpressJS->Postgres?
我有大量 table 数据(使用 AG-Grid 呈现),我想在 Postgres 后端更新它,但下一部分的最佳方法让我搪塞,就数量而言工作,以及最佳行动方案。
使用 fast-json-patch
库,我可以在客户端中轻松获得 JSON 补丁列表,然后大致如下:
import * as jsonpatch from 'fast-json-patch'
postData = jsonpatch.compare(originalData, updatedData)
const request = new Request(url, {
method: 'PATCH',
body: JSON.stringify(postData),
headers: new Headers({
Accept: 'application/json',
'Content-Type': 'application/json-patch',
Authorization: 'Bearer ' + user.token,
}),
})
然后在 ExpressJS 'backend' 中迭代一堆 jsonb_set
查询来更新 Postgres。
或者,我可以从 Postgres 中获取要更新的记录,然后使用 fast-json-patch
修补 ExpressJS 后端中的 JSONB 数据,然后一次性更新 Postgres 记录?
这不是我以前做过的事情,但我确信这种事情一定很常见。最好的通用方法是什么?
更新
我已经尝试实施第二种方法 - 我现在的问题是 locking/unlocking Postgres 当我有 JSONB 字段要更新时。我现在的问题是从 express 端实际实现记录锁定和更新,特别是尝试处理使用 pg 后端的异步性质。
我只是想知道是否有人能发现这种笨手笨脚的尝试中的(非故意的)错误:
const express = require('express')
const bodyParser = require('body-parser')
const SQL = require('sql-template-strings')
const { Client } = require('pg')
const dbConfig = require('../db')
const client = new Client(dbConfig)
const jsonpatch = require('fast-json-patch')
// excerpt for patch records in 'forms' postgres table
const patchFormsRoute = (req, res) => {
const { id } = req.body
const jsonFields = [ 'sections', 'descriptions' ]
const possibleFields = [ 'name','status',...jsonFields ]
const parts = []
const params = [id] // stick id in as first param
let lockInUse = false
// find which JSONB fields are being PATCHed.
// incoming JSONB field values are already JSON
// arrays of patches to apply for that particular field
const patchList = Object.keys(req.body)
.filter(e => jsonFields.indexOf(e) > -1)
client.connect()
if (patchList.length > 0) {
const patchesToApply = pullProps(req.body, jsonFields)
lockInUse = getLock('forms',id)
// return record from pg as object with just JSONB field values
const oldValues = getCurrentValues(patchList, id)
// returns record with patches applied
const patchedValues = patchValues( oldValues , patchesToApply )
}
possibleFields.forEach(myProp => {
if (req.body[myProp] != undefined) {
parts.push(`${myProp} = $${params.length + 1}`)
if (jsonFields.indexOf(myProp) > -1) {
params.push(JSON.stringify(patchedValues[myProp]))
} else {
params.push(req.body[myProp])
}
}
})
result = runUpdate(client, 'forms', parts, params)
if(lockInUse) {
releaseLock(client, 'forms', id)
}
client.end()
return result
}
// helper functions to try and cope with async nature of pg
function async getLock(client, tableName, id ) {
await client.query(SQL`SELECT pg_advisory_lock(${tableName}::regclass::integer, ${id});`)
return true
}
function async releaseLock(client, tableName, id) {
await client.query(SQL`SELECT pg_advisory_unlock(${tableName}::regclass::integer, ${id});`)
}
function async getCurrentValues(client, fieldList, id) {
const fl = fieldList.join(', ')
const currentValues = await client
.query(SQL`SELECT ${fl} FROM forms WHERE id = ${id}`)
.then((result) => {return result.rows[0]})
return currentValues
}
function pullProps(sourceObject, propList) {
return propList.reduce((result, propName) => {
if(sourceObject.hasOwnProperty(propName)) result[propName] = sourceObject[propName]
return result
}, {})
}
function patchValues(oldValues, patches) {
const result = {}
Object.keys(oldValues).forEach(e => {
result[e] = jsonpatch.apply( oldValues[e], patches[e] );
})
return result
}
function async runUpdate(client, tableName, parts, params) {
const updateQuery = 'UPDATE ' + tableName + ' SET ' + parts.join(', ') + ' WHERE id = '
const result = await client
.query(updateQuery, params)
.then(result => {
res.json(result.rowCount)
})
return result
}
使用第二种方法。 PostgreSQL 没有针对 JSONB 的就地编辑功能。它总是会包括制作完整的副本。您也可以在客户端中执行此操作,这似乎有更好的工具。
如果补丁很小并且 JSONB 很大并且您的网络很慢,则可能是一个例外。
我有大量 table 数据(使用 AG-Grid 呈现),我想在 Postgres 后端更新它,但下一部分的最佳方法让我搪塞,就数量而言工作,以及最佳行动方案。
使用 fast-json-patch
库,我可以在客户端中轻松获得 JSON 补丁列表,然后大致如下:
import * as jsonpatch from 'fast-json-patch'
postData = jsonpatch.compare(originalData, updatedData)
const request = new Request(url, {
method: 'PATCH',
body: JSON.stringify(postData),
headers: new Headers({
Accept: 'application/json',
'Content-Type': 'application/json-patch',
Authorization: 'Bearer ' + user.token,
}),
})
然后在 ExpressJS 'backend' 中迭代一堆 jsonb_set
查询来更新 Postgres。
或者,我可以从 Postgres 中获取要更新的记录,然后使用 fast-json-patch
修补 ExpressJS 后端中的 JSONB 数据,然后一次性更新 Postgres 记录?
这不是我以前做过的事情,但我确信这种事情一定很常见。最好的通用方法是什么?
更新
我已经尝试实施第二种方法 - 我现在的问题是 locking/unlocking Postgres 当我有 JSONB 字段要更新时。我现在的问题是从 express 端实际实现记录锁定和更新,特别是尝试处理使用 pg 后端的异步性质。
我只是想知道是否有人能发现这种笨手笨脚的尝试中的(非故意的)错误:
const express = require('express')
const bodyParser = require('body-parser')
const SQL = require('sql-template-strings')
const { Client } = require('pg')
const dbConfig = require('../db')
const client = new Client(dbConfig)
const jsonpatch = require('fast-json-patch')
// excerpt for patch records in 'forms' postgres table
const patchFormsRoute = (req, res) => {
const { id } = req.body
const jsonFields = [ 'sections', 'descriptions' ]
const possibleFields = [ 'name','status',...jsonFields ]
const parts = []
const params = [id] // stick id in as first param
let lockInUse = false
// find which JSONB fields are being PATCHed.
// incoming JSONB field values are already JSON
// arrays of patches to apply for that particular field
const patchList = Object.keys(req.body)
.filter(e => jsonFields.indexOf(e) > -1)
client.connect()
if (patchList.length > 0) {
const patchesToApply = pullProps(req.body, jsonFields)
lockInUse = getLock('forms',id)
// return record from pg as object with just JSONB field values
const oldValues = getCurrentValues(patchList, id)
// returns record with patches applied
const patchedValues = patchValues( oldValues , patchesToApply )
}
possibleFields.forEach(myProp => {
if (req.body[myProp] != undefined) {
parts.push(`${myProp} = $${params.length + 1}`)
if (jsonFields.indexOf(myProp) > -1) {
params.push(JSON.stringify(patchedValues[myProp]))
} else {
params.push(req.body[myProp])
}
}
})
result = runUpdate(client, 'forms', parts, params)
if(lockInUse) {
releaseLock(client, 'forms', id)
}
client.end()
return result
}
// helper functions to try and cope with async nature of pg
function async getLock(client, tableName, id ) {
await client.query(SQL`SELECT pg_advisory_lock(${tableName}::regclass::integer, ${id});`)
return true
}
function async releaseLock(client, tableName, id) {
await client.query(SQL`SELECT pg_advisory_unlock(${tableName}::regclass::integer, ${id});`)
}
function async getCurrentValues(client, fieldList, id) {
const fl = fieldList.join(', ')
const currentValues = await client
.query(SQL`SELECT ${fl} FROM forms WHERE id = ${id}`)
.then((result) => {return result.rows[0]})
return currentValues
}
function pullProps(sourceObject, propList) {
return propList.reduce((result, propName) => {
if(sourceObject.hasOwnProperty(propName)) result[propName] = sourceObject[propName]
return result
}, {})
}
function patchValues(oldValues, patches) {
const result = {}
Object.keys(oldValues).forEach(e => {
result[e] = jsonpatch.apply( oldValues[e], patches[e] );
})
return result
}
function async runUpdate(client, tableName, parts, params) {
const updateQuery = 'UPDATE ' + tableName + ' SET ' + parts.join(', ') + ' WHERE id = '
const result = await client
.query(updateQuery, params)
.then(result => {
res.json(result.rowCount)
})
return result
}
使用第二种方法。 PostgreSQL 没有针对 JSONB 的就地编辑功能。它总是会包括制作完整的副本。您也可以在客户端中执行此操作,这似乎有更好的工具。
如果补丁很小并且 JSONB 很大并且您的网络很慢,则可能是一个例外。