通过 id 更新 Postgres 9.5 Jsonb

Updating Postgres 9.5 Jsonb by id

我已经阅读了文档和堆栈,但我太笨了,无法在不问我的具体问题的情况下得到这个。

假设我有一个对象数组存储在 jsonb 列中,例如

[{"id":1, "value":"a"], {"id":2, "value":"b"}]

如果您没有索引并且必须按 id=2 搜索,将索引 1 的值从 "b" 更改为 "c" 的最有效方法是什么?

更具体地说,我正在编写一个 react/redux/node 实时应用程序,我不想信任 redux 状态来为索引提供更新值。相反,我希望客户端发送 id=2 并让 server/database 找到该数组的索引,然后更新值。

我目前正在使用 return 索引的逻辑(例如 select 整个 jsonb 列,使用 lodash 函数查找 id=2 的索引,然后更新 jsonb_set 与 lodash 找到的索引)。

我希望有一个查询,没有服务器逻辑方式来完成这个。我试过使用子查询,但 postgres 文档没有说明如何 return 索引。

感谢您的帮助!

编辑:这是当前使用 Node.js 的数据库查询和逻辑。

let _ = require('lodash');
let id=2;
let newValue='c';
let query=`SELECT jsonb_column from table where rowid=1`;
pgQuery(query)
.then((result)=>{
    result=result[0].result // cleaning up the return object
    //result=[{"id":1, "value":"a"], {"id":2, "value":"b"}];
    let index=_.findLastIndex(result, {id}) // index=1
    let query2=`UPDATE table
               SET jsonb_column=jsonb_set(jsonb_column, '{${index}, value}', '${newValue}') 
               WHERE rowid=1` RETURNING jsonb_column

    return pgQuery(query2) 
    // returns [{"id":1, "value":"a"], {"id":2, "value":"c"}];
})

可以减少到一个 postgres 查询吗?

示例数据:

create table a_table (rowid int, jsonb_column jsonb);
insert into a_table values (1, '[{"id":1, "value":"a"}, {"id":2, "value":"b"}]');
insert into a_table values (2, '[{"id":2, "value":"a"}, {"id":1, "value":"b"}]');

你有两个选择。第一种(有点复杂),使用jsonb_array_elements(jsonb_column) with ordinality:

update a_table t1
set jsonb_column = 
        jsonb_set(
            jsonb_column, 
            array[(
                select ordinality::int- 1
                from a_table t2, jsonb_array_elements(jsonb_column) with ordinality
                where t1.rowid = t2.rowid and value->>'id' = '2')::text,
            'value'::text],
            '"c"'
        );

select * from a_table;

 rowid |                    jsonb_column                    
-------+----------------------------------------------------
     1 | [{"id": 1, "value": "a"}, {"id": 2, "value": "c"}]
     2 | [{"id": 2, "value": "c"}, {"id": 1, "value": "b"}]
(2 rows)

第二个方案(简单一点),修改连续json个元素的值并聚合结果:

update a_table t1
set jsonb_column = (
    select jsonb_agg(val)
    from (
        select case 
            when value->'id' = '2' then jsonb_set(value, '{value}', '"d"')
            else value end val
        from a_table t2, jsonb_array_elements(jsonb_column)
        where t1.rowid = t2.rowid
        ) s
    );

select * from a_table;

 rowid |                    jsonb_column                    
-------+----------------------------------------------------
     1 | [{"id": 1, "value": "a"}, {"id": 2, "value": "d"}]
     2 | [{"id": 2, "value": "d"}, {"id": 1, "value": "b"}]
(2 rows)