How to update jsonb sub element postgres? - database

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

Related

Postgres update jsonb string to array

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

Update/Add Array Value PostgreSQL

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.

Snowflake Retrieve value from Semi Structured Data

I'm trying to retrieve the health value from Snowflake semi structured data in a variant column called extra from table X.
An example of the code can be seen below:
[
{
"party":
"[{\"class\":\"Farmer\",\"gender\":\"Female\",\"ethnicity\":\"NativeAmerican\",\"health\":2},
{\"class\":\"Adventurer\",\"gender\":\"Male\",\"ethnicity\":\"White\",\"health\":3},
{\"class\":\"Farmer\",\"gender\":\"Male\",\"ethnicity\":\"White\",\"health\":0},
{\"class\":\"Banker\",\"gender\":\"Female\",\"ethnicity\":\"White\",\"health\":0}
}
]
I have tried reading the Snowflake documentation from https://community.snowflake.com/s/article/querying-semi-structured-data
I have also tried the following queries to flatten the query:
SELECT result.value:health AS PartyHealth
FROM X
WHERE value = 'Trail'
AND name = 'Completed'
AND PartyHealth > 0,
TABLE(FLATTEN(X, 'party')) result
AND
SELECT [0]['party'][0]['health'] AS Health
FROM X
WHERE value = 'Trail'
AND name = 'Completed'
AND PH > 0;
I am trying to retrieve the health value from table X from column extra which contains the the variant party, which has 4 repeating values [0-3]. Im not sure how to do this is someone able to tell me how to query semi structured data in Snowflake, considering the documentation doesn't make much sense?
First, the JSON value you posted seems wrong formatted (might be a copy paste issue).
Here's an example that works:
first your JSON formatted:
[{ "party": [ {"class":"Farmer","gender":"Female","ethnicity":"NativeAmerican","health":2}, {"class":"Adventurer","gender":"Male","ethnicity":"White","health":3}, {"class":"Farmer","gender":"Male","ethnicity":"White","health":0}, {"class":"Banker","gender":"Female","ethnicity":"White","health":0} ] }]
create a table to test:
CREATE OR REPLACE TABLE myvariant (v variant);
insert the JSON value into this table:
INSERT INTO myvariant SELECT PARSE_JSON('[{ "party": [ {"class":"Farmer","gender":"Female","ethnicity":"NativeAmerican","health":2}, {"class":"Adventurer","gender":"Male","ethnicity":"White","health":3}, {"class":"Farmer","gender":"Male","ethnicity":"White","health":0}, {"class":"Banker","gender":"Female","ethnicity":"White","health":0} ] }]');
now, to select a value you start from column name, in my case v, and as your JSON is an array inside, I specify first value [0], and from there expand, so something like this:
SELECT v[0]:party[0].health FROM myvariant;
Above gives me:
For the other rows you can simply do:
SELECT v[0]:party[1].health FROM myvariant;
SELECT v[0]:party[2].health FROM myvariant;
SELECT v[0]:party[3].health FROM myvariant;
Another option might be to make the data more like a table ... I find it easier to work with than the JSON :-)
Code at bottom - just copy/paste and it runs in Snowflake returning screenshot below.
Key Doco is Lateral Flatten
SELECT d4.path, d4.value
from
lateral flatten(input=>PARSE_JSON('[{ "party": [ {"class":"Farmer","gender":"Female","ethnicity":"NativeAmerican","health":2}, {"class":"Adventurer","gender":"Male","ethnicity":"White","health":3}, {"class":"Farmer","gender":"Male","ethnicity":"White","health":0}, {"class":"Banker","gender":"Female","ethnicity":"White","health":0} ] }]') ) as d ,
lateral flatten(input=> value) as d2 ,
lateral flatten(input=> d2.value) as d3 ,
lateral flatten(input=> d3.value) as d4

How to delete array element in JSONB column based on nested key value?

How can I remove an object from an array, based on the value of one of the object's keys?
The array is nested within a parent object.
Here's a sample structure:
{
"foo1": [ { "bar1": 123, "bar2": 456 }, { "bar1": 789, "bar2": 42 } ],
"foo2": [ "some other stuff" ]
}
Can I remove an array element based on the value of bar1?
I can query based on the bar1 value using: columnname #> '{ "foo1": [ { "bar1": 123 } ]}', but I've had no luck finding a way to remove { "bar1": 123, "bar2": 456 } from foo1 while keeping everything else intact.
Thanks
Running PostgreSQL 9.6
Assuming that you want to search for a specific object with an inner object of a certain value, and that this specific object can appear anywhere in the array, you need to unpack the document and each of the arrays, test the inner sub-documents for containment and delete as appropriate, then re-assemble the array and the JSON document (untested):
SELECT id, jsonb_build_object(key, jarray)
FROM (
SELECT foo.id, foo.key, jsonb_build_array(bar.value) AS jarray
FROM ( SELECT id, key, value
FROM my_table, jsonb_each(jdoc) ) foo,
jsonb_array_elements(foo.value) AS bar (value)
WHERE NOT bar.value #> '{"bar1": 123}'::jsonb
GROUP BY 1, 2 ) x
GROUP BY 1;
Now, this may seem a little dense, so picked apart you get:
SELECT id, key, value
FROM my_table, jsonb_each(jdoc)
This uses a lateral join on your table to take the JSON document jdoc and turn it into a set of rows foo(id, key, value) where the value contains the array. The id is the primary key of your table.
Then we get:
SELECT foo.id, foo.key, jsonb_build_array(bar.value) AS jarray
FROM foo, -- abbreviated from above
jsonb_array_elements(foo.value) AS bar (value)
WHERE NOT bar.value #> '{"bar1": 123}'::jsonb
GROUP BY 1, 2
This uses another lateral join to unpack the arrays into bar(value) rows. These objects can now be searched with the containment operator to remove the objects from the result set: WHERE NOT bar.value #> '{"bar1": 123}'::jsonb. In the select list the arrays are re-assembled by id and key but now without the offending sub-documents.
Finally, in the main query the JSON documents are re-assembled:
SELECT id, jsonb_build_object(key, jarray)
FROM x -- from above
GROUP BY 1;
The important thing to understand is that PostgreSQL JSON functions only operate on the level of the JSON document that you can explicitly indicate. Usually that is the top level of the document, unless you have an explicit path to some level in the document (like {foo1, 0, bar1}, but you don't have that). At that level of operation you can then unpack to do your processing such as removing objects.

how to modify an array jsonb in postgresql?

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.

Resources