Get nested value in JSONB column POSTGRESQL - postgresql

I have a table guest_group with a jsonb column. I want to query one ID where the ID_Context is equal to protelIO.
Here the column of the table:
[
{
"protelSurname":"Smith",
"servicio_tags":[
"protel-info"
],
"protelUniqueID":"[{\"ID\":\"294623726\",\"Type\":\"21\",\"ID_Context\":\"GHA\"},{\"ID\":\"4842148\",\"Type\":\"1\",\"ID_Context\":\"protelIO\"}]",
"protelGivenName":"Seth"
},
{
"value":"test",
"display_name":"Traces",
"servicio_tags":[
"trace"
]
}
]
My try:
SELECT field ->>'protelUniqueID'
FROM guest_group gg
 cross join lateral
jsonb_array_elements(custom_fields) AS field
WHERE value #> '{"servicio_tags": ["protel-info"]}'::jsonb
This gave me:
[{"ID":"294623726","Type":"21","ID_Context":"GHA"},{"ID":"4842148","Type":"1","ID_Context":"protelIO"}]
How can I go the last mile an only get the value of the ID key with the value key pair "ID_Context":"protelIO"?
I appreciate your help!

This will get you the desired result I think. Not very pretty perhaps :-)
select * from (select jsonb_array_elements(f) from (
select (field ->>'protelUniqueID')::jsonb f
FROM guest_group gg,
lateral jsonb_array_elements(custom_fields) AS field
WHERE value #> '{"servicio_tags": ["protel-info"]}'::jsonb
) d(f)) dd(x)
where x->>'ID_Context'='protelIO';

Related

Update table with newly added column containing data from the same table old column, but modified (flattened) jsonb

So i've came across issue with having to migrate data from one column to "clone" of itself with different jsonb schema -> i need to parse the json from
["keynamed": [...{"type": "type_info", "value": "value_in_here"}]]into something plain object with key:value - dictionary like {"type_info": "value_in_here" ,...}
so far i've tried with subqueries and json functions in subquery + switch case to map "type" to "type_info" and then use jsonb_build_object(), but this takes data from the wole table and i need to have it on update with data from row - is there anything simpler than doing N subqueries closest way i've came with is:
select
jsonb_object_agg(t.k, t.v):: jsonb as _json
from
(
select
jsonb_build_object(type_, _value) as _json
from
(
select
_value,
CASE _type
...
END type_
from
(
select
(datasets ->> 'type') as _type,
datasets -> 'value' as _value
from
(
select
jsonb_array_elements(
values
-> 'keynamed'
) as datasets
from
table
) s
) s
) s
) s,
jsonb_each(_json) as t(k, v);
But i have no idea how to make it row specyfic and apply to simple update like:
UPDATE table
SET table.new_field = (subquery with parsed dict in json)
Any ideas/tips how to solve it with plain PSQL without any external support?
The expected output of the table would be:
id | old_value | new_value
----------------+-------------------------------------+------------------------------------
1 | ["keynamed": [...{"type": "type_info", "value": "value_in_here"}]] | {"type_info": "value_in_here" ,...}
According to postgres documents you can use update with select table and use join pattern update document
Sample:
UPDATE accounts SET contact_first_name = first_name,
contact_last_name = last_name
FROM salesmen WHERE salesmen.id = accounts.sales_id;
If I understand correctly, below query can help you. but I can't test because I haven't sample data and I don't know this query has syntax error or not.
update table t
set new_value = tmp._json
from (
select
id,
jsonb_object_agg(t.k, t.v):: jsonb as _json
from
(
select
id,
jsonb_build_object(type_, _value) as _json
from
(
select
id,
_value,
CASE _type
...
END type_
from
(
select
id,
(datasets ->> 'type') as _type,
datasets -> 'value' as _value
from
(
select
id,
jsonb_array_elements(
values
-> 'keynamed'
) as datasets
from
table
) s
) s
) s
) s,
jsonb_each(_json) as t(k, v)
group by id) tmp
where tmp.id = t.id;

Extract multiple values from JSONB in Postgres

I have a table that looks like this:
id
attrs
1
{"a":{"kind":"kind_1", "value":"val_1"}, "b":{"kind":"kind_2", "value":"val_2"}
2
{"c":{"kind":"kind_3", "value":"val_1"}}
3
{"a":{"kind":"kind_1", "value":"val_1"}, "d":{"kind":"kind_4", "value":"val_4"}, .....
I would like to extract all the unique value, so the output would be:
val_1
val_2
val_4
...
I tried to use jsonb_each method for it, but without any luck
You can use a JSON Path query:
select distinct v.item #>> '{}'
from the_table t
cross join jsonb_array_elements(jsonb_path_query_array(t.attrs, '$.**.value')) as v(item);
The v.item #>> '{}' is a trick to convert a scalar JSON value to text (because casting it wouldn't work)
Alternatively you can use jsonb_each() twice:
select distinct v.value
from the_table t
cross join jsonb_each(t.attrs) as i(key, item)
cross join jsonb_each_text(i.item) as v(key, value)
where v.key = 'value'

How to check if json inner field is EMPTY?

One of the column ( called details ) in my table is of jsonb data type and have data format somthing like this:
{"media_height":"350", "media_height":"450", "media_alt":"", "file_name":"myfile.jpeg"}
This field I am taking in case when because I want to mark the records of missing alt text.
SELECT
distinct ON ( p.property_name )
p.id, p.property_name,
CASE
WHEN mma.id IS NULL THEN 'Z'
WHEN mma.details->'media_alt'::TEXT IS NULL THEN 'NO'
ELSE 'YES' END as has_media_alt
FROM properties p
LEFT JOIN marketing_media_associations mma ON ( mma.reference_id = p.id )
GROUP BY p.id, p.property_name , mma.details->'media_alt', mma.id
ORDER BY p.property_name, has_media_alt ASC
The above query gives me accurate results for Z, but it never goes in NO block. What I am missing here?
An empty string is not the same as NULL, you probably want:
WHEN nullif(mma.details->>'media_alt', '') IS NULL THEN 'NO'
You don't need to cast to text, if you use ->> which returns the value as text directly.

PostgreSQL - Add key to each objects of an JSONB array

My database contains a table which has a column with jsonb type, and I want to update a part of these data using functions/operators from postgreSQL. Given we have this:
{
"A":[
{"index":"1"},
{"index":"2"}
],
"B":[
{"index":"3"},
{"index":"4"}
]
}
Let's say we went to add a key with an empty array to objects from "A" array, in order to have:
{
"A":[
{"index":"1", "myArray":[]},
{"index":"2", "myArray":[]}
],
"B":[
{"index":"3"},
{"index":"4"}
]
}
How can I proceed?
I've already tried this kind of things without success:
UPDATE myTable SET myColumn = (myColumn::jsonb)->>'A' || '{"myArray":[]}'
UPDATE myTable SET myColumn = (
SELECT jsonb_agg(jsonb_set(
element,
array['A'],
to_jsonb(((element ->> 'A')::jsonb || '{"myArray":[]}')::jsonb)
))
FROM jsonb_array_elements(myColumn::jsonb) element
)::json
UPDATE myTable SET myColumn = (
SELECT jsonb_each((element ->> 'A')::jsonb) || '{"myArray":[]}'::jsonb
FROM jsonb_array_elements(myColumn::jsonb) element
)::json
Obviously, all of these tests have been big failure. I have difficulties to understand how works postgreSQL functions.
Somebody can help?
The approach with jsonb_array_elements and jsonb_set was the right idea, but somehow you nested them the wrong way round:
UPDATE myTable SET myColumn = jsonb_set(myColumn, '{A}', (
SELECT jsonb_agg( element || '{"myArray":[]}' )
FROM jsonb_array_elements(myColumn -> 'A') element
));
(online demo)
Btw if your column already has jsonb data type, you shouldn't need any casts.

PostgreSQL - jsonb_each

I have just started to play around with jsonb on postgres and finding examples hard to find online as it is a relatively new concept.I am trying to use jsonb_each_text to printout a table of keys and values but get a csv's in a single column.
I have the below json saved as as jsonb and using it to test my queries.
{
"lookup_id": "730fca0c-2984-4d5c-8fab-2a9aa2144534",
"service_type": "XXX",
"metadata": "sampledata2",
"matrix": [
{
"payment_selection": "type",
"offer_currencies": [
{
"currency_code": "EUR",
"value": 1220.42
}
]
}
]
}
I can gain access to offer_currencies array with
SELECT element -> 'offer_currencies' -> 0
FROM test t, jsonb_array_elements(t.json -> 'matrix') AS element
WHERE element ->> 'payment_selection' = 'type'
which gives a result of "{"value": 1220.42, "currency_code": "EUR"}", so if i run the below query I get (I have to change " for ')
select * from jsonb_each_text('{"value": 1220.42, "currency_code": "EUR"}')
Key | Value
---------------|----------
"value" | "1220.42"
"currency_code"| "EUR"
So using the above theory I created this query
SELECT jsonb_each_text(data)
FROM (SELECT element -> 'offer_currencies' -> 0 AS data
FROM test t, jsonb_array_elements(t.json -> 'matrix') AS element
WHERE element ->> 'payment_selection' = 'type') AS dummy;
But this prints csv's in one column
record
---------------------
"(value,1220.42)"
"(currency_code,EUR)"
The primary problem here, is that you select the whole row as a column (PostgreSQL allows that). You can fix that with SELECT (jsonb_each_text(data)).* ....
But: don't SELECT set-returning functions, that can often lead to errors (or unexpected results). Instead, use f.ex. LATERAL joins/sub-queries:
select first_currency.*
from test t
, jsonb_array_elements(t.json -> 'matrix') element
, jsonb_each_text(element -> 'offer_currencies' -> 0) first_currency
where element ->> 'payment_selection' = 'type'
Note: function calls in the FROM clause are implicit LATERAL joins (here: CROSS JOINs).
WITH testa AS(
select jsonb_array_elements
(t.json -> 'matrix') -> 'offer_currencies' -> 0 as jsonbcolumn from test t)
SELECT d.key, d.value FROM testa
join jsonb_each_text(testa.jsonbcolumn) d ON true
ORDER BY 1, 2;
tetsa get the temporal jsonb data. Then using lateral join to transform the jsonb data to table format.