更新数组中的JSONB对象而不在PostgreSQL中指定索引

问题描述

是否有一种方法可以更新数组数组中的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

Demo