I have a table where column2 is type JSONB and I would like to alter the values where column2 is a string to an array. I would like the result for column2 to have "one" be ["one"] and "third" be ["third"]
Table
column1
column2
First
["one", "two", "three"]
Second
"one"
Third
"third"
Third
4
How should I be updating the value?
Here's what I have tried:
UPDATE table
SET columnn2 = ARRAY[value]::JSONB
WHERE jsonb_typeof(column2)!='array';
Use the jsonb_build_array() function:
update my_table set
column2 = jsonb_build_array(column2)
where jsonb_typeof(column2) != 'array';
Test it in db<>fiddle.
Read about the function in the documentation.
One option is to use concatenation operators(||) to wrap up the expresion with square brackets along with casting to TEXT, then recasting to JSONB such as
UPDATE tab
SET column2 = ('['||column2::TEXT||']')::JSONB
WHERE jsonb_typeof(column2) != 'array'
Demo
Related
My table:
CREATE TABLE public.invoice (
id bigint NOT NULL,
json_data jsonb NOT NULL
);
My Data:
INSERT INTO public.invoice (id,json_data) VALUES
(1,'{"Id": 1, "Items": ["2", "3", "1"], "Invoice": "CR000012"}');
Todo List:
Need to add to "Items" a new value i.e "5". (output of items ["2", "3", "1","5"])
Need to update items value 2 to 9. (output of items ["9", "3", "1","5"])
I have tried below but this will replace the array values not update or add
UPDATE invoice SET json_data = jsonb_set(json_data, '{invoice }', '"4"') where Id ='1'
I recommend you to use this aproach, you should point element with index of array.
In your case, your code should look something like this,
1. Add to Items a new value i.e "5
UPDATE invoice SET json_data = jsonb_set(json_data, {Items,0}, json_data->'Items' || '"5"', TRUE) where Id =1
2. Update Items value 2 to 9.
UPDATE invoice SET json_data = jsonb_set(json_data, {Items,0}, '"9"') where Id =1
You can check PostgreSQL, JSON Functions and Operators from here.
Table name: the_table
Column name: the_column
I want to update from this:
old_json: {"aa": {"bb": {"asd": "asd", "qqq": "aqaq", "the_key": "the_value"}"}}
modified json: {"aa": {"bb": {"asd": "asd", "qqq": "aqaq", "the_key": "the_NEW_value"}"}}
In my case I have to update many similar rows like above.
if I do like that update the_table set the_column = jsonb_set(the_column, '{aa}', '"the_value"') then result be like : {"aa": "the_value"}
Then I tried update the_table set the_column = jsonb_set(the_column, '{aa: {bb: {the_key}}}', '"the_value"') but it's does't work
So how to update jsonb correctly?
demo:db<>fiddle
The second parameter of jsonb_set() is an text array which contains the keys as path to your values:
UPDATE the_table
SET the_column = jsonb_set(the_column, '{aa, bb, the_key}', '"the_NEW_value"');
I have table myTable with a JSONB column myJsonb with a data structure that I want to index like:
{
"myArray": [
{
"subItem": {
"email": "bar#bar.com"
}
},
{
"subItem": {
"email": "foo#foo.com"
}
}
]
}
I want to run indexed queries on email like:
SELECT *
FROM mytable
WHERE 'foo#foo.com' IN (
SELECT lower(
jsonb_array_elements(myjsonb -> 'myArray')
-> 'subItem'
->> 'email'
)
);
How do I create a Postgres JSONB index for that?
If you don't need the lower() in there, the query can be simple and efficient:
SELECT *
FROM mytable
WHERE myjsonb -> 'myArray' #> '[{"subItem": {"email": "foo#foo.com"}}]'
Supported by a jsonb_path_ops index:
CREATE INDEX mytable_myjsonb_gin_idx ON mytable
USING gin ((myjsonb -> 'myArray') jsonb_path_ops);
But the match is case-sensitive.
Case-insensitive!
If you need the search to match disregarding case, things get more complex.
You could use this query, similar to your original:
SELECT *
FROM t
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(myjsonb -> 'myArray') arr
WHERE lower(arr #>>'{subItem, email}') = 'foo#foo.com'
);
But I can't think of a good way to use an index for this.
Instead, I would use an expression index based on a function extracting an array of lower-case emails:
Function:
CREATE OR REPLACE FUNCTION f_jsonb_arr_lower(_j jsonb, VARIADIC _path text[])
RETURNS jsonb LANGUAGE sql IMMUTABLE AS
'SELECT jsonb_agg(lower(elem #>> _path)) FROM jsonb_array_elements(_j) elem';
Index:
CREATE INDEX mytable_email_arr_idx ON mytable
USING gin (f_jsonb_arr_lower(myjsonb -> 'myArray', 'subItem', 'email') jsonb_path_ops);
Query:
SELECT *
FROM mytable
WHERE f_jsonb_arr_lower(myjsonb -> 'myArray', 'subItem', 'email') #> '"foo#foo.com"';
While this works with an untyped string literal or with actual jsonb values, it stops working if you pass text or varchar (like in a prepared statement). Postgres does not know how to cast because the input is ambiguous. You need an explicit cast in this case:
... #> '"foo#foo.com"'::text::jsonb;
Or pass a simple string without enclosing double quotes and do the conversion to jsonb in Postgres:
... #> to_jsonb('foo#foo.com'::text);
Related, with more explanation:
Query for array elements inside JSON type
Index for finding an element in a JSON array
In postgresql,I have a table which defined like this:
create table carts(
id serial,
cart json
)
has data like this:
id cart
3 [{"productid":5,"cnt":6},{"productid":8,"cnt":1}]
5 [{"productid":2},{"productid":7,"cnt":1},{"productid":34,"cnt":3}]
if i want to modify the data "cnt", with id=n and productid=m,
how can I do this?
for example, when id=3,and productid=8, i want to change the cnt to cnt+3,
how to realize it?
Try This, here we will use jsonb_set method
jsonb_set(target jsonb, path text[], new_value jsonb) which will return jsonb object
update carts
set cart = (
(select json_agg(val)from
(SELECT
CASE
WHEN value->>'productid'='8' THEN jsonb_set(value::jsonb, '{cnt}', (((value->>'cnt')::int)+3)::text::jsonb)::json --again convert to json object
ELSE value END val
FROM carts,json_array_elements(cart) where id=3))
where id=3;
Hope it works for you
EDIT: you can generalized this by creating a function with id and
productid as input parameter.
For versions greater than 9.5 see this question
I have created a table in PostgreSQL using this:
CREATE TEMP TABLE jsontesting
AS
SELECT id, jsondata::jsonb FROM ( VALUES
(1, '["abra","value","mango", "apple", "sample"]'),
(2, '["japan","china","india", "russia", "australia"]'),
(3, '["must", "match"]'),
(4, '["abra","value","true", "apple", "sample"]'),
(5, '["abra","false","mango", "apple", "sample"]'),
(6, '["string","value","mango", "apple", "sample"]'),
(7, '["must", "watch"]')
) AS t(id,jsondata);
Now what I wanted was to
add Something like append_to_json_array takes in the actual jsondata which is a json-array and the newString which I have to add to that jsondata array and this function should return the updated json-array.
UPDATE jsontesting
SET jsondata=append_to_json_array(jsondata, 'newString')
WHERE id = 7;
remove a value from the json data array, one function for removing the value.
I tried to search documentation of postgreSQL but found nothing there.
Radek's idea can be used to define these handy functions:
create function jsonb_array_append(j jsonb, e text)
returns jsonb language sql immutable
as $$
select array_to_json(array_append(array(select * from jsonb_array_elements_text(j)), e))::jsonb
$$;
create function jsonb_array_remove(j jsonb, e text)
returns jsonb language sql immutable
as $$
select array_to_json(array_remove(array(select * from jsonb_array_elements_text(j)), e))::jsonb
$$;
create function jsonb_array_replace(j jsonb, e1 text, e2 text)
returns jsonb language sql immutable
as $$
select array_to_json(array_replace(array(select * from jsonb_array_elements_text(j)), e1, e2))::jsonb
$$;
The functions in action:
select jsonb_array_append('["alfa", "beta", "gamma"]', 'delta');
jsonb_array_append
------------------------------------
["alfa", "beta", "gamma", "delta"]
select jsonb_array_remove('["alfa", "beta", "gamma"]', 'beta');
jsonb_array_remove
-------------------
["alfa", "gamma"]
select jsonb_array_replace('["alfa", "beta", "gamma"]', 'alfa', 'delta');
jsonb_array_replace
----------------------------
["delta", "beta", "gamma"]
If they prove useful for you, please appreciate Radek's answer. However, I have to add I fully agree with a_horse's comment.
To add:
update jsontesting
set jsondata = array_to_json(array(select * from jsonb_array_elements_text(jsondata)) || 'newString'::text)::jsonb
where id = 7;
To remove:
update jsontesting
set jsondata = array_to_json(array_remove(array(select * from jsonb_array_elements_text(jsondata)), 'toRemove'))::jsonb
where id = 7;