跨多个记录更新 postgresql jsonb 对象数组中的属性值

Update the value of an attribute in a postgresql jsonb array of objects across muliple records

我正在尝试更新作为 jsonb 数组对象一部分的三个数据库记录中的值 tit,以使用 postgresql 和 python.

create table groups (name varchar, grp jsonb)
 insert into groups (name, grp) values 
  ('joe', [{"gid": "1", "ona": "joe", "tit": "group 1 "}, {"gid": "2", "ona": "harry", "tit": "tester1 group 2"}, {"gid": "3", "ona": "moe", "tit": "group 3"}]),
  ('harry', [{"gid": "1", "ona": "joe", "tit": "group 1 "}, {"gid": "2", "ona": "harry", "tit": "tester1 group 2"}, {"gid": "3", "ona": "moe", "tit": "group 3"}])
  ('moe' , [{"gid": "1", "ona": "joe", "tit": "group 1 "}, {"gid": "2", "ona": "harry", "tit": "tester1 group 2"}, {"gid": "3", "ona": "moe", "tit": "group 3"}])

我要得到结果:

  ('joe', [{"gid": "1", "ona": "joe", "tit": "newTitle Group 1'"}, {"gid": "2", "ona": "harry", "tit": "tester1 group 2"}, {"gid": "3", "ona": "moe", "tit": "group 3"}]),
  ('harry', [{"gid": "1", "ona": "joe", "tit": "newTitle Group 1'"}, {"gid": "2", "ona": "harry", "tit": "tester1 group 2"}, {"gid": "3", "ona": "moe", "tit": "group 3"}])
  ('moe' , [{"gid": "1", "ona": "joe", "tit": "newTitle Group 1'"}, {"gid": "2", "ona": "harry", "tit": "tester1 group 2"}, {"gid": "3", "ona": "moe", "tit": "group 3"}])

我还没有找到方法,请帮忙。似乎大多数 postgresql 9.5 功能都是针对 jsonb 对象而不是对象数组。

我尝试使用网络上的许多示例,但似乎都失败了。我也会考虑在实际查询中使用 where 子句语句来限制正在搜索的 jsonb 记录,例如包括 WHERE name IN ('harry', 'moe') 但现在只需要看看这个基本更新问题是如何解决的。

只需要在数据库中使用REPLACE()函数,包括::text::jsonb转换,不需要python 代码中的任何额外操作:

UPDATE groups
   SET grp = REPLACE(grp::text, '"tit": "group 1 "','"tit": "newTitle Group 1 "')::jsonb

Demo

当然可以加where条件如WHERE name IN ('harry', 'moe')限制更新

更新 1:

如果您需要对 jsonb 对象中的特定记录执行更新 比如

WHERE j->>'ona' = 'joe',然后在语句中使用 jsonb_array_elements() 函数作为 :

UPDATE groups AS g
   SET grp = REPLACE(grp::text, '"tit": "group 1 "','"tit": "newTitle Group 1 "')::json
 WHERE g.name IN ( SELECT g.name 
                     FROM groups AS g 
                    CROSS JOIN jsonb_array_elements(grp) AS j 
                    WHERE j.value->>'ona' = 'joe' ) 

Demo

更新 2:

如果您想在 jsonb 列中动态找到所需的值(gid=1 在本例中)以导出路径,则可以使用jsonb_set() 功能为:

WITH T AS
(
 SELECT ('{'||index-1||',tit}')::text[] AS path
   FROM groups AS g2 
  CROSS JOIN jsonb_array_elements(grp) 
   WITH ORDINALITY arr(j,index)
  WHERE j->>'gid'='1' 
)
UPDATE groups AS g
   SET grp = jsonb_set(grp,t.path,'"newTitle Group 1 "',false)
  FROM t

Demo