将新的键/值对添加到 PostgreSQL JSON 列内的嵌套数组中

Posted

技术标签:

【中文标题】将新的键/值对添加到 PostgreSQL JSON 列内的嵌套数组中【英文标题】:Add a new key/value pair into a nested array inside a PostgreSQL JSON column 【发布时间】:2022-01-18 09:11:53 【问题描述】:

使用 PostgreSQL 13.4,我有一个包含 JSON 列的表,其结构类似于以下示例:


  "username": "jsmith",
  "location": "United States",
  "posts": [
    
      "id":"1",
      "title":"Welcome",
      "newKey":true <----------- insert new key/value pair here
    ,
    
      "id":"4",
      "title":"What started it all",
      "newKey":true <----------- insert new key/value pair here
    
  ]


为了更改第一级的键,我使用了这样的简单查询

UPDATE
    sample_table_json
SET
    json = json::jsonb || '"active": true';

但这不适用于示例中的嵌套对象和数组中的对象。 如何将键/值对插入到 JSON 列中,其中包含数组中的嵌套对象?

【问题讨论】:

【参考方案1】:

您必须在指定正确路径时使用jsonb_set 函数,请参阅manual。

对于单个 json 更新:

UPDATE sample_table_json
  SET json = jsonb_set( json::jsonb
                      , 'post,0,active'
                      , 'true'
                      , true
                      )

对于一组(非常)有限的 json 更新:

UPDATE sample_table_json
   SET json = jsonb_set(jsonb_set( json::jsonb
                                 , 'post,0,active'
                                 , 'true'
                                 , true
                                 )
                       , 'post,1,active'
                       , 'true'
                       , true
                       )

对于同一json数据的更大的json更新集,可以创建jsonb_set函数的“聚合版本”:

CREATE OR REPLACE FUNCTION jsonb_set(x jsonb, y jsonb, p text[], e jsonb, b boolean)
RETURNS jsonb LANGUAGE sql AS $$
SELECT jsonb_set(COALESCE(x,y), p, e, b) ; $$ ;

CREATE OR REPLACE AGGREGATE jsonb_set_agg(x jsonb, p text[], e jsonb, b boolean)
( STYPE = jsonb, SFUNC = jsonb_set) ;

然后在迭代查询结果时使用新的聚合函数jsonb_set_agg 可以计算路径和val 字段:

SELECT jsonb_set_agg('"username": "jsmith","location": "United States","posts": ["id":"1","title":"Welcome","id":"4","title":"What started it all"]' :: jsonb
                    , l.path :: text[]
                    , to_jsonb(l.val)
                    , true)
 FROM (VALUES ('posts,0,active', 'true'), ('posts,1,active', 'true')) AS l(path, val) -- this list could be the result of a subquery

这个查询最终可以用来更新一些数据:

WITH list AS
(
SELECT id
     , jsonb_set_agg(json :: jsonb
                    , l.path :: text[]
                    , to_jsonb(l.val)
                    , true) AS res
 FROM sample_table_json
CROSS JOIN (VALUES ('posts,0,active', 'true'), ('posts,1,active', 'true')) AS l(path, val) 
GROUP BY id
)
UPDATE sample_table_json AS t
   SET json = l.res
  FROM list AS l
 WHERE t.id = l.id

在dbfiddle查看测试结果

【讨论】:

同意你的意见,谢谢你的评论。答案已更新。 这个解决方案比我的好。第一个true 必须是单引号。 但是post,1,activepost,2,active等等呢? 谢谢,看起来不错。我将第一个 true 更改为 to_jsonb(true),但更改为文本也可以,谢谢。唯一缺少的方面是迭代,因为对象的数量会有所不同。如果我将索引更改为一个不存在的数字,例如“post,100,active”,那不会只使用该键创建一个对象吗? 好吧,别介意我之前的问题。它只修改现有对象,所以我可以只使用post,1,activepost,2,active。对于较大的数组,这会很困难,但在我的情况下它可以工作,因为数组中最多只有少数对象【参考方案2】:

它变得有点复杂。遍历数组,将新的键/值对添加到每个数组元素并重新聚合数组,然后重建整个对象。

with t(j) as 
(
 values ('
  "username": "jsmith",
  "location": "United States",
  "posts": [
    
      "id":"1", "title":"Welcome", "newKey":true
    ,
    
      "id":"4", "title":"What started it all", "newKey":true
    ]
 '::jsonb)
)
select j || 
 jsonb_build_object
 (
  'posts', 
  (select jsonb_agg(je||'"active":true') from jsonb_array_elements(j->'posts') je)
 )
from t;

【讨论】:

感谢您的迭代方法。正如你所注意到的,我认为这让事情变得相当复杂。您的示例会起作用,但问题是每个对象的值都会发生变化 好吧,您可以用update 替换select 语句,而不是t(j) 模型。如果您的意思是某些值应为 "active":true 和其他 - "active":false 则用适当的表达式替换文字,也许是标量子查询。然而,更复杂的是。

以上是关于将新的键/值对添加到 PostgreSQL JSON 列内的嵌套数组中的主要内容,如果未能解决你的问题,请参考以下文章

将新的键值对推送到 json

需要使用 Objective C 将新的键和值添加到 Plist 中? [复制]

用于有效百分位查找的数据结构?

如何使用Immutable.js在嵌套Map中添加新的键/值对

将新的字典值列添加到熊猫数据框

如何从字典列表中向现有键值对添加新的键值对?