将新的键/值对添加到 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,active
、post,2,active
等等呢?
谢谢,看起来不错。我将第一个 true
更改为 to_jsonb(true)
,但更改为文本也可以,谢谢。唯一缺少的方面是迭代,因为对象的数量会有所不同。如果我将索引更改为一个不存在的数字,例如“post,100,active”,那不会只使用该键创建一个对象吗?
好吧,别介意我之前的问题。它只修改现有对象,所以我可以只使用post,1,active
、post,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 列内的嵌套数组中的主要内容,如果未能解决你的问题,请参考以下文章
需要使用 Objective C 将新的键和值添加到 Plist 中? [复制]