Update or create nested jsonb value using single update command

后端 未结 2 1892
时光说笑
时光说笑 2021-02-05 17:56

Let us say I have [in Postgres 9.6] a JSONB column named xyz. In an update, I want to set the .foo.bar key of this column to {\"

相关标签:
2条回答
  • 2021-02-05 18:07

    You can just use || to concatenate. It will overwrite or add any json value.

    SELECT '{}'::jsonb || '{"foo":"bar"}'::jsonb
    UPDATE tablename SET jdoc = jdoc || '{"foo":"bar"}'::jsonb
    

    It's that easy. I rarely use the functions in my software.

    In the case of merging:

    create or replace function jsonb_merge(orig jsonb, delta jsonb)
    returns jsonb language sql as $$
        select
            jsonb_object_agg(
                coalesce(keyOrig, keyDelta),
                case
                    when valOrig isnull then valDelta
                    when valDelta isnull then valOrig
                    when (jsonb_typeof(valOrig) <> 'object' or jsonb_typeof(valDelta) <> 'object') then valDelta
                    else jsonb_merge(valOrig, valDelta)
                end
            )
        from jsonb_each(orig) e1(keyOrig, valOrig)
        full join jsonb_each(delta) e2(keyDelta, valDelta) on keyOrig = keyDelta
    $$;
    
    0 讨论(0)
  • 2021-02-05 18:10

    For this example:

    { 
        "foo": {
            "baz": { "done": false }
        },
        "abc": "Hello"
    }
    

    INSERT:

    You have to use jsonb_insert you can test it with a SELECT.

    SELECT jsonb_insert(xyz, '{foo,bar}', '{"done":true}'::jsonb) FROM tablename;
    

    Note: With jsonb_insert is really important to set the path correctly. Here the path is '{foo:bar}' meaning that you will insert a JSON inside the object foo called bar.

    Hence, the result is:

    {
        "abc": "Hello",
        "foo": {
            "baz": {
                "done": false
            },
            "bar": {
                "done": true
            }
        }
    }
    

    SET:

    To edit bar and set it to false you have to use jsonb_set. You can test it with SELECT:

    SELECT jsonb_set(xyz, '{foo,bar}', '{"done":false}'::jsonb) FROM tablename;
    

    This returns:

    {
        "abc": "Hello",
        "foo": {
            "baz": {
                "done": false
            },
            "bar": {
                "done": false
            }
        }
    }
    

    UPDATE FOR SET AND INSERT

    You use jsonb_set when the object exists and jsonb_insert when it doesn't. To update without knowing which one to use, you can use CASE

    UPDATE tablename SET 
    xyz= (CASE
            WHEN xyz->'foo' IS NOT NULL
            THEN jsonb_set(xyz, '{foo,bar}', '{"done":false}'::jsonb)
            WHEN xyz->'foo' IS NULL
            THEN jsonb_insert(xyz, '{foo}', '{"bar":{"done":true}}'::jsonb)
        END)
    WHERE id=1;-- if you use an id to identify the JSON.
    

    You can add some CASE clauses for more specific values.

    0 讨论(0)
提交回复
热议问题