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
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)
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 ...;
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.
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';
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';
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';