问题描述
是否有一种方法可以更新数组数组中的JSONB值而不必指定索引(该索引并不总是可靠的),而是通过一个对象内的属性来指定?
让我们说我有这个JSONB值:
select '[{"foo": [{"id": "baz"},{"id": "bar"}]},{"foo": [{"id": "bor"}]}]'::jsonb;
通过此查询,我可以找到ID为bar
的元素:
select * from x where f @> '[{"foo": [{"id": "bar"}]}]'::jsonb:
但是我不能只说:更新ID为bar
的对象并更改它,而无需指定其索引:
UPDATE x SET f = jsonb_set(f,'{0,foo,1}','{"id": "new-bar","something": "hello"}'::jsonb);
是否可以通过使用此嵌套对象的属性来更新JSONB的一部分而无需指定索引(即位置)?
侧面说明:索引位置的问题在于查询之间可能会有所不同,并且在读取日志时不可读。
不确定是否足够清楚,请告诉我是否可以改善我的问题!
解决方法
一种选择是将JSONB_ARRAY_ELEMENTS()
函数与CROSS JOIN
一起递归地应用到最深的数组元素到达的位置。
然后匹配'{"id": "bar"}::JSONB'
作为搜索标识符:
WITH x0 AS
(
SELECT j
FROM x
CROSS JOIN JSONB_ARRAY_ELEMENTS(f) AS j
),x1 AS
(
SELECT ('{foo,'||index-1||'}')::text[] AS path,x0.*
FROM x0
CROSS JOIN JSONB_ARRAY_ELEMENTS((j->>'foo')::JSONB)
WITH ORDINALITY arr(j1,index)
WHERE j1 = '{"id": "bar"}'::JSONB
),x_upd AS
(
SELECT JSONB_AGG(
JSONB_SET(x1.j,x1.path,'{"id": "new-bar","something": "hello"}',false)
) AS js_agg
FROM x1
)
UPDATE x
SET f = js_agg
FROM x_upd