基于Postgres、Knex/Express的批量更新及历史记录追加问题
如何在Knex中批量向JSON数组字段追加新记录
你已经实现了资产状态和位置的批量更新逻辑,现在要在不覆盖原有数据的前提下,向history JSON数组字段追加新操作记录,以下是针对不同数据库的解决方案:
针对PostgreSQL(你的代码中使用了->>操作符,推测是PostgreSQL)
PostgreSQL支持直接通过||操作符向JSONB数组追加元素,同时用COALESCE处理字段为null的情况(自动转为空数组)。修改后的代码如下:
async function bulkUpdate(req, res){ const site = req.body.data; // 构造新的操作记录,根据实际业务填充字段值 const newHistoryEntry = { action_date: new Date().toISOString(), action_taken: "Pending Transfer", action_by: req.user?.name || "Unknown", // 从请求上下文获取操作人 action_by_id: req.user?.id || 0, action_comment: `批量更新站点状态为待转移:${site.physical_site_name}`, action_key: require('uuid').v4() // 生成唯一操作标识(需先安装uuid库) }; const data = await knex('assets') // 修复SQL注入风险:用参数绑定替代字符串拼接 .whereRaw(`location ->> 'site' = ?`, [site.physical_site_name]) .update({ status: "Pending Transfer", location: { site: site.physical_site_name, site_loc: { first_octet: site.first_octet, mdc: '', shelf: '', unit: ''} }, // 追加新记录到history数组,处理null情况 history: knex.raw(`COALESCE(history, '[]'::jsonb) || ?::jsonb`, [newHistoryEntry]) }) .returning('*') .then(results => results[0]); res.status(200).json({ data }); }
关键说明:
- 防SQL注入:原代码中直接拼接字符串到
whereRaw存在注入风险,改为参数绑定?后Knex会自动处理转义 - 处理null值:
COALESCE(history, '[]'::jsonb)确保当history为null时,先转为空数组再追加新元素 - JSONB类型:确保
history字段在PostgreSQL中是jsonb类型(比json更适合更新操作)
针对MySQL
如果使用MySQL,需要用JSON_ARRAY_APPEND函数,同时用IFNULL处理空值:
// 替换update中的history字段更新逻辑 history: knex.raw(`JSON_ARRAY_APPEND(IFNULL(history, '[]'), '$', ?)`, [newHistoryEntry])
注意事项:
- MySQL中
history字段需设为json类型 JSON_ARRAY_APPEND的'$'参数表示向数组根节点追加元素
内容的提问来源于stack exchange,提问作者Treesap
相关产品推荐
相关产品推荐

