Update jsonb array one element without key using jsonb_set in postgresql - postgresql

I have table audits with jsonb field data and it has content:
"products": [
{"id": 405, "color": null, "price": 850, "title": "test", "value": 52, "real_value": 105 },
{"id": 347, "color": null, "price": 195, "title": "test2", "value": 69, "real_value": 0}
]
For update, one needs to know the key of the element, but I don't know how to find the key of this element(0). Example:
UPDATE audits set data = jsonb_set(data::jsonb,'{"products",0,"real_value"}','125')
where id = 10 and enterprise_id = 1;
I tried using this:
SELECT ( select index from
generate_series(0, jsonb_array_length(data->'products') - 1) as index
where data->'products'->index->>'id' = '347') as index_of_element
FROM audits; but it's a very hard operation for pg.
Also I know how to find the product id for this object:
select id as possiton from audits
where data->'products' #> '[{"id":347}]'; but not key in array(((
Maybe I can use another function to update an item in an array, but I do not know how.

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 get item from JSON-b field

I have table with JSON-b field like this:
id | data
----------
1 | '{"points": [{"id": 10, "address": "Test 1"}, {"id": 20, "address": "Test 2"}, {"id": 30, "address": "Test 3"}]}'
2 | '{"points": [{"id": 40, "address": "Test 444"}, {"id": 20, "address": "Test 222"}, {"id": 50, "address": "Test 555"}]}'
Please note that data->'points' item with id 20 is in both lines.
The JSON-b field "data" contains "points" array. How to get the point address by id 20?
For example should get:
address
--------
'Test 2'
'Test 222'
I started trying with this query:
SELECT
data
FROM test_json
WHERE
data->'points' #> '[{"id": 20}]'
;
But the whole field is returned, obviously.
PostgreSQL 11
You will need to unnest the array, using a lateral join with jsonb_array_elements, then you can filter for the individual points:
SELECT
point ->> 'address' AS address
FROM test_json,
jsonb_array_elements(data -> 'points') AS point
WHERE
(point ->> 'id')::int = 20
You might be able to add AND data #> '{"points":[{"id": 20}]}' to speed up the search if you have an index on the data column.

PostgresSQL nested jsonb update value of complex key/value pairs

Starting out with JSONB data type and I'm hoping someone can help me out.
I have a table (properties) with two columns (id as primary key and data as jsonb).
The data structure is:
{
"ProductType": "ABC",
"ProductName": "XYZ",
"attributes": [
{
"name": "Color",
"type": "STRING",
"value": "Silver"
},
{
"name": "Case",
"type": "STRING",
"value": "Shells"
},
...
]
}
I would like to update the value of a specific attributes element by name for a row with a given id. For example, for the element with "name"="Case" change the value to "Glass". So it ends up like
{
"ProductType": "ABC",
"ProductName": "XYZ",
"attributes": [
{
"name": "Color",
"type": "STRING",
"value": "Silver"
},
{
"name": "Case",
"type": "STRING",
"value": "Glass"
},
...
]
}
Is this possible with this structure using SQL?
I have created table structure if any of you would like to give it a shot.
dbfiddle
Use the jsonb concatenation operator, ||, to replace keys on the fly:
WITH properties (id, data) AS (
values
(1, '{"ProductType": "ABC","ProductName": "XYZ","attributes": [{"name": "Color","type": "STRING","value": "Silver"},{"name": "Case","type": "STRING","value": "Shells"}]}'::jsonb),
(2, '{"ProductType": "ABC","ProductName": "XYZ","attributes": [{"name": "Color","type": "STRING","value": "Red"},{"name": "Case","type": "STRING","value": "Shells"}]}'::jsonb)
)
SELECT id,
data||
jsonb_build_object(
'attributes',
jsonb_agg(
case
when attribs->>'name' = 'Case' then attribs||'{"value": "Glass"}'::jsonb
else attribs
end
)
) as data
FROM properties m
CROSS JOIN LATERAL JSONB_ARRAY_ELEMENTS(data->'attributes') as a(attribs)
GROUP BY id, data
Updated fiddle

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 + jsonb + get values of key from multidimentional array

I am trying to get jsonb result based on matching key.
I have DB table "listings" with number and data column.
number | data
1 | {"name": "XYZ company", "city": "toronto", "province": "ON", "people" : [
{ "firstName": "tom", "lastName": "hanks",
"phonenumber": [{"type": "mobile", "Number": "111111"}],
"Email": [{"type": "business", "address": "tom#xyz.com"},{"type": "personal", "address": "tom#mailinator.com"}] },
{ "firstName": "sandra", "lastName": "petes",
"phonenumber": [{"type": "mobile", "Number": "333"}, {"type": "home", "Number": "444"}],
"Email": [{"type": "business", "address": "sandra#xyz.com"}]
}
]}
I need to pull all values for data column with keys -
people->firstname
people->lastName
people->phonenumber->Number
people->Email->address
What I achieved so far is:
SELECT number
,jonb_array_length(jsonb_extract_path(data,'people')) as people_count
,jsonb_extract_path(data,'people','0','firstname') as FirstName
,jsonb_extract_path(data,'people','0','lastname') as LastName
,jsonb_extract_path(data,'people','0','email','Address'") as personEmail
,jsonb_extract_path(data,'people','0','phonenumber','Number') as personPhone
FROM listings
WHERE number='1';
However, this only gives me 0th element of people, I need to find all elements. Is there any way to achieve this in single query.
Thanks for your time!
You need to use the jsonb_array_elements() function to get all of the elements of the array. Since that function returns a set of rows, you need to use it as a row source.
SELECT '1' AS number,
jsonb_array_length(data->'people') AS people_count,
people->>'firstname' AS FirstName,
people->>'lastname' AS LastName,
people->'email'->0->>'Address' AS personEmail,
people->'phonenumber'->0->>'Number' as personPhone
FROM listings, jsonb_array_elements(data->'people') p(people)
WHERE number = '1';
This will result in a row for every person where number = '1'. The email and phone number objects are arrays too and I pick here just the first value. If you want all of them you need to just get the whole JSON arrays and then wrap this in an outer query where you do jsonb_array_elements() again.