How to query json objects using Postgres JSONB column - postgresql

I have JSON stored in a jsonb column:
{
"processedResult": {
"orderPayment": {
"paymentType": "VISA"
},
"store": "US"
}
}
What I have tried:
SELECT DISTINCT myData -> 'processedResult' -> 'orderPayment' -> 'paymentType'
FROM mytable
WHERE myData ->> 'processedResult' ->> 'store' = 'US'
The WHERE clause seems to be incorrect.
Desired Output:
VISA
Mastedcard
Postgres Version: PostgreSQL 11.13

You'll want to use
SELECT DISTINCT myData -> 'processedResult' -> 'orderPayment' ->> 'paymentType'
FROM mytable
WHERE myData -> 'processedResult' ->> 'store' = 'US'
Notice that -> returns the selected jsonb value, whereas ->> always returns a postgres text value (or NULL, or an error).

Related

Convert individual postgres jsonb array elements to row elements

I have to query a table with 2 columns, id and content. Id is just a uuid and the content column looks like
{
"fields": [
{
"001": "mig00004139229"
},
{
"856": {
"ind1": " ",
"ind2": " ",
"subfields": [
{
"u": "https://some.domain.com"
},
{
"z": "some text"
}
]
}
},
{
"999": {
"subfields": [
{
"i": "81be1acf-11df-4d13-a5c6-4838e3a808ee"
},
{
"s": "3a6aa357-8fd6-4451-aedc-13453c1f2296"
}
]
}
}
]
}
I need to select the id, 001, and 856 elements where the subfield "u" domain matches a string "domain.com" so the output would be
id
001
856
81be1acf-11df-4d13-a5c6-4838e3a808ee
mig00004139229
https://some.domain.com
If this were a flat table, the query would correspond with "select id, 001, 856 from table where 856 like '%domain.com%'"
I can select individual columns based on the criteria I need, but they appear in separate rows except the id which appears with any other individual field in a regular select statement. How would I get the other fields to appear in the same row since it's part of the same record?
Unfortunately, my postgres version doesn't support jsonb_path_query, so I've been trying something along the lines of:
SELECT id, jsonb_array_elements(content -> 'fields') -> '001',
jsonb_array_elements(content -> 'fields') -> '856' -> 'subfields'
FROM
mytable
WHERE....
This method returns the data I need, but the individual elements arrive on separate rows with the with the id in the first column and nulls for every element that is neither the 001 nor 856 e.g.
id
001
856
id_for_first_record
001_first_record
null
id_for_first_record
null
null
id_for_first_record
null
null
id_for_first_record
null
856_first_record
id_for_second_record
001_second_record
null
id_for_second_record
null
null
id_for_second_record
null
null
id_for_second_record
null
856_second_record
Usable, but clunky so I'm looking for something better
I think my query can help you. There are different ways to resolve this, I am not sure if this is the best approach.
I use jsonb_path_query() function with the path for the specified JSON value.
SELECT
id,
jsonb_path_query(content, '$.fields[*]."001"') AS "001",
jsonb_path_query(content, '$.fields[*]."856".subfields[*].u') AS "856"
FROM t
WHERE jsonb_path_query_first(content, '$.fields[*]."856".subfields[*].u' )::text ilike '%domain%';
Output:
id
001
856
81be1acf-11df-4d13-a5c6-4838e3a808ee
"mig00004139229"
"https://some.domain.com"
UPDATED: because of Postgresql version is prior to 12.
You could try something like this, but I think there must be a better approach:
SELECT
t.id,
max(sq1."001") AS "001",
max(sq2."856") AS "856"
FROM t
INNER JOIN (SELECT id, (jsonb_array_elements(content -> 'fields') -> '001')::text AS "001" FROM t) AS sq1 ON t.id = sq1.id
INNER JOIN (SELECT id, (jsonb_array_elements(jsonb_array_elements(content -> 'fields') -> '856' -> 'subfields') -> 'u')::text AS "856" FROM t) AS sq2 ON t.id = sq2.id
WHERE sq2."856" ilike '%domain%'
GROUP BY t.id;

Postgres jsonb: querying multi level

In a Postgres (9+) table there is a column of type jsonb with the following json:
{
"dynamicFields":[
{
"name":"040",
"subfields":[
{
"name":"a",
"value":"abc"
},
{
"name":"a",
"value":"xyz"
}
]
}
]
}
I would like to write a query that return only the rows where the field name equals 040 and subfield a equals xyz.
This is as far as I got, so far:
select e.obj from my_table
cross join lateral jsonb_array_elements(my_column-> 'dynamicFields') as e(obj)
where e.obj ->> 'name' = '040' and e.obj ->> 'subfields' #> '{"name": "a", "value": "xyz"}'::jsonb
How should this query be to achieve this?
e.obj ->> 'subfields' has a text result. You'll want to use e.obj -> 'subfields' that returns the jsonb value where the #> operator works. Also the containment checks needs to have another array as the right hand side, so that it will test whether all values in the right array are contained in the left array - it doesn't work to pass the element object directly.
select e.obj from my_table
cross join lateral jsonb_array_elements(my_column-> 'dynamicFields') as e(obj)
where e.obj ->> 'name' = '040' and e.obj -> 'subfields' #> '[{"name": "a", "value": "xyz"}]'::jsonb
-- ^ ^ ^
(online demo)
As you have an equality condition you can use the "contains" operator directly by providing a JSON value of what you want. There is no need to unnest the arrays.
select *
from my_table
where my_column -> 'dynamicFields' #> '[{"name": "040", "subfields": [{"name":"a", "value": "xyz"}]}]'
Starting with Postgres 12 an alternative is to a SQL/JSON path operator:
select *
from my_table
where my_column #? '$.dynamicFields[*] ? (#.name == "040").subfields[*] ? (#.name == "a" && #.value == "xyz")'

Convert date in jsonb (Postgres)

I have jsonb column data =
"{"history": [{"endDate": "30.06.2015", "classname": "Class A", "startDate": "2010-04-01", "numberAction": "0016", "positionName": "Teacher"},
{"endDate": "31.06.2010", "classname": "Class A", "startDate": "2005-08-10", "numberAction": "0015", "positionName": "Student"},
{"endDate": "2005.08.09", "classname": "Class B", "startDate": "2005-02-21", "numberAction": "0014", "positionName": " Student "}]}"
As you can see, the dates of the "endDate" in the array are not correct. Please tell me how they can be converted to the format YYYY-MM-DD?
My idle attempt:
UPDATE table
SET data = jsonb_set(data, '{endDate}', to_date('{endDate}', 'YYYY-MM-DD'), false)
WHERE id = 'UUID';
Answer =
update table
set data = data - 'history' ||
jsonb_build_object
( 'history'
, ( select jsonb_agg
( case when aa.value ->> 'endDate' like '%.%' then
aa.value - 'endDate' || jsonb_build_object
( 'endDate'
, to_date(aa.value ->> 'endDate','dd.mm.yyyy')
)
else
aa.value
end
)
from jsonb_array_elements(data -> 'history') as aa
)
)
WHERE uuid = 'UUID'
and exists ( select *
from jsonb_array_elements(data -> 'history') as aa
where aa.value ->> 'endDate' like '%.%'
);

Deep search within jsonb field PostgreSQL without knowking the key

Assuming the following jsonb data :
{
"3f210b": {
"hash": "e60ab24c549ac7060fzedfefe563e8493d759bb"
},
"Siaa1b3": {
"hash": "d24b37efgregr1a2c6db3b9334b3bf4fef3f22bfc9a43f"
}
}
Is it possible, in PostgreSQL, to do a deep jsonb search without know the key (3f210b & Siaa1b3) but we know the hash.
With Postgres 11 you will need to unnest that value:
select *
from the_table t
where exists (select *
from jsonb_each(t.the_column) as x(ky, item)
where x.item ->> 'hash' = 'd24b37efgregr1a2c6db3b9334b3bf4fef3f22bfc9a43f');
With Postgres 12, you could use the new JSON/Path expression:
select *
from the_table t
where jsonb_path_exists(the_column, '$.*.hash ? (# == "d24b37efgregr1a2c6db3b9334b3bf4fef3f22bfc9a43f")')

org.postgresql.util.PSQLException: ERROR: set-returning functions are not allowed in WHERE

I need a help for the below mentioned detail,
I am using Postgres + Spring-Data-JPA. Moreover, I have used the jsonb data type for storing data.
I am trying to execute a query, but it gives me the following error:
ERROR: set-returning functions are not allowed in WHERE
The cause here is that I have added a jsonb condition in the WHERE clause (kindly refer to the below query for more detail).
Query (I have renamed column name just because of hiding the actual column name):
select distinct
jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'firstName' as firstName,
jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'lastName' as lastName,
jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'country' as country
from
tale1 table10_
left outer join
table2 table21_
on table10_.end_user_id=table21_.end_user_id
left outer join
table3 table32_
on table10_.manufacturer_id=table32_.manufacturer_id
where
table21_.end_user_uuid=(
?
)
and table21_.is_active=true
and table32_.manufacturer_uuid=(
?
)
and table32_.is_active=true
and table10_.is_active=true
and table32_.is_active=true
and jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'action' = ('PENDING')
order by
jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'firstName',
jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'lastName'
limit ?
The following line in the above query is causing the error:
and jsonb_array_elements(initiated_referral_detail->'listOfAttribue')::jsonb
->> 'action' = ('PENDING')
Can anyone please guide me about how do fetch data from the inner JSON? Especially in my case I have an inner List and a few elements inside.
I recommend a lateral join with jsonb_array_elements for cases like that. Here is an example:
CREATE TABLE tale1 (
id integer PRIMARY KEY,
initiated_referral_detail jsonb NOT NULL
);
INSERT INTO tale1 VALUES
(1, '{
"name": "one",
"listOfAttribue": [
{ "id": 1, "action": "DONE"},
{ "id": 2, "action": "PENDING" },
{ "id": 3, "action": "ACTIVE" }
]
}');
INSERT INTO tale1 VALUES
(2, '{
"name": "two",
"listOfAttribue": [
{ "id": 1, "action": "DONE"},
{ "id": 2, "action": "ACTIVE" }
]
}');
To find all ids where the associated JSON contains an array element with action = PENDING, you can query like this:
SELECT DISTINCT id
FROM tale1 CROSS JOIN LATERAL
jsonb_array_elements(initiated_referral_detail -> 'listOfAttribue') AS attr
WHERE attr ->> 'action' = 'PENDING';