[
{
"name" : "A",
"key" : "KA"
},
{
"name" : "B",
"key" : "KB"
}
]
Given a column containing the above data,
I use select jsonb_path_query_array(column, '$.key') to get the output [KA, KB]
However this doesn't work in Postgres-11. Are there any alternatives for the same ?
Yes. This yields a Postgres array. Use jsonb_agg instead of array_agg if you need a JSON array.
select array_agg(j ->> 'key')
from jsonb_array_elements(column) t(j);
Update
select
(select array_agg(j ->> 'key') from jsonb_array_elements(column) t(j))
from the_table;
Related
There is such a data structure:
Column "recipient" type jsonb
{
"phoneNumbers": [
{
"isDefault": true,
"type": "MOBILE",
"number": "3454654645"
},
{
"isDefault": true,
"type": "MOBILE",
"number": "12423543645"
}
]
}
I need to write a search request by number. In the postgres documentation, I did not find a search by value in an array, only it is obtained by an index. It doesn't suit me
I made a query like this, it gets executed, but are there any other ways to search through an array?
SELECT *
FROM my_table
WHERE recipient -> 'phoneNumbers' #> '[{"number":3454654645}]'
That's pretty much the best way, yes.
If you have a (GIST) index on recipient the index would not be used by your condition. But the following could make use of such an index:
SELECT *
FROM my_table
WHERE recipient #> '["phoneNumbers": {"number":3454654645}]}'
If you are using Postgres 12 or later, you can also use a JSON path expression:
SELECT *
FROM my_table
WHERE recipient ## '$.phoneNumbers[*].number == "12423543645"'
If you can't pass a JSON object to your query, you can use an EXISTS sub-select:
SELECT mt.*
FROM my_table mt
WHERE EXISTS (SELECT *
FROM jsonb_array_elements_text(mt.recipient -> 'phoneNumbers') as x(element)
WHERE x.element ->> 'number' = '3454654645')
The '3454654645' can be passed as a parameter to your query. This will never make use of an index though.
I have a postgresql table called datasource with jsonb column called data. It has the following structure:
{
"key1":{"param1" : "value1", "param2" : "value2"},
"key2":{"param2_1" : "value2_1", "param2_2" : "value2_2"},
"key3":{"param3_1" : "value3_1", "param3_2" : "value3_2"}
}
Is there any way to write some UPDATE script to transform given JSON to the following:
[
{"key": "key1", "param1" : "value1", "param2" : "value2"},
{"key": "key2", "param2_1" : "value2_1", "param2_2" : "value2_2"},
{"key": "key3", "param3_1" : "value3_1", "param3_2" : "value3_2"}
]
You can unnest the object to rows in a lateral join, then aggregate back into an array:
select d.*, x.*
from datasource d
cross join lateral (
select jsonb_agg(jsonb_build_object('key', j.k) || j.v) new_data
from jsonb_each(d.data) as j(k, v)
) x
Demo on DB Fiddle - with jsonb_pretty() enabled:
If you wanted an update statement:
update datasource d
set data = (
select jsonb_agg(jsonb_build_object('key', j.k) || j.v)
from jsonb_each(d.data) as j(k, v)
)
I have a jsonb column named items in Postgres 10.12 like this:
{
"items": [
{
"itemQty": 2,
"itemName": "snake"
},
{
"itemQty": 1,
"itemName": "x kodiyum"
}
]
}
Now I want to convert itemQty type to string for every array element so that the new values are like this:
{
"items": [
{
"itemQty": "2",
"itemName": "snake"
},
{
"itemQty": "1",
"itemName": "x kodiyum"
}
]
}
How do I do this? I have gone through the documentation for Postgres jsonb and couldn't figure out.
On the server-side, I am using Spring boot and Hibernate with com.vladmihalcea.hibernate.type.json (Hibernate Types 52) if it helps.
Thanks
You could unnest the array, modify the elements, and then rebuild it. Assuming that the primary key of your table is id, that would be:
select jsonb_build_object(
'items', jsonb_agg(
jsonb_build_object(
'itemQty', (x.obj ->> 'itemQty')::text,
'itemName', x.obj ->> 'Name'
)
)
)new_items
from mytable t
cross join lateral jsonb_array_elements(t.items -> 'items') as x(obj)
group by id
Note that the explicit cast to ::text is not really needed here, as ->> extract text values anyway: I kept it because it makes the intent clearer.
If you want an update statement:
update mytable t
set items = (
select jsonb_build_object(
'items', jsonb_agg(
jsonb_build_object(
'itemQty', (x.obj ->> 'itemQty')::text,
'itemName', x.obj ->> 'Name'
)
)
)
from jsonb_array_elements(t.items -> 'items') as x(obj)
)
Demo on DB Fiddle
I'm new with jsonb request and i got a problem. Inside an 'Items' table, I have 'id' and 'data' jsonb. Here is what can look like a data:
[
{
"paramId": 3,
"value": "dog"
},
{
"paramId": 4,
"value": "cat"
},
{
"paramId": 5,
"value": "fish"
},
{
"paramId": 6,
"value": "",
"fields": [
{
"paramId": 3,
"value": "cat"
},
{
"paramId": 4,
"value": "dog"
}
]
},
{
"paramId": 6,
"value": "",
"fields": [
{
"paramId": 5,
"value": "cat"
},
{
"paramId": 3,
"value": "dog"
}
]
}
]
The value in data is always an array with object inside but sometimes the object can have a 'fields' value with objects inside. It is maximum one level deep.
How can I select the id of the items which as for example an object containing "paramId": 3 and "value": "cat" and also have an object with "paramId": 5 and "value" LIKE '%ish%'.
I already have found a way to do that when the object is on level 0
SELECT i.*
FROM items i
JOIN LATERAL jsonb_array_elements(i.data) obj3(val) ON obj.val->>'paramId' = '3'
JOIN LATERAL jsonb_array_elements(i.data) obj5(val) ON obj2.val->>'paramId' = '5'
WHERE obj3.val->>'valeur' = 'cat'
AND obj5.val->>'valeur' LIKE '%ish%';
but I don't know how to search inside the fields array if fields exists.
Thank you in advance for you help.
EDIT:
It looks like my question is not clear. I will try to make it better.
What I want to do is to find all the 'item' having in the 'data' column objects who match my search criteria. This without looking if the objects are at first level or inside a 'fields' key of an object.
Again for example. This record should be selected if I search:
'paramId': 3 AND 'value': 'cat
'paramId': 4 AND 'value': LIKE '%og%'
the matching ones are in the 'fields' key of the object with 'paramId': 6 and I don't know how to do that.
This can be expressed using a JSON/Path expression without the need for unnesting everything
To search for paramId = 3 and value = 'cat'
select *
from items
where data #? '$[*] ? ( (#.paramId == 3 && #.value == "cat") || exists( #.fields[*] ? (#.paramId == 3 && #.value == "cat")) )'
The $[*] part iterates over all elements of the first level array. To check the elements in the fields array, the exists() operator is used to nest the expression. #.fields[*] iterates over all elements in the fields array and applies the same expression again. I don't see a way how repeating the values could be avoided though.
For a "like" condition, you can use like_regex:
select *
from items
where data #? '$[*] ? ( (#.paramId == 4 && #.value like_regex ".*og.*") || exists( #.fields[*] ? (#.paramId == 4 && #.value like_regex ".*og.*")) )'
For now I have found a solution but it is not really clean and I don't know how it will perform in production with 10M records.
SELECT i.id, i.data
FROM ( -- A;
select it.id, it.data, i as value
from items it,
jsonb_array_elements(it.data) i
union
select it.id, it.data, f as value
from items it,
jsonb_array_elements(it.data) i,
jsonb_array_elements(i -> 'fields') f
) as i
WHERE (i.value ->> 'paramId' = '5' -- B1;
AND i.value ->> 'value' LIKE '%ish%')
OR (i.value ->> 'paramId' = '3' -- B2;
AND i.value ->> 'value' = 'cat')
group by i.id, i.data
having COUNT(*) >= 2; -- C;
A: I "flatten" the first and second level (second level is in 'fields' key)
B1, B2: These are my search criteria
C: I make sure the fields have all the criteria matching. If 3 criteria --> COUNT(*) >=3
It really doesn't look clean to me. It is working for dev purpose but I think there is a better way to do it.
If somebody have an idea Big thanks to him/her!
I have a json array similar to this structure in a column of my database -
{
"id": "123abc",
"Y/N": "Y",
"Color": "Purple",
"arr": [ {
"time": 1210.55
"person": "Sean"
"action": "yes" //The values for this field can only be 'yes', 'no, 'maybe'
},
{
"time": 1230.19
"person": "Linda"
"action": "no"
} ],
}
I need to pull all the corresponding attributes based on 2 criteria of an object in the "arr" array. I want to get the latest "arr" object based on the "time" (highest value) but only pull this index if the "action" is equal to 'no' or 'yes', so exclude all the objects when "action" = "maybe".
I have tried using a WHERE statement to have a "time" range set and ORDER BY DESC to pull the latest entry and return the entire "arr". This just returns the highest value of "time" but returns all the attributes when "action" = "maybe" but I want to return the objects with only "yes" or "no".
Here is the current query I have -
SELECT jsonb_build_object('ID', t.col -> '_id',
'Yes or No', t.col -> 'Y/N',
'arr', x.elem)
FROM tbl t
CROSS JOIN LATERAL (
SELECT elem
FROM jsonb_array_elements(t.col -> 'arr') a(elem)
WHERE a.elem -> 'time' between '1110.23' and '1514.12'
AND t.col ->> 'Color' = 'Purple'
ORDER BY a.elem -> 'time' DESC NULLS LAST
LIMIT 1
) x;
The query is returning the latest object in the array with the highest time but it is also returning objects when "action" = "maybe". I have tried adding AND a.elem -> 'action' = 'yes' after the WHERE statement but receive an error saying the Token "yes" is invalid.
Is it possible to return an object with the largest "time" that has the "action" attribute equal to "yes" or "no" only?
Your code is alternatively using -> and ->>, but it isn't using them correctly.
Lots of good details here: What is the difference between `->>` and `->` in Postgres SQL?.
Everywhere in the code using -> should be ->> if you try that in the where clause with AND a.elem ->> 'action' = 'yes' it should work.