jsonb_set() and jsonb_insert()
Purpose: Use jsonb_set()
to change a JSON value that is the value of an existing key-value pair in a JSON object or the value at an existing index in a JSON array. Use jsonb_insert()
to insert a value, either as the value for a key that doesn't yet exist in a JSON object or beyond the end or before the start of the index range, for a JSON array.
Signature: For jsonb_set()
:
jsonb_in: jsonb
path: text[]
replacement: jsonb
create_if_missing: boolean default true
return value: jsonb
Signature: For jsonb_insert()
:
jsonb_in: jsonb
path: text[]
replacement: jsonb
insert_after: boolean default false
return value: jsonb
Notes:
-
There is no
json
variant. -
It turns out that the effect of the two functions is the same in some cases. This brings useful "upsert" functionality when the target is a JSON array.
-
The meaning of the defaulted
boolean
formal parameter is context dependent. -
The input JSON value must be either an object or an array—in other words, it must have elements that can be addressed by a path.
Semantics when "jsonb_in" is a JSON object
parsed, or when two JSON values are concatenated, and if a key is repeated, then the last-mentioned in left-to-right order wins.) The functionality is sufficiently illustrated by a "json_in" value that has just primitive values. The result of each function invocation is the same.
do $body$
declare
j constant jsonb := '{"a": 1, "b": 2, "c": 3}';
path constant text[] := array['d'];
new_number constant jsonb := '4';
j_set constant jsonb := jsonb_set(
jsonb_in => j,
path => path,
replacement => new_number,
create_if_missing => true);
j_insert constant jsonb := jsonb_insert(
jsonb_in => j,
path => path,
replacement => new_number,
insert_after => false);
expected_j constant jsonb := '{"a": 1, "b": 2, "c": 3, "d": 4}';
begin
assert
j_set = expected_j and
j_insert = expected_j,
'unexpected';
end;
$body$;
Notice that the specified path
, the key "d"
doesn't yet exist. Each function call asks to produce the result that the key "d"
should exist with the value 4
. So, as you see, the effect of each, as written above, is the same.
If jsonb_set()
is invoked with "create_if_missing" set to FALSE
, then its result is the same as the input. But if jsonb_insert()
is invoked with "insert_after" set to TRUE
, then its output is the same as when it's invoked with "insert_after" set to FALSE
. This reflects the fact that the order of key-value pairs in an object is insignificant.
What if "path" specifies a key that does already exist? Now jsonb_insert()
causes this error when it's invoked both with "insert_after" set to TRUE
and with "insert_after" set to FALSE
:
cannot replace existing key
Try using the function jsonb_set to replace key value.
And this DO
block quietly succeeds, both when it's invoked with "create_if_missing" set to FALSE
and when it's invoked with "create_if_missing" set to TRUE
.
do $body$
declare
j constant jsonb := '{"a": 1, "b": 2, "c": 3}';
path constant text[] := array['c'];
new_number constant jsonb := '4';
j_set constant jsonb := jsonb_set(
jsonb_in => j,
path => path,
replacement => new_number,
create_if_missing => true);
expected_j constant jsonb := '{"a": 1, "b": 2, "c": 4}';
begin
assert
j_set = expected_j,
'unexpected';
end;
$body$;
Semantics when "jsonb_in" is an JSON array
A JSON array is a list of index-addressable values—in other words, the order is defined and significant. Again, the functionality is sufficiently illustrated by a json_in
value that has just primitive values. Now the result of jsonb_set()
differs from that of jsonb_insert()
.
do $body$
declare
j constant jsonb := '["a", "b", "c", "d"]';
path constant text[] := array['3'];
new_string constant jsonb := '"x"';
j_set constant jsonb := jsonb_set(
jsonb_in => j,
path => path,
replacement => new_string,
create_if_missing => true);
j_insert constant jsonb := jsonb_insert(
jsonb_in => j,
path => path,
replacement => new_string,
insert_after => true);
expected_j_set constant jsonb := '["a", "b", "c", "x"]';
expected_j_insert constant jsonb := '["a", "b", "c", "d", "x"]';
begin
assert
(j_set = expected_j_set) and
(j_insert = expected_j_insert),
'unexpected';
end;
$body$;
Notice that the path denotes the fourth value and that this already exists.
Here, jsonb_set()
located the fourth value and set it to "x" while jsonb_insert()
located the fourth value and, as requested by "insert_after" set to TRUE
, inserted "x" after it. Of course, with "insert_after" set to FALSE
, "x" is inserted before "d". And (of course, again) the choice for "create_if_missing" has no effect on the result of jsonb_set()
.
What if the path denotes a value beyond the end of the array?
do $body$
declare
j constant jsonb := '["a", "b", "c", "d"]';
path constant text[] := array['42'];
new_string constant jsonb := '"x"';
j_set constant jsonb := jsonb_set(
jsonb_in => j,
path => path,
replacement => new_string,
create_if_missing => true);
j_insert constant jsonb := jsonb_insert(
jsonb_in => j,
path => path,
replacement => new_string,
insert_after => true);
expected_j constant jsonb := '["a", "b", "c", "d", "x"]';
begin
assert
j_set = expected_j and
j_insert = expected_j,
'unexpected';
end;
$body$;
Here, each function had the same effect.
The path, for jsonb_set()
, is taken to mean the as yet nonexistent fifth value. So, with "create_if_missing" set to FALSE
, jsonb_set()
has no effect.
The path, for jsonb_insert()
, is also taken to mean the as yet nonexistent fifth value. But now, the choice of TRUE
or FALSE
for "insert_after" makes no difference because before, or after, a nonexistent element is taken to mean insert it.
Notice that if the path is specified as -42
(i.e. an impossible array index) the result is to establish the specified value at the start of the array. jsonb_set
and jsonb_insert
produce the same result, this:
do $body$
declare
j constant jsonb := '["a", "b", "c", "d"]';
path constant text[] := array['-42'];
new_string constant jsonb := '"x"';
j_set constant jsonb := jsonb_set(
jsonb_in => j,
path => path,
replacement => new_string,
create_if_missing => true);
j_insert constant jsonb := jsonb_insert(
jsonb_in => j,
path => path,
replacement => new_string,
insert_after => true);
expected_j constant jsonb := '["x", "a", "b", "c", "d"]';
begin
assert
j_set = expected_j and
j_insert = expected_j,
'unexpected';
end;
$body$;
The path, for jsonb_set()
, is taken to mean a new first value (implying that the existing values all move along one place). So, again, with "create_if_missing" set to FALSE
, jsonb_set()
has no effect.
The path, for jsonb_insert()
, is also taken to mean a new first value. So again, the choice of TRUE
or FALSE
for "insert_after" makes no difference because before or after, a nonexistent element is taken to mean insert it.