如何基于Postgres中的特定键/值对更新JSON数组中的对象

时间:2020-10-02 01:17:41

标签: sql json postgresql

我有如下数据:

id  name    blocks
1   Test 1  [{"name": "Bob", "type": "INFO", "title": "CFO"}, {"type": "FOOTER"}]
2   Another [{"name": "Bob", "type": "INFO", "title": "Manager"}, {"type": "FOOTER"}]
3   Final   [{"id": 22, "type": "IMAGE"}]

用户可能希望在所有行上更改其名称,因此我需要Postgres JSON版本为:

UPDATE blocks SET name = 'New Name' WHERE type = 'INFO';

如何编写查询以在对象的这些数组中搜索值type的特定"INFO"键?这里最大的收获是这些对象可以在数组中以任何顺序排列。因此,INFO块不一定总是位于索引0。

2 个答案:

答案 0 :(得分:1)

您的示例中有2个name字段。

如果要根据您的条件更新name字段中的json标记,请尝试以下查询:

with cte as 
(
select 
id,
name,
json_agg(case when t.x->>'type'='INFO' then jsonb_set(t.x,'{name}','"New Name"',false) else t.x end) "new_value"
from blocks cross join lateral jsonb_array_elements(blocks::jsonb) t(x)
group by 1,2
)
update blocks t1 
set blocks=t2.new_value
from cte t2 
where t1.id=t2.id;

DEMO

如果要根据条件更新表的name字段,请尝试以下查询:

with cte as 
(
select 
id,
name,
bool_or(t.x->>'type'='INFO') "new_value"
from blocks cross join lateral jsonb_array_elements(blocks::jsonb) t(x)
group by 1,2
)
update blocks t1 
set name='New Name'
from cte t2 
where t1.id=t2.id and t2.new_value=true;

DEMO

答案 1 :(得分:1)

这最终是我所用的(基于公认的答案),只是清理了一下,并使用实际的表名和JSONB结构进行了命名。这还可以确保用户只能批量更新自己的数据,而不能批量更新其他人的数据。

WITH new_data AS (
  SELECT id, json_agg(
    CASE
      WHEN blocks.block->>'type' = 'CONTACT_INFO'
        THEN jsonb_set(blocks.block, '{data, name}', '"New Name"', false)
      ELSE
        blocks.block
    END) AS new_block
  FROM reels
  CROSS JOIN LATERAL jsonb_array_elements(blocks) blocks(block)
  WHERE owner_id = '9292dcc6-f906-418a-aee0-074b297bfb52'
  GROUP BY id
)
UPDATE reels
SET blocks = new_data.new_block
FROM new_data
WHERE reels.id = new_data.id;