RedisJSON修改数组对象状态问题求助(Node Redis客户端)
解决方案
问题根源
- JSONPath表达式错误:你使用的
$.*[?(@.status=="sent")]存在两处问题:- 数组元素通配符应为
$[*]而非$.*; - 字符串匹配的引号被HTML转义(
"),导致Redis无法识别正确的字符串值。
- 数组元素通配符应为
- 批量修改限制:Redis的
JSON.SET命令不支持通过带过滤条件的JSONPath一次性修改多个离散节点,这就是你遇到ERR new objects must be created at the root错误的核心原因。
正确实现方式
使用Lua脚本在Redis服务器端直接处理数组遍历与修改,既避免客户端多次网络请求,也能精准匹配并修改目标字段:
async updateSentToDelivered(roomId: string) { const key = `rooms:${roomId}`; // Lua脚本:遍历数组,修改status为sent的元素为delivered const luaScript = ` local key = KEYS[1] local targetStatus = ARGV[1] local newValue = ARGV[2] -- 获取完整数组 local jsonArr = redis.call('JSON.GET', key, '$') local arr = cjson.decode(jsonArr)[1] -- 取根节点的数组 -- 遍历修改符合条件的元素 local updatedCount = 0 for idx, obj in ipairs(arr) do if obj.status == targetStatus then local path = string.format('$[%d].status', idx - 1) -- Redis数组索引从0开始 redis.call('JSON.SET', key, path, newValue) updatedCount = updatedCount + 1 end end return updatedCount `; // 执行Lua脚本 const updatedCount = await this.redisProvider.publisher.eval(luaScript, { keys: [key], args: ["sent", "delivered"] }); console.log(`成功修改 ${updatedCount} 条消息状态`); }
替代方案(客户端处理)
如果不想用Lua脚本,可先获取整个数组,在客户端筛选出目标元素的索引,再逐个调用JSON.SET修改:
async updateSentToDelivered(roomId: string) { const key = `rooms:${roomId}`; // 获取完整数组 const roomMessages = await this.redisProvider.publisher.json.get(key, { path: '$' }); if (!roomMessages || !Array.isArray(roomMessages[0])) return; const messages = roomMessages[0]; for (let i = 0; i < messages.length; i++) { if (messages[i].status === "sent") { await this.chatRepository.jsonSet( key, "delivered", `$[${i}].status` ); } } }
关键注意事项
- 禁止在
JSON.SET中使用带过滤条件的JSONPath批量修改,Redis不支持此类操作; - 字符串匹配时直接使用单/双引号,无需HTML转义(如
@.status == 'sent'); - Redis数组索引从0开始,与JavaScript逻辑一致,无需额外转换。
内容的提问来源于stack exchange,提问作者Bruce Armstrong
相关产品推荐
相关产品推荐

