PostgresSQL nested jsonb update value of complex key/value pairs - postgresql

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

Related

Remove entire json object from JSON array

I'm trying to update the list of contacts by deleting whatever contact is requested to be deleted by user input. In other words, trying to remove an entire JSON object from a JSON array in my PostgreSQL database from a Node.js script, but I get error
error: null value in column "info" of relation "user_emails" violates
not-null constraint
I double-checked and the value and everything is there. When I try it here online it works, but on my server it returns the error. How can I fix this?
DROP table if exists user_emails;
CREATE table user_emails (
id serial not null PRIMARY KEY,
info jsonb NOT NULL
);
insert into user_emails(info) values('{
"userid": "4",
"mailbox": "johndoe#example.com",
"contacts": [
{
"id": "ghr3gk8dez4",
"email": "janedoe#gmail.com",
"last_name": "Doe",
"first_name": "Jane",
"date_created": "2022-05-08T20:52:47.967Z"
},
{
"id": "th2lypvoxpr1652045110763",
"email": "aldoe#gmail.com",
"last_name": "Doe",
"first_name": "Al",
"date_created": "2022-05-08T21:25:10.763Z"
},
{
"id": "ld123tqicmj1652045372671",
"email": "stdoe#gmail.com",
"last_name": "Doe",
"first_name": "Stella",
"date_created": "2022-05-08T21:29:32.671Z"
},
{
"id": "1ltbrpbj8xf1652045768004",
"email": "mdoe#mail.com",
"last_name": "Doe",
"first_name": "Marta",
"date_created": "2022-05-08T21:36:08.004Z"
},
{
"id": "1dgntfwvsmf1652045832589",
"email": "nala#mail.com",
"last_name": "La",
"first_name": "Na",
"date_created": "2022-05-08T21:37:12.589Z"
},
{
"id": "ll3z1n0jkhc1652045984538",
"email": "bdoe#mail.com",
"last_name": "doe",
"first_name": "bruno",
"date_created": "2022-05-08T21:39:44.538Z"
},
{
"id": "kzr996xxxt1652046050118",
"email": "pp#mail.com",
"last_name": "Perf",
"first_name": "Perf",
"date_created": "2022-05-08T21:40:50.118Z"
},
{
"id": "41bovnvsihq1652046121940",
"email": "mmd#mm.com",
"last_name": "Doe",
"first_name": "Melinda",
"date_created": "2022-05-08T21:42:01.940Z"
},
{
"id": "tnjlj4dcg2b1652046154937",
"email": "keke#j.com",
"last_name": "Kee",
"first_name": "Kee",
"date_created": "2022-05-08T21:42:34.937Z"
},
{
"id": "hor0wafkuj1652046684582",
"email": "jojo#mail.com",
"last_name": "Jo",
"first_name": "Jo",
"date_created": "2022-05-08T21:51:24.582Z"
}
],
"auto_reply": false,
"email_name": "johndoe",
"signatures": [],
"domain_name": "example.com",
"date_created": "2022-05-08T20:39:54.881Z",
"forward_email": [],
"auto_reply_messages": []
}');
this is my UPDATE
UPDATE user_emails SET info = (SELECT jsonb_agg(j)
FROM jsonb_array_elements(user_emails.info->'contacts') as t(j)
WHERE j ->> 'id' not in ('ghr3gk8dez4'));
SELECT * FROM user_emails;
jsonb_agg, like so many other aggregate functions, returns NULL if there are no rows to aggregate. You might be looking to COALESCE it to an empty array instead:
UPDATE user_emails
SET info = jsonb_set(
user_emails.info,
'{contacts}',
COALESCE(
(SELECT jsonb_agg(j)
FROM jsonb_array_elements(user_emails.info->'contacts') as t(j)
WHERE j ->> 'id' not in ('ghr3gk8dez4')
),
'[]'::jsonb
)
);
I don't think there is an efficient way to do that using only built-in functions.
There is an operator #- that removes an element by specifying the path, e.g. info #- '{contacts, 0}' would do what you want. However, it's not straight forward to build such a "path array" directly.
I would write a function that finds the index of the contact to be deleted and generates the path array:
create or replace function find_entry(p_info jsonb, p_id text)
returns text[]
as
$$
select array['contacts', (idx - 1)::text]
from jsonb_array_elements(p_info -> 'contacts') with ordinality as t(element, idx)
where t.element ->> 'id' = p_id
limit 1;
$$
language sql;
The -1 is necessary because SQL uses 1-based numbering, but in JSON arrays start at zero.
With that function you can then do:
update user_emails
set info #- find_entry(info, 'ghr3gk8dez4')
where ...

Search inside array of array in JSONB column in Postgresql

I have a JSONB column in my PostgreSQL database. The data looks like this:
{
"cars":
[
{
"id": 1,
"brand": "BMW"
"parts":
[
{
"partId": 5,
"type": "battery"
}
]
},
{
"id": 2,
"brand": "Mercedes"
"parts":
[
{
"partId": 5,
"type": "battery"
},
{
"partId": 6,
"type": "engine"
}
]
}
]
}
Is there any way that I can search for all cars that have a part with type "battery"? How can I search inside of cars array and then inside of the parts array of each car element?
As it's not clear in your question that what output you want. So I am assuming that you want id and brand name in output:
so you try this:
select distinct x.y->>'id', x.y->>'brand'
from test
cross join lateral jsonb_array_elements(data->'cars') x(y)
cross join lateral jsonb_array_elements(x.y->'parts') a(b)
where a.b->>'type'='battery'
DEMO

Extracting info out of lists inside a jsonb

I have a with a jsonb column called jsonb that contains data in the following format.
{
"stuff": [
{
"name": "foo",
"percent": "90.0000"
},
{
"name": "bar",
"percent": "10.0000"
}
],
"countries": [
{
"name": "USA",
"value": "30"
},
{
"name": "Canada",
"value": "25"
},
{
"name": "Mexico",
"value": "20"
},
{
"name": "Ecuador",
"value": "10"
}
]
}
I am having a lot of trouble working with this data. Specifically what I want to do is find all the different values "name" can have in "stuff" as well as in "countries", kind of like a SELECT distinct.
But my problem is that I can't seem to extract anything useful from this jsonb. My approach so far was to do
SELECT jsonb->>'stuff' FROM table, but this only gave me a column of type text which contained [{"name": "foo","percent": "90.0000"},{"name": "bar","percent": "10.0000"}].
But since this is text I can't really do anything with it. I also tried SELECT jsonb_array_elements_text(jsonb) FROM table but that returned the following Error:
ERROR: cannot extract elements from an object
SQL state: 22023
Any help with working with this format of data is greatly appreciated!
You need to unnest each array separately and then create a union on the result of those two steps:
select c.x ->> 'name'
from the_table
cross join lateral jsonb_array_elements(json_column -> 'countries') as c(x)
union
select s.x ->> 'name'
from the_table
cross join lateral jsonb_array_elements(json_column -> 'stuff') as s(x);
Online example: https://rextester.com/ZEOIXF91294

How to get two same attribute in azure cosmos db

I am creating a service using cosmos db.I am trying to creating a search query.
Query :
SELECT product.Name,product1.Name
FROM catalog
join industry in catalog.Industy
join category in industry.Category
join product1 in category.Product
join Subcategory in category.Subcategory
join product in Subcategory.Product
WHERE CONTAINS(product1.Name,'dg')
But i can not able to get both product and product list . it give me the error. Name already used.
error:
Object creation error, property name 'Name' specified more than once
Tree that i am trying to fetch :
[
{
"id": "string",
"industy": [
{
"id": "string",
"category": [
{
"id": "string",
"subcategory": [
{
"id": "string",
"product": [
{
"id": "string",
"methodOfPreparation": [
{
"id": "string",
}
],
"addons": [
{
"id": "string"
}
]
}
]
}
],
"product": [
{
"id": "string",
"methodOfPreparation": [
{
"id": "string"
}
],
"addons": [
{
"id": "string"
}
]
}
]
}
]
}
]
}
]
expect Output
product[],prodcut1[]
How can i solve this?
Firstly, the error could be solved if you use alias as #Zohar mentioned in the comment.
SELECT product.Name as productName,product1.Name as product1Name
FROM catalog
join industry in catalog.industy
join category in industry.category
join product1 in category.product
join Subcategory in category.subcategory
join product in Subcategory.product
The reason is that every retrieved item is an obj, the format of results is an array consists of many objects. The object can't accept duplicate column names.
If you want to get the format like product[],prodcut1[],you need to loop the result and assemble by yourself.(For example,use stored procedure)

Apache Druid sql query conversion to json based query

I am trying to convert the following druid sql query to a druid json query, as one of the columns i have is a multi-value dimension for which druid does not support a sql style query.
My sql query:
SELECT date_dt, source, type_labels, COUNT(DISTINCT unique_p_hll)
FROM "test"
WHERE
type_labels = 'z' AND
(a_id IN ('a', 'b', 'c') OR b_id IN ('m', 'n', 'p'))
GROUP BY date_dt, source, type_labels;
unique_p_hll is an hll column with uniques.
The druid json query i came up with is following:
{
"queryType": "groupBy",
"dataSource": "test",
"granularity": "day",
"dimensions": ["source", "type_labels"],
"limitSpec": {},
"filter": {
"type": "and",
"fields": [
{ "type": "selector", "dimension": "type_labels", "value": "z" },
{ "type": "or", "fields": [
{ "type": "in", "dimension": "a_id", "values": ["a", "b", "c"] },
{ "type": "in", "dimension": "b_id", "values": ["m", "n", "p"] }
]}
]
},
"aggregations": [
{ "type": "longSum", "name": "unique_p_hll", "fieldName": "p_id" }
],
"intervals": [ "2018-08-01/2018-08-02" ]
}
But the json query seems to be returning empty resultset.
I can see the output correctly in Pivot UI. Though the array column type_labels values show up as {"array_element": "z"} instead of simply "z".
Does the query return empty string, or does it return a formatted JSON with zero records?
If the former, I can suggest a couple of leads for debugging this issue:
Make sure that the query is properly sent to the Broker, as shown in Druid's query tutorial:
curl -X 'POST' -H 'Content-Type:application/json' -d #query-file.json http://<BROKER-IP>:<BROKER-PORT>/druid/v2?pretty
Also, check the Broker's log for errors.