Filtering jsonb array in postgreSql - sql

I have a table with a jsonb field of the following type:
[
{
"Id": "98f2a810-8f29-4707-9098-759eec83b2d5",
"Data": {
"value": 6
},
"Created": "2022-12-15T10:04:04.8100461Z"
},
{
"Id": "950df931-abfd-460f-916f-14b00d3e1593",
"Data": {
"value": "hello world"
},
"Created": "2022-12-15T10:04:04.9270694Z"
}
]
Now I can filter records only by decomposing the jsonb array into separate lines and join them:
LEFT JOIN LATERAL jsonb_array_elements(""attributes"") as x(attribute) ON TRUE
and filter:
WHERE x.attribute ->> 'Id' ILIKE '98f2a810-8f29-4707-9098-759eec83b2d5' and cast(x.attribute -> 'Data' ->> 'value' as numeric) = 6
But if I need filter by two attributes with AND operator it doesn't work(the results exclude each other).
How I can filter jsonb array without separate and join elements?

Related

Querying JSONB having array elements in Postgres

I have a table named settings with the columns program_id(number), client_id(number), filters(Jsonb) and filters column has jsonb data in such format-
{
"sources": [
{
"conditions": [
{
"value": [
{
"id": 1211,
"name": "ABM BETA INVITE LIST",
"isSelected": true
}
],
"condition": "one of the following"
}
],
"objectType": "SmartLists",
"subscriptionId": 1173,
"integrationType": "mkto"
}
],
"listType": "All Accounts",
"programId": 30203,
"noOfAccounts": null,
"expiryDuration": 0,
"subscriptionId": null,
"updateFrequency": null
}
I now want to retrieve all the records from table settings where filters.sources[0].integrationType = 'mkto'. I have tried this query but gives me error of set-returning functions are not allowed in WHERE-
select * from settings where (jsonb_array_elements(filters -> 'sources') ->> 'integrationType' = 'mkto');
I now want to retrieve all the records from table settings where filters.sources[0].integrationType = 'mkto'.
Using the #>> operator:
SELECT *
FROM settings
WHERE filters #>> '{sources, 0, integrationType}' = 'mkto';
fiddle
filters #>> '{sources, 0, integrationType}' is the same as:
filters -> 'sources' -> 0 ->> 'integrationType'
filters['sources'][0]['integrationType'] #>> '{}' -- for Postgres 14+
But do you really only want to look at the first array element?

How to update value in nested json Postgres

I have following JSON stored in "Info" column
{
"customConfig": {
"isCustomGoods": 1
},
"new_addfields": {
"data": [
{
"val": {
"items": [
{
"Code": "calorie",
"Value": "365.76"
},
{
"Code": "protein",
"Value": "29.02"
},
{
"Code": "fat",
"Value": "23.55"
},
{
"Code": "carbohydrate",
"Value": "6.02"
},
{
"Code": "spirit",
"Value": "1.95"
}
],
"storageConditions": "",
"outQuantity": "100"
},
"parameterType": "Nutrition",
"name": "00000000-0000-0000-0000-000000000001",
"label": "1"
},
{
"name": "b4589168-5235-4ec5-bcc7-07d4431d14d6_Для ресторанов",
"val": "true"
}
]
}
}
I want to update value of nested json
{
"name": "b4589168-5235-4ec5-bcc7-07d4431d14d6_Для ресторанов",
"val": "true"
}
and set "val"to "Yes" str so the result should be like
{
"name": "b4589168-5235-4ec5-bcc7-07d4431d14d6_Для ресторанов",
"val": "Yes"
}
How can i do that ? Assuming that i need to update this value in json for many records in database
Considering you have a constant JSON Structure and a primary key in your table. Idea is to get the exact path of element val having value true (which can be at any index in the array) then replace it with desired value. So you can write your query like below:
with cte as (
select
id,
('{new_addfields,data,'||index-1||',val}')::text[] as json_path
from
test,
jsonb_array_elements(info->'new_addfields'->'data')
with ordinality arr(vals,index)
where
arr.vals->>'val' ilike 'true'
)
update test
set info = jsonb_set(info,cte.json_path,'"Yes"',false)
from cte
where test.id=cte.id;
DEMO
We can use jsonb_set() which is available from Postgres 9.5+
From Docs:
jsonb_set(target jsonb, path text[], new_value jsonb [, create_missing boolean])
Query to update the nested object:
UPDATE temp t
SET info = jsonb_set(t.info,'{new_addfields,data,1,val}', jsonb '"Yes"')
where id = 1;
It can also be used in select query:
SELECT
jsonb_set(t.info,'{new_addfields,data,1,val}', jsonb '"Yes"')
FROM temp t
LIMIT 1;

Remove json object from array of jsons in postgres

I have the following json in my column in posgres database.
Column items in table products
{
"name": "Super name",
"type": "Green",
"information": [
{
"name": "first",
"value": "high"
},
{
"name": "second",
"value": "medium"
}
],
}
I want to delete json object using jsonb
{
"name": "second",
"value": "medium"
}
I try this:
update products set items = jsonb_set(items, '{information}', (items->'information') - '{"name": "second", "value": "medium"}');
I tried different approaches but nothing work correctly.
The "minus" operator doesn't work on objects, only keys. And it doesn't work on arrays of objects either.
I would write a function that removes a single object from an array.
create function remove_element(p_input jsonb, p_to_remove jsonb)
returns jsonb
as
$$
select coalesce(jsonb_agg(t.item order by t.idx), '[]')
from jsonb_array_elements(p_input) with ordinality as t(item, idx)
where t.item <> p_to_remove;
$$
language sql
immutable;
Then you can use it like this:
update products
set items = jsonb_set(items, '{information}', remove_element(items -> 'information', '{"name": "second", "value": "medium"}'))
where ...
Online example

Trying to construct PostgreSQL Query to extract from JSON a text value in an object, in an array, in an object, in an array, in an object

I am constructing an interface between a PostgreSQL system and a SQL Server system and am attempting to "flatten" the structure of the JSON data to facilitate this. I'm very experienced in SQL Server but I'm new to both PostgreSQL and JSON.
The JSON contains essentially two types of structure: those of type "text" or "textarea" where the value I want is in an object named value (the first two cases below) and those of type "select" where the value object points to an id object in a lower-level options array (the third case below).
{
"baseGroupId": {
"fields": [
{
"id": "1f53",
"name": "Location",
"type": "text",
"options": [],
"value": "Over the rainbow"
},
{
"id": "b547",
"name": "Description",
"type": "textarea",
"options": [],
"value": "A place of wonderful discovery"
},
{
"id": "c12f",
"name": "Assessment",
"type": "select",
"options": [
{
"id": "e5fd",
"name": "0"
},
{
"id": "e970",
"name": "1"
},
{
"id": "0ff4",
"name": "2"
},
{
"id": "2db3",
"name": "3"
},
{
"id": "241f",
"name": "4"
},
{
"id": "3f52",
"name": "5"
}
],
"value": "241f"
}
]
}
}
Those with a sharp eye will see that the value of the last value object "241f" can also be seen within the options array against one of the id objects. When nested like this I need to extract the value of the corresponding name, in this case "4".
The JSON-formatted information is in table customfield field textvalue. It's datatype is text but I'm coercing it to json. I was originally getting array set errors when trying to apply the criteria in a WHERE clause and then I read about using a LATERAL subquery instead. It now runs but returns all the options, not just the one matching the value.
I'm afraid I couldn't get an SQL Fiddle working to reproduce my results, but I would really appreciate an examination of my query to see if the problem can be spotted.
with cte_custombundledfields as
(
select
textvalue
, cfname
, json_array_elements(textvalue::json -> 'baseGroupId'->'fields') ->> 'name' as name
, json_array_elements(textvalue::json -> 'baseGroupId'->'fields') ->> 'value' as value
, json_array_elements(textvalue::json -> 'baseGroupId'->'fields') ->> 'type' as type
from
customfield
)
, cte_custombundledfieldsoptions as
(
select *
, json_array_elements(json_array_elements(textvalue::json -> 'baseGroupId'->'fields') -> 'options') ->> 'name' as value2
from
cte_custombundledfields x
, LATERAL json_array_elements(x.textvalue::json -> 'baseGroupId'->'fields') y
, LATERAL json_array_elements(y -> 'options') z
where
type = 'select'
and z ->> 'id' = x.value
)
select *
from
cte_custombundledfieldsoptions
I posted a much-simplified rewrite of this question which was answered by Bergi.
How do I query a string from JSON based on another string within the JSON in PostgreSQL?

How to select field values from array of objects?

I have a JSON column with following JSON
{
"metadata": { "value": "JABC" },
"force": false,
"users": [
{ "id": "111", "comment": "abc" },
{ "id": "222", "comment": "abc" },
{ "id": "333" }
]
}
I am expecting list of IDs from the query output ["111","222", "333"]. I tried following query but getting null value.
select colName->'users'->>'id' ids from tableName
How to get this specific field value from the array of object?
You need to extract the array as rows and then get the id:
select json_array_elements(colName->'users')->>'id' ids from tableName;
If you're using jsonb rather than json, the function is jsonb_array_elements.