Postgres - jsonb create and update new attribute in column - postgresql

I have such a column attributes:
{
"a:value": "123",
"a:origin": "abc"
}
I want to create a new attribute which should look like this:
"abcKey:value": {
"value": "123ABC",
"version": "1"
}
So, in the end attributes should look like this:
{
"a:value": "123",
"a:origin": "abc",
"abcKey:value": {
"value": "123ABC",
"version": "1"
}
}
How can I do this?
I tried this
update my_table
set attributes = jsonb_set(attributes, '{abcKey:value,value}', '"123ABC"')
attributes = jsonb_set(attributes, '{abcKey:value,version}', '"1"')
where ...;
But this does not work because I think, I have to create the new attribute at first. How can I create and update this new attribute (maybe in one step)?
Thank you very much!

I wrote two samples for you:
-- if you have same objects are json
with tb as (
select
'{
"a:value": "123",
"a:origin": "abc"
}'::jsonb a1,
'{"abcKey:value": {
"value": "123ABC",
"version": "1"
}}'::jsonb a2
)
select a1, a2, a1||a2 from tb; -- you can concate json objects
-- if you can create json objects via using key value
with tb as (
select
'{
"a:value": "123",
"a:origin": "abc"
}'::jsonb a1
)
select a1 || jsonb_build_object('abcKey:value', jsonb_build_object('value', '123ABC', 'version', 1)) from tb;

Related

Insert new item in JSONB column based on value of other field - postgres

I have the following jsonb structure with many entries in it
[
{
"name":"test",
"features":[
{
"name":"feature1",
"granted":false
},
{
"name":"feature2",
"granted":true
}
]
}...
]
I'd like to add a new entry in the features array when the parent name element has value "test" and feature1 granted is "false".
The idea is to write a flyway script to migrate my data.
I've been battling with jsonb_insert but I can't figure out the path portion of it since I can have potentially many elements in there and I can't just add a given subscript.
End result should be:
[
{
"name":"test",
"features":[
{
"name":"feature1",
"granted":false
},
{
"name":"feature2",
"granted":true
},
{
"name":"newFeature",
"granted":false
}
]
}
]
EDIT1
So far I've attempted:
UPDATE my_table SET modules =
jsonb_insert(my_column, '{features, [0]}', '{"name": "newFeature", "granted": false}')
WHERE my_column ->> 'name' = 'test' AND my_column #> '{"features": [{"name":"feature1", "granted": false}]}';
The statement executes but no updates are actually done.
EDIT2
I modified the query just to test the PATH out to
UPDATE my_table SET modules =
jsonb_insert(my_column, '{0, features, 0}', '{"name": "newFeature", "granted": false}')
WHERE my_column ->> 'name' = 'test' AND my_column #> '{"features": [{"name":"feature1", "granted": false}]}';
However this only always updates the first entry in the array, and the object I need to update is not guaranteed to always be in this position
This should be enough information to complete the query:
Let's create the mock data
create table a (id serial primary key , b jsonb);
insert into a (b)
values ('[
{
"name": "test",
"features": [
{
"name": "feature1",
"granted": false
},
{
"name": "feature2",
"granted": true
}
]
},
{
"name": "another-name",
"features": [
{
"name": "feature1",
"granted": false
},
{
"name": "feature2",
"granted": true
}
]
}
]');
Now explode the array using jsonb_array_elements with ordinality to get the index and the property
select first_level.id, position, feature_position, feature
from (select a.id, arr.*
from a,
jsonb_array_elements(a.b) with ordinality arr (elem, position)
where elem ->> 'name' = 'test') first_level,
jsonb_array_elements(first_level.elem -> 'features') with ordinality features (feature, feature_position);
The result of this query is:
1,1,1,"{""name"": ""feature1"", ""granted"": false}"
1,1,2,"{""name"": ""feature2"", ""granted"": true}"
There you have the necessary info that you need to fetch the sub elements that you need, as well as all the indexes that you needed for your query.
Now, to the final edit, you already had the query that you wanted:
UPDATE my_table SET modules =
jsonb_insert(my_column, '{0, features, 0}', '{"name": "newFeature", "granted": false}')
WHERE my_column ->> 'name' = 'test' AND my_column #> '{"features": [{"name":"feature1", "granted": false}]}';
In the where you'll use the id, because those are the rows that you are interested in, and in the indexes you got them from the query. So:
UPDATE my_table SET modules =
jsonb_insert(my_column, '{' || exploded_info.position::string || ', features, ' || exploded_info.feature_position || '}', '{"name": "newFeature", "granted": false}') from (/* previous query */) as exploded_info
WHERE exploded_info.id = my_table.id and exploded_info.feature -> 'granted' = false;
As you can see this easily get's very nasty.
I'd recommend either using a more sql approach, that is, having features in a table instead of inside a json, a fk linking that to your table...
If you really need to use the json, for example, because the domain is really complex and defined at the application level and very flexible. Then I would recommend doing the updates inside app code

Couchbase N1QL update query for Array of objects

I am new to n1ql. I want to search all records in a bucket with "ABC" and replace it with "DEF". Can you please help me in creating this query and index?
Sample records
{
"userTypeNm": "pro",
"userStateArray": [
{
"bindCd": "1591779772457",
"name": "########",
"state": "**ABC**",
"ts": "1591779772457"
}
],
"vts": "1591779772457",
"ets": "1591779772457",
"daoObj": {
"authDaObj": {
"data": "eyJ0cmFuc2FjdGlvbklkIjoiVVNMT0dPTi0xN2U3YWQ5ZC0wN",
"id": "829892839892"
}
}
}
CREATE INDEX ix1 ON default
(DISTINCT ARRAY v.state FOR v IN userStateArray END) WHERE userTypeNm = "pro";
UPDATE default AS d SET usa.state = "DEF" FOR usa IN d.userStateArray WHEN usa.state = "ABC" END
WHERE ANY v IN d.userStateArray SATISFIES v.state = "ABC" END;
https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/update.html

How can I access a key value in jsonb postgresql?

{
"data": {
"val": "{\"cell_number\": \"123\"}"
}
}
I want to get the value in data -> val -> cell_number i.e '123'. Is there a way to do it in postgresql?
If that is not a typo and you put a stringified json object under the val key, then this will untangle it for you:
with invar as (
select '{
"data": {
"val": "{\"cell_number\": \"123\"}"
}
}'::jsonb as jsonb_col
)
select ((jsonb_col->'data'->>'val')::jsonb)->>'cell_number' from invar;
?column?
----------
123
(1 row)
The first step gets you down to the val key. That result has to be returned as text (hence the ->>) and then cast to jsonb so that cell_number can be dereferenced.

Postgres query array elements inside JSON?

I have Postgres 11 table called fb_designs that has a json column with data structured like so:
{
"listings": [
{
"id": "KTyneMdrAhAEKyC9Aylf",
"active": true
},
{
"id": "ZcjK9M4tuwhWWdK8WcfX",
"active": false
}
]
}
and a tags column in a character varying[] format as so {dWLaRWChaThFPH6b3BpA,BrYiPaUiou020hsmRugR}. Both lengths are undefined.
What I am trying to do is produce a some queries that will let me say in laymans terms,
show me all results where at all items.listings has an
active status and tags contains BrYiPaUiou020hsmRugR
I got this far, however I'm not sure how to add in WHERE uid = "foo", WHERE tags contains "foo", "bar and WHERE title is like %hoot%
SELECT id, title, tags, selected_preview_image, items
FROM fb_designs r, json_array_elements(r.items#>'{listings}') obj
WHERE obj->>'active' = 'true'
GROUP BY id
If they are all true, then none of them are false. Sounds like you want to negate the containment operation over false.
select * from fb_designs where
not items::jsonb #> '{"listings":[{"active": false}]}'
and tags && ARRAY['BrYiPaUiou020hsmRugR']::varchar[]

Creating an AND query on a list of items in Azure Cosmos

I'm building an application in Azure Cosmos and I'm having trouble creating a query. Using the dataset below, I want to create a query that only finds CharacterId "Susan" by searching for all characters that have the TraitId of "Athletic" and "Slim".
Here is my JSON data set
{
"characterId": "Bob",
"traits": [
{
"traitId": "Athletic",
"traitId": "Overweight"
}
],
},
{
"characterId": "Susan",
"traits": [
{
"traitId": "Athletic",
"traitId": "Slim"
}
],
},
{
"characterId": "Jerry",
"traits": [
{
"traitId": "Slim",
"traitId": "Strong"
}
],
}
]
The closest I've come is this query but it acts as an OR statement and what I want is an AND statement.
SELECT * FROM Characters f WHERE f.traits IN ("Athletic", "Slim")
Any help is greatly appreciated.
EDITED: I figured out the answer to this question. If anyone is interested this query gives the results I was looking for:
SELECT * FROM Characters f
WHERE EXISTS (SELECT VALUE t FROM t IN f.traits WHERE t.traitId = 'Athletic')
AND EXISTS (SELECT VALUE t FROM t IN f.traits WHERE t.traitId = 'Slim')
The answer that worked for me is to use EXISTS statements with SELECT statements that searched the traits list. In my program I can use StringBuilder to create a SQL statement that concatenates an AND EXISTS statement for each of the traits I want to find:
SELECT * FROM Characters f
WHERE EXISTS (SELECT VALUE t FROM t IN f.traits WHERE t.traitId = 'Athletic')
AND EXISTS (SELECT VALUE t FROM t IN f.traits WHERE t.traitId = 'Slim')