Update JSONB with UUID value - json

I have a table with a column of jsonb type, the JSON object is of key:value pairs, the problem is, I need to update one of the keys to contain a UUID instead of the original value.
The update statement I'm using:
UPDATE
public.applications a
SET
data = jsonb_set(data, '{id}', (SELECT b.uuid FROM public.devices b WHERE b.id = (a.data ->> 'id')::integer)::text)
Postgres returns the following error:
ERROR: function jsonb_set(jsonb, unknown, text) does not exist
I've tried to cast the value to different data types, but with same result.
Sample data
id | data
---------
1 | {"id": 1}
2 | {"id": 2}
Expected output
id | data
---------
1 | {"id": device_uuid_here}
2 | {"id": device_uuid_here}
DB_VERSION: PostgreSQL12.12

UPDATE public.applications a
SET data = jsonb_set(data, '{id}',
(SELECT '"'||b.uuid::text||'"' FROM public.devices b
WHERE b.id = (a.data ->> 'id')::integer)::jsonb);

Related

How to update postgresql json date field

I am trying to update json date field with value from another column.
I am able to update with the following statement:
UPDATE table
SET column = column || '{"date_field":"2022-08-25"}'
where id = 123;
When I try to update with value from another column ie:
UPDATE table
SET json_column = json_column || '{"date_field":column}'
where id = 123;
I would get the following Error.
ERROR: invalid input syntax for type json
LINE 2: ...DATE table SET json_column = json_column || '{"date_field...
DETAIL: Token "column" is invalid.
CONTEXT: JSON data, line 1: {"date_field":column...
SQL state: 22P02
Character: 54
The database is PostgreSQL version 10.9.
Could someone point me to where I can find the right syntax?
Note || only works with jsonb.
\d json_test
Table "public.json_test"
Column | Type | Collation | Nullable | Default
-----------+---------+-----------+----------+---------
id | integer | | |
fld_json | json | | |
fld_jsonb | jsonb | | |
select fld_jsonb from json_test where id = 2;
fld_jsonb
----------------------
{"one": 1, "two": 2}
update json_test set fld_jsonb = fld_jsonb || jsonb_build_object('id', id) where id = 2 ;
UPDATE 1
select fld_jsonb from json_test where id = 2;
fld_jsonb
-------------------------------
{"id": 2, "one": 1, "two": 2}

Update integer json object to become an array with one integer

I'm currently working with psql and trying to update an embedded json object to change a value from an integer to an array with one value (this is due to a function changing elsewhere).
My table looks like this:
id | dashboard_settings
-----------------------
1 | {"query": {"year_end": 2018, "year_start": 2015, "category": 4}}
However I want the category bit to change to [4], like below:
id | dashboard_settings
-----------------------
1 | {"query": {"year_end": 2018, "year_start": 2015, "category": [4]}}
So far, I have this which allows me to access the category section but I can't figure out how to turn it to an array:
UPDATE table
SET dashboard_settings = jsonb_set(
dashboard_settings::jsonb,
array['query, category'],
to_jsonb(dashboard_settings->'query' ->'category'));
I've tried to concatenate and cast as an array but had no success
Click: demo:db<>fiddle
Instead of to_jsonb() you could use jsonb_build_array():
UPDATE mytable
SET dashboard = s.out
FROM (
SELECT
jsonb_set(
dashboard,
'{query, category}',
jsonb_build_array(dashboard -> 'query' -> 'category')
) AS out
FROM mytable
WHERE id = 1;
) s
WHERE id = 1;

query to Extract from Json in Postgres

I've a json object in my postgres db, which looks like as given below
{"Actor":[{"personName":"Shashi Kapoor","characterName":"Prem"},{"personName":"Sharmila Tagore","characterName":"Preeti"},{"personName":"Shatrughan Sinha","characterName":"Dr. Amar"]}
Edited (from editor: left the original because it is an invalid json, in my edit I fixed it)
{
"Actor":[
{
"personName":"Shashi Kapoor",
"characterName":"Prem"
},
{
"personName":"Sharmila Tagore",
"characterName":"Preeti"
},
{
"personName":"Shatrughan Sinha",
"characterName":"Dr. Amar"
}
]
}
the name of the column be xyz and I've a corresponding content_id.
I need to retrieve content_ids that have Actor & personName = Sharmila Tagore.
I tried many queries, among those these two where very possible query to get but still i didn't get.
SELECT content_id
FROM content_table
WHERE cast_and_crew #>> '{Actor,personName}' = '"C. R. Simha"'
.
SELECT cast_and_crew ->> 'content_id' AS content_id
FROM content_table
WHERE cast_and_crew ->> 'Actor' -> 'personName' = 'C. R. Simha'
You should use jsonb_array_elements() to search in a nested jsonb array:
select content_id, value
from content_table,
lateral jsonb_array_elements(cast_and_crew->'Actor');
content_id | value
------------+-----------------------------------------------------------------
1 | {"personName": "Shashi Kapoor", "characterName": "Prem"}
1 | {"personName": "Sharmila Tagore", "characterName": "Preeti"}
1 | {"personName": "Shatrughan Sinha", "characterName": "Dr. Amar"}
(3 rows)
Column value is of the type jsonb so you can use ->> operator for it:
select content_id, value
from content_table,
lateral jsonb_array_elements(cast_and_crew->'Actor')
where value->>'personName' = 'Sharmila Tagore';
content_id | value
------------+--------------------------------------------------------------
1 | {"personName": "Sharmila Tagore", "characterName": "Preeti"}
(1 row)
Note, if you are using json (not jsonb) use json_array_elements() of course.

How to search JSON array in MySQL?

Let's say I have a JSON column named data in some MySQL table, and this column is a single array. So, for example, data may contain:
[1,2,3,4,5]
Now I want to select all rows which have a data column where one of its array elements is greater than 2. Is this possible?
I tried the following, but seems it is always true regardless of the values in the array:
SELECT * from my_table
WHERE JSON_EXTRACT(data, '$[*]') > 2;
You may search an array of integers as follows:
JSON_CONTAINS('[1,2,3,4,5]','7','$') Returns: 0
JSON_CONTAINS('[1,2,3,4,5]','1','$') Returns: 1
You may search an array of strings as follows:
JSON_CONTAINS('["a","2","c","4","x"]','"x"','$') Returns: 1
JSON_CONTAINS('["1","2","3","4","5"]','"7"','$') Returns: 0
Note: JSON_CONTAINS returns either 1 or 0
In your case you may search using a query like so:
SELECT * from my_table
WHERE JSON_CONTAINS(data, '2', '$');
SELECT JSON_SEARCH('["1","2","3","4","5"]', 'one', "2") is not null
is true
SELECT JSON_SEARCH('["1","2","3","4","5"]', 'one', "6") is not null
is false
Since MySQL 8 there is a new function called JSON_TABLE.
CREATE TABLE my_table (id INT, data JSON);
INSERT INTO my_table VALUES
(1, "[1,2,3,4,5]"),
(2, "[0,1,2]"),
(3, "[3,4,-10]"),
(4, "[-1,-2,0]");
SELECT DISTINCT my_table.*
FROM my_table, JSON_TABLE(data, "$[*]" COLUMNS(nr INT PATH '$')) as ids
WHERE ids.nr > 2;
+------+-----------------+
| id | data |
+------+-----------------+
| 1 | [1, 2, 3, 4, 5] |
| 3 | [3, 4, -10] |
+------+-----------------+
2 rows in set (0.00 sec)
I use a combination of JSON_EXTRACT and JSON_CONTAINS (MariaDB):
SELECT * FROM table WHERE JSON_CONTAINS(JSON_EXTRACT(json_field, '$[*].id'), 11, '$');
I don't know if we found the solution.
I found with MariaDB a way, to search path in a array. For example, in array [{"id":1}, {"id":2}], I want find path with id equal to 2.
SELECT JSON_SEARCH('name_field', 'one', 2, null, '$[*].id')
FROM name_table
The result is:
"$[1].id"
The asterisk indicate searching the entire array
This example works for me with mysql 5.7 above
SET #j = '{"a": [ "8428341ffffffff", "8428343ffffffff", "8428345ffffffff", "8428347ffffffff","8428349ffffffff", "842834bffffffff", "842834dffffffff"], "b": 2, "c": {"d": 4}}';
select JSON_CONTAINS(JSON_EXTRACT(#j , '$.a'),'"8428341ffffffff"','$') => returns 1
notice about " around search keyword, '"8428341ffffffff"'
A possible way is to deal with the problem as string matching. Convert the JSON to string and match.
Or you can use JSON_CONTAINS.
You can use JSON extract to search and select data
SELECT data, data->"$.id" as selectdata
FROM table
WHERE JSON_EXTRACT(data, "$.id") = '123'
#ORDER BY c->"$.name";
limit 10 ;
SET #doc = '[{"SongLabels": [{"SongLabelId": "111", "SongLabelName": "Funk"}, {"SongLabelId": "222", "SongLabelName": "RnB"}], "SongLabelCategoryId": "test11", "SongLabelCategoryName": "曲风"}]';
SELECT *, JSON_SEARCH(#doc, 'one', '%un%', null, '$[*].SongLabels[*].SongLabelName')FROM t_music_song_label_relation;
result: "$[0].SongLabels[0].SongLabelName"
SELECT song_label_content->'$[*].SongLabels[*].SongLabelName' FROM t_music_song_label_relation;
result: ["Funk", "RnB"]
I have similar problem, search via function
create function searchGT(threshold int, d JSON)
returns int
begin
set #i = 0;
while #i < json_length(d) do
if json_extract(d, CONCAT('$[', #i, ']')) > threshold then
return json_extract(d, CONCAT('$[', #i, ']'));
end if;
set #i = #i + 1;
end while;
return null;
end;
select searchGT(3, CAST('[1,10,20]' AS JSON));
This seems to be possible with to JSON_TABLE function. It's available in mysql version 8.0 or mariadb version 10.6.
With this test setup
CREATE TEMPORARY TABLE mytable
WITH data(a,json) AS (VALUES ('a','[1]'),
('b','[1,2]'),
('c','[1,2,3]'),
('d','[1,2,3,4]'))
SELECT * from data;
we get the following table
+---+-----------+
| a | json |
+---+-----------+
| a | [1] |
| b | [1,2] |
| c | [1,2,3] |
| d | [1,2,3,4] |
+---+-----------+
It's possible to select every row from mytable wich has a value greater than 2 in the json array with this query.
SELECT * FROM mytable
WHERE TRUE IN (SELECT val > 2
FROM JSON_TABLE(json,'$[*]'
columns (val INT(1) path '$')
) as json
)
Returns:
+---+-----------+
| a | json |
+---+-----------+
| c | [1,2,3] |
| d | [1,2,3,4] |
+---+-----------+

Extract values from Postgres JSONB column

I have a JSONB column called metrics in a table events. It stores various metrics as a flat hash, e.g.
{"m1": 123, "m2": 122.3, "m3": 32}
I would like to extract all the values stored in that column. Is it possible? I have found a function jsonb_object_keys(jsonb), but I failed to find anything similar for values.
Use jsonb_each() for this purpose:
WITH json_test(data) AS ( VALUES
('{"m1": 123, "m2": 122.3, "m3": 32}'::JSONB)
)
SELECT element.value
FROM json_test jt, jsonb_each(jt.data) as element;
Output:
value
-------
123
122.3
32
(3 rows)
Use jsonb_each() in a lateral join:
with val as (
select '{"m1": 123, "m2": 122.3, "m3": 32}'::jsonb js
)
select key, value
from val,
lateral jsonb_each(js);
key | value
-----+-------
m1 | 123
m2 | 122.3
m3 | 32
(3 rows)
Using json_each you can extract the values with:
SELECT value FROM json_each('{"m1": 123, "m2": 122.3, "m3": 32}')
Output
value
-----
123
122.3
32