How do I modify fields inside the new PostgreSQL JSON datatype?

前端 未结 21 1947
孤独总比滥情好
孤独总比滥情好 2020-11-22 15:37

With postgresql 9.3 I can SELECT specific fields of a JSON data type, but how do you modify them using UPDATE? I can\'t find any examples of this in the postgresql documenta

相关标签:
21条回答
  • 2020-11-22 16:14

    what do you think about this solution ?

    It will add the new value or update an existing one.

    Edit: edited to make it work with null and empty object

    Edit2: edited to make it work with object in the object...

    create or replace function updateJsonb(object1 json, object2 json)
    returns jsonb
    language plpgsql
    as
    $$
    declare
        result jsonb;
        tempObj1 text;
        tempObj2 text;
    
    begin
        tempObj1 = substr(object1::text, 2, length(object1::text) - 2); --remove the first { and last }
        tempObj2 = substr(object2::text, 2, length(object2::text) - 2); --remove the first { and last }
    
        IF object1::text != '{}' and object1::text != 'null' and object1::text != '[]' THEN
            result = ('{' || tempObj1 || ',' || tempObj2 || '}')::jsonb;
        ELSE
            result = ('{' || tempObj2 || '}')::jsonb;
        END IF;
        return result;
    end;
    $$;
    

    usage:

    update table_name
    set data = updatejsonb(data, '{"test": "ok"}'::json)
    
    0 讨论(0)
  • 2020-11-22 16:15

    If you want to use values from other columns in your JSON update command you can use string concatenation:

    UPDATE table
    SET column1 = column1::jsonb - 'key' || ('{"key": ' || column2::text ||  '}')::jsonb
    where ...;
    
    0 讨论(0)
  • 2020-11-22 16:17

    With PostgreSQL 9.4, we've implemented the following python function. It may also work with PostgreSQL 9.3.

    create language plpython2u;
    
    create or replace function json_set(jdata jsonb, jpaths jsonb, jvalue jsonb) returns jsonb as $$
    import json
    
    a = json.loads(jdata)
    b = json.loads(jpaths)
    
    if a.__class__.__name__ != 'dict' and a.__class__.__name__ != 'list':
      raise plpy.Error("The json data must be an object or a string.")
    
    if b.__class__.__name__ != 'list':
       raise plpy.Error("The json path must be an array of paths to traverse.")
    
    c = a
    for i in range(0, len(b)):
      p = b[i]
      plpy.notice('p == ' + str(p))
    
      if i == len(b) - 1:
        c[p] = json.loads(jvalue)
    
      else:
        if p.__class__.__name__ == 'unicode':
          plpy.notice("Traversing '" + p + "'")
          if c.__class__.__name__ != 'dict':
            raise plpy.Error("  The value here is not a dictionary.")
          else:
            c = c[p]
    
        if p.__class__.__name__ == 'int':
          plpy.notice("Traversing " + str(p))
          if c.__class__.__name__ != 'list':
            raise plpy.Error("  The value here is not a list.")
          else:
            c = c[p]
    
        if c is None:
          break    
    
    return json.dumps(a)
    $$ language plpython2u ;
    

    Example usage:

    create table jsonb_table (jsonb_column jsonb);
    insert into jsonb_table values
    ('{"cars":["Jaguar", {"type":"Unknown","partsList":[12, 34, 56]}, "Atom"]}');
    
    select jsonb_column->'cars'->1->'partsList'->2, jsonb_column from jsonb_table;
    
    update jsonb_table
    set jsonb_column = json_set(jsonb_column, '["cars",1,"partsList",2]', '99');
    
    select jsonb_column->'cars'->1->'partsList'->2, jsonb_column from jsonb_table;
    

    Note that for a previous employer, I have written a set of C functions for manipulating JSON data as text (not as a json or jsonb type) for PostgreSQL 7, 8 and 9. For example, extracting data with json_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']'), setting data with json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87') and so on. It took about 3 days work, so if you need it to run on legacy systems and have the time to spare, it may be worth the effort. I imagine the C version is much faster than the python version.

    0 讨论(0)
  • 2020-11-22 16:17

    Sadly, I've not found anything in the documentation, but you can use some workaround, for example you could write some extended function.

    For example, in Python:

    CREATE or REPLACE FUNCTION json_update(data json, key text, value json)
    returns json
    as $$
    from json import loads, dumps
    if key is None: return data
    js = loads(data)
    js[key] = value
    return dumps(js)
    $$ language plpython3u
    

    and then

    update test set data=json_update(data, 'a', to_json(5)) where data->>'b' = '2';
    
    0 讨论(0)
  • 2020-11-22 16:17

    The following plpython snippet might come in handy.

    CREATE EXTENSION IF NOT EXISTS plpythonu;
    CREATE LANGUAGE plpythonu;
    
    CREATE OR REPLACE FUNCTION json_update(data json, key text, value text)
     RETURNS json
     AS $$
        import json
        json_data = json.loads(data)
        json_data[key] = value
        return json.dumps(json_data, indent=4)
     $$ LANGUAGE plpythonu;
    
    -- Check how JSON looks before updating
    
    SELECT json_update(content::json, 'CFRDiagnosis.mod_nbs', '1')
    FROM sc_server_centre_document WHERE record_id = 35 AND template = 'CFRDiagnosis';
    
    -- Once satisfied update JSON inplace
    
    UPDATE sc_server_centre_document SET content = json_update(content::json, 'CFRDiagnosis.mod_nbs', '1')
    WHERE record_id = 35 AND template = 'CFRDiagnosis';
    
    0 讨论(0)
  • 2020-11-22 16:17

    You can try updating as below:

    Syntax: UPDATE table_name SET column_name = column_name::jsonb || '{"key":new_value}' WHERE column_name condition;

    For your example:

    UPDATE test SET data = data::jsonb || '{"a":new_value}' WHERE data->>'b' = '2';

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