我有一个表mapping_transform
,其中包含JSONB列content_json
,其中包含
{
"meta": {...},
"mapping": [
...,
{
"src": "up",
"dest": "down",
...
},
...
]
}
我想将新的JSON条目("rule_names": [ "some name" ]
)添加到与src
= up
和dest
= down
匹配的JSON对象中,这将导致在
{
"meta": {...},
"mapping": [
...,
{
"src": "up",
"dest": "down",
...,
"rule_names": [ "some name" ]
},
...
]
}
以下查询返回符合过滤器要求的 JSON对象:
WITH elems AS (SELECT json_array_elements(content_json->'mapping') from mapping_transform)
SELECT * FROM elems WHERE json_array_elements->>'src' = 'up' and json_array_elements->>'dest' = 'down';
-- Alternative
SELECT mt_entry
FROM mapping_transform,
LATERAL jsonb_array_elements(content_json::jsonb->'mapping') mt_entry
WHERE mt_entry->>'src' = 'up' and mt_entry->>'dest' = 'down';
我现在的问题是我不知道如何将新条目添加到特定对象。我试过像
这样的东西WITH elems AS (SELECT json_array_elements(content_json->'mapping') from mapping_transform),
results SELECT * FROM elems WHERE json_array_elements->>'src' = 'up' and json_array_elements->>'dest' = 'down'
UPDATE mapping_transform
SET content_json = jsonb_set(results, '{"rule_names"}', '["some name"]'); -- this does obviously not work
但由于results
是未知列,因此无法执行。我还需要在分配给jsonb_set
之前将content_json
的结果与content_json
的其余部分合并,否则它会覆盖整个内容。
如何根据过滤条件更新特定的深层嵌套JSON对象? 如果我有一个定义良好的路径,我想要更新我的对象,那么事情就会容易得多。但由于目标对象位于JSON数组中且具有任意位置,因此查找和更新它会更加困难。
答案 0 :(得分:3)
如果您熟悉JavaScript,您将乐意安装和使用JavaScript procedural language plv8.此扩展允许您原生修改json值,例如:
create extension if not exists plv8;
create or replace function update_mapping_v8(data json)
returns json language plv8 as $$
var len = data['mapping'].length;
for (var i = 0; i < len; i++) {
var o = data['mapping'][i];
if (o.src == 'up' && o.dest == 'down') {
o.rule_names = 'some name'
}
}
return data;
$$;
update mapping_transform
set content_json = update_mapping_v8(content_json);
对于MS Windows用户:ready to install Windows binaries.
plpgsql替代解决方案使用jsonb类型:
create or replace function update_mapping_plpgsql(data jsonb)
returns json language plpgsql as $$
declare
r record;
begin
for r in
select value, ordinality- 1 as pos
from jsonb_array_elements(data->'mapping') with ordinality
where value->>'src' = 'up' and value->>'dest' = 'down'
loop
data = jsonb_set(
data,
array['mapping', r.pos::text],
r.value || '{"rule_names": "some name"}'
);
end loop;
return data;
end $$;
update mapping_transform
set content_json = update_mapping_plpgsql(content_json::jsonb);
答案 1 :(得分:1)
我在这里建立路径:concat('{mapping,',(ord::int-1),'}')::text[]
其余的相同。请注意我加入text = text(因为我不知道你的PK是什么 - 不推荐)。将值保留为更新,右侧原始:
vao=# with num as (select content_json,val,ord from mapping_transform, json_array_elements(content_json->'mapping') with ordinality as o (val,ord) where val->>'src' = 'up')
select
jsonb_pretty(
jsonb_set(t.content_json::jsonb,concat('{mapping,',(ord::int-1),'}')::text[],((t.content_json->'mapping'->(ord::int-1))::jsonb||'{"rule_names":["some name"]}')::jsonb)
)
, jsonb_pretty(t.content_json::jsonb)
from mapping_transform t
join num on num.content_json::text = t.content_json::text
/* of course join should be on PK, not text representation*/
;
jsonb_pretty | jsonb_pretty
-----------------------------+----------------------------
{ +| { +
"meta": { +| "meta": { +
"a": true +| "a": true +
}, +| }, +
"mapping": [ +| "mapping": [ +
"a", +| "a", +
"c", +| "c", +
{ +| { +
"a": 0, +| "a": 0, +
"src": "up", +| "src": "up", +
"dest": "down",+| "dest": "down"+
"rule_names": [+| }, +
"some name"+| "b" +
] +| ] +
}, +| }
"b" +|
] +|
} |
{ +| { +
"meta": { +| "meta": { +
"a": true +| "a": true +
}, +| }, +
"mapping": [ +| "mapping": [ +
"a", +| "a", +
{ +| { +
"a": 0, +| "a": 0, +
"src": "up", +| "src": "up", +
"dest": "down",+| "dest": "down"+
"rule_names": [+| }, +
"some name"+| "b" +
] +| ] +
}, +| }
"b" +|
] +|
} |
(2 rows)
和构建:
vao=# create table mapping_transform(content_json jsonb);
CREATE TABLE
vao=# insert into mapping_transform select '{
"meta": {
"a": true
},
"mapping": ["a",{
"src": "up",
"dest": "down",
"a": 0
},
"b"
]
}';
INSERT 0 1
vao=# insert into mapping_transform select '{
"meta": {
"a": true
},
"mapping": ["a","c",{
"src": "up",
"dest": "down",
"a": 0
},
"b"
]
}';
INSERT 0 1