我有一个mapping_transform带有JSONB列的表,content_json其中包含类似
mapping_transform
content_json
{ "meta": {...}, "mapping": [ ..., { "src": "up", "dest": "down", ... }, ... ] }
我想向"rule_names": [ "some name" ]匹配src=up和dest=的JSON对象添加一个新的JSON条目()down,这将导致
"rule_names": [ "some name" ]
src
up
dest
down
{ "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
results
jsonb_set
content_json``content_json
如何根据过滤条件更新特定的深层嵌套JSON对象?如果我有一个明确的路径来确定要更新的对象,那么事情会容易得多。但是由于目标对象位于JSON数组内并且具有 任意位置 ,因此查找和更新目标要困难得多。
如果您熟悉JavaScript,将很乐意安装和使用JavaScript过程语言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用户:准备安装Windows二进制文件。
一个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);