代码之家  ›  专栏  ›  技术社区  ›  Torsten Scholz

如何根据Postgres中的过滤条件更新深度嵌套的JSON对象?

  •  3
  • Torsten Scholz  · 技术社区  · 7 年前

    mapping_transform content_json 包含以下内容

    {
        "meta": {...},
        "mapping": [
            ...,
            {
                "src": "up",
                "dest": "down",
                ...
            },
            ...
        ]
    }
    

    "rule_names": [ "some name" ] )到JSON对象匹配 src = up dest = down ,这将导致

    {
        "meta": {...},
        "mapping": [
            ...,
            {
                "src": "up",
                "dest": "down",
                ...,
                "rule_names": [ "some name" ]
            },
            ...
        ]
    }
    

    满足过滤器要求的:

    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\u json 分配给之前 content\u json ,否则它将覆盖整个内容。

    如何根据过滤条件更新特定的深度嵌套JSON对象? 如果我有一个定义良好的路径来确定我要更新的对象的位置,事情就会容易得多。但是由于目标对象位于JSON数组中,并且具有 任意位置

    2 回复  |  直到 7 年前
        1
  •  3
  •   klin    7 年前

    如果你熟悉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);
    

    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);
    
        2
  •  1
  •   Vao Tsun    7 年前

    我在这里构建路径: concat('{mapping,',(ord::int-1),'}')::text[]

    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