Search and update a JSON array element in Postgres - postgresql

I have a Jsonb column that store array of elements like the following:
[
{"id": "11", "name": "John", "age":"25", ..........},
{"id": "22", "name": "Mike", "age":"35", ..........},
{"id": "33", "name": "Tom", "age":"45", ..........},
.....
]
I want to replace the 2nd object(id=22) with a totally new object. I don't want to update each property one by one because there are many properties and their values all could have changed. I want to just identify the 2nd element and replace the whole object.
I know there is a jsonb_set(). However, to update the 2nd element, I need to know its array index=1 so I can do the following:
jsonb_set(data, '{1}', '{"id": "22", "name": "Don", "age":"55"}',true)
But I couldn't find any way to search and get that index. Can someone help me out?

One way I can think of is to combine row_number and json_array_elements:
-- test data
create table test (id integer, data jsonb);
insert into test values (1, '[{"id": "22", "name": "Don", "age":"55"}, {"id": "23", "name": "Don2", "age":"55"},{"id": "24", "name": "Don3", "age":"55"}]');
insert into test values (2, '[{"id": "32", "name": "Don", "age":"55"}, {"id": "33", "name": "Don2", "age":"55"},{"id": "34", "name": "Don3", "age":"55"}]');
select subrow, id, row_number() over (partition by id)
from (
select json_array_elements(data) as subrow, id
from test
) as t;
subrow | id | row_number
------------------------------------------+----+------------
{"id": "22", "name": "Don", "age":"55"} | 1 | 1
{"id": "23", "name": "Don2", "age":"55"} | 1 | 2
{"id": "24", "name": "Don3", "age":"55"} | 1 | 3
{"id": "32", "name": "Don", "age":"55"} | 2 | 1
{"id": "33", "name": "Don2", "age":"55"} | 2 | 2
{"id": "34", "name": "Don3", "age":"55"} | 2 | 3
-- apparently you can filter what you want from here
select subrow, id, row_number() over (partition by id)
from (
select json_array_elements(data) as subrow, id
from test
) as t
where subrow->>'id' = '23';
In addition, think about your schema design. It may not be the best idea to store your data this way.

Related

Jsonb array of objects update

So this is my jsonb array of objects. Column is called bids in my db.
bids column
[
{
"id": "1",
"size": "5.5Y",
"price": 180
},
{
"id": "f0d1d36a-f6af-409e-968e-54c1dc104566",
"size": "6.5Y",
"price": 22
}
]
I want to update price property by the ID of an element for ex. "f0d1d36a-f6af-409e-968e-54c1dc104566", so the price would change from 22 to 150 IN ROW WHICH CONTAINS ELEMENT WITH DESIRED ID IN THE COLUMN.
How can I do that?
create table json_update (id integer, json_fld jsonb);
insert into json_update values (1, '[
{
"id": "1",
"size": "5.5Y",
"price": 180
},
{
"id": "f0d1d36a-f6af-409e-968e-54c1dc104566",
"size": "6.5Y",
"price": 22
}
]'
)
;
UPDATE
json_update
SET
json_fld = jsonb_set(json_fld, ARRAY[(idx)::text, 'price'::text], '150'::jsonb)
FROM (
SELECT
(row_number() OVER (ORDER BY t.a ->> 'id') - 1) AS idx,
t.a
FROM (
SELECT
jsonb_array_elements(json_fld)
FROM
json_update) AS t (a)) AS i
WHERE
i.a ->> 'id' = 'f0d1d36a-f6af-409e-968e-54c1dc104566';
select * from json_update ;
id | json_fld
----+---------------------------------------------------------------------------------------------------------------------------
1 | [{"id": "1", "size": "5.5Y", "price": 180}, {"id": "f0d1d36a-f6af-409e-968e-54c1dc104566", "size": "6.5Y", "price": 150}]

How to add key/value to element in array of array (jsonb type) in plain sql (encountered ERROR: aggregate function calls cannot be nested)

dbfiddle here:
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=5924abbdad955e159de7f3571ebbac5a
Say I've a table
CREATE TABLE yewu (
id serial PRIMARY KEY,
org varchar(50),
data jsonb
);
I want to update table
id org data
1 OA [{"profiles": [{"id": 1}, {"id": 2}]}, {"profiles": [{"id": 3}, {"id": 4}]}]
2 OB [{"profiles": [{"id": 1}, {"id": 2}]}, {"profiles": [{"id": 3}, {"id": 4}]}]
to
id org data
1 OA [{"profiles": [{"id": 1,"org":"OA"}, {"id": 2,"org":"OA"}]}, {"profiles": [{"id": 3,"org":"OA"}, {"id": 4,"org":"OA"}]}]
2 OB [{"profiles": [{"id": 1,"org":"OB"}, {"id": 2,"org":"OB"}]}, {"profiles": [{"id": 3,"org":"OB"}, {"id": 4,"org":"OB"}]}]
this is what I try:
UPDATE
yewu
SET
data = (
SELECT
jsonb_agg((
SELECT
jsonb_set(oc, '{profiles}', jsonb_agg((
SELECT
jsonb_set(p, '{org}', yewu.org::jsonb)
FROM jsonb_array_elements(oc -> 'profiles') p)))))
FROM
jsonb_array_elements(data) oc)
RETURNING
id;
and error:
ERROR: aggregate function calls cannot be nested
LINE 8: jsonb_set(oc, '{profiles}', jsonb_agg((
^
Sample Query:
select
t2.id,
t2.org,
jsonb_agg(t2.p1) as "data"
from (
select
t1.id,
t1.org,
t1.profiles,
jsonb_build_object('profiles', jsonb_agg(t1.p1)) as p1
from (
select
id,
org,
jsonb_array_elements("data")->'profiles' as "profiles",
jsonb_array_elements(jsonb_array_elements("data")->'profiles') || jsonb_build_object('org', org) as p1
from yewu
) t1
group by t1.id, t1.org, t1.profiles
) t2
group by t2.id, t2.org
------------------------- RETURN -------------------------
id org data
--------------------------------------------------------------------------------------------------------------------------------------------------
1 OA [{"profiles": [{"id": 1, "org": "OA"}, {"id": 2, "org": "OA"}]}, {"profiles": [{"id": 3, "org": "OA"}, {"id": 4, "org": "OA"}]}]
2 OB [{"profiles": [{"id": 1, "org": "OB"}, {"id": 2, "org": "OB"}]}, {"profiles": [{"id": 3, "org": "OB"}, {"id": 4, "org": "OB"}]}]

PostgreSQL jsonb_path_query removes result instead of returning null value

In an example table:
create table example
(
id serial not null
constraint example_pk
primary key,
data json not null
);
and data
INSERT INTO public.example (id, data) VALUES (1, '[{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}]');
INSERT INTO public.example (id, data) VALUES (2, '[{"key": "1", "value": "val1"}]');
INSERT INTO public.example (id, data) VALUES (3, '[{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}]');
id
data
1
[{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}]
2
[{"key": "1", "value": "val1"}]
3
[{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}]
I want to query the value field in the data column where key = 2
The query I'm currently using is this:
SELECT id,
jsonb_path_query(
TO_JSONB(data),
'$[*] ? (#.key == "2").value'::JSONPATH
)::VARCHAR AS values
FROM example
I would expect the results to be:
id
values
1
"val2"
2
null
3
"val2"
But the actual result is:
id
values
1
"val2"
3
"val2"
Is there a reason why the null output of jsonb_path_query is omitted? How do I get it to behave the way I'm expecting?
You want jsonb_path_query_first() if you want the result of the path expression:
SELECT id,
jsonb_path_query_first(data, '$[*] ? (#.key == "2").value') AS values
FROM example
Note that this returns a jsonb value. If you want a text value, use:
jsonb_path_query_first(data, '$[*] ? (#.key == "2").value') #>> '{}
As per PostgreSQL documentation the filter acts as WHERE condition
When defining the path, you can also use one or more filter expressions that work similar to the WHERE clause in SQL. A filter expression begins with a question mark and provides a condition in parentheses:
I managed to achieve what you're looking for using the LATERAL and a LEFT JOIN
SELECT id,
*
FROM example left join
LATERAL jsonb_path_query(
TO_JSONB(data),
'$[*] ? (#.key == "2").value'::JSONPATH)
on true;
Result
id | id | data | jsonb_path_query
----+----+----------------------------------------------------------------+------------------
1 | 1 | [{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}] | "val2"
2 | 2 | [{"key": "1", "value": "val1"}] |
3 | 3 | [{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}] | "val2"
(3 rows)

Fetch records where json contains a particular object

I have a postgres 9.6 table which has a json field config. I want to fetch records from this table where the json has a particular key value pair.
My table is as follows
CREATE TABLE features(
id integer NOT NULL,
plan character,
config json NOT NULL
)
In the json field, I am storing a json in the form
[
{ "name": "A", "state": "active"},
{ "name": "B", "state": "inactive"},
{ "name": "C", "state": "active"}
]
Now, I am querying the database to fetch all the records for which the json field contains the key-value pair { "name": "B", "state": "inactive"}.
My query is as follows
select * from features where config #> '[{ "name": "B", "state": "inactive"}]';
However, I get an error
ERROR: operator does not exist: config #> unknown
Any idea where I am going wrong here. Pointers will be highly appreciated. TIA !!!
Operator #> is only available for jsonb data type:
CREATE TABLE features(
id integer NOT NULL,
plan character,
config jsonb NOT NULL
);
CREATE
insert into features values(1,'a',' [ { "name": "A", "state": "active"}, { "name": "B", "state": "inactive"}, { "name": "C", "state": "active"} ]');
INSERT 0 1
select * from features where config #> '[{ "name": "B", "state": "inactive"}]';
id | plan | config
----+------+----------------------------------------------------------------------------------------------------------
1 | a | [{"name": "A", "state": "active"}, {"name": "B", "state": "inactive"}, {"name": "C", "state": "active"}]
(1 row)
With json data type in the table, you can use:
select * from
(select json_array_elements(config)::jsonb as item from features) as setofjsonb
where item = '{"name": "B", "state": "inactive"}'::jsonb;
item
------------------------------------
{"name": "B", "state": "inactive"}
(1 row)

Postgres: how to delete duplicate based on field in jsonb column

I have a postgres table with a created_at and data column, where the data column is in jsonb format.
A row is considered duplicated if it has the same value for the id propery in the data column.
The table looks like this
created_at | data
--------------------------------------------------------------------------
1 2018-03-20 | {"id": "abc", "name": "please"}
2 2018-01-10 | {"id": "sdf", "name": "john" }
3 2018-03-31 | {"id": "lkj", "name": "doe" }
4 2018-02-30 | {"id": "dfg", "name": "apple"}
5 2018-05-24 | {"id": "dfg", "name": "seed" }
6 2018-03-27 | {"id": "23f", "name": "need" }
7 2018-11-14 | {"id": "abc", "name": "help" }
What is an efficient way to remove duplicates in this table? I do want to keep one instance behind
ie. if 5 entries have the same id I want to delete 4 and leave 1 of them in table
In this scenario I want to remove one of the entries with id='abc' and id='dfg'