Update integer json object to become an array with one integer - json

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;

Related

Can't get through mysql JSON_CONTAINS to show the correcct data. Getting null everytime i try

The data in the table events with column attributes is in json and looks like this -
{"event_volunteer_requests":{"1":{"volunteertypeid":250,"volunteer_needed_count":50,"cc_email":""},"2":{"volunteertypeid":249,"volunteer_needed_count":30,"cc_email":""}}}
So the context is that i want to show the events with volunteertypeid present in the attributes. There are many volunteers., this is just sample data.
I've been using the query below
SELECT
*
FROM `events`
WHERE `zoneid` = 27
AND `isactive` = 1
AND JSON_CONTAINS(`attributes` -> '$.event_volunteer_requests[*].volunteertypeid', '249');
I just can't make this work and i've scorched the internet for hours. Any guidance would be appreciated.
Your JSON structure uses object syntax ({"key": "value"}), but you seem to want to use it as a JSON array (["value", "value", ...]).
The [*] syntax in JSON paths works for JSON arrays, not for JSON objects.
If your JSON document must be nested object syntax:
{
"event_volunteer_requests": {
"1": {
"cc_email": "",
"volunteertypeid": 250,
"volunteer_needed_count": 50
},
"2": {
"cc_email": "",
"volunteertypeid": 249,
"volunteer_needed_count": 30
}
}
}
Then you can do what you want this way:
SELECT t.id, t.volunteertypeid
FROM (
SELECT j.id, JSON_EXTRACT(a.attributes, CONCAT('$.event_volunteer_requests."', j.id, '".volunteertypeid')) AS volunteertypeid
FROM (SELECT '{"event_volunteer_requests":{"1":{"volunteertypeid":250,"volunteer_needed_count":50,"cc_email":""},"2":{"volunteertypeid":249,"volunteer_needed_count":30,"cc_email":""}}}' as attributes) AS a
CROSS JOIN JSON_TABLE(JSON_KEYS(a.attributes, '$.event_volunteer_requests'), '$[*]' COLUMNS (id INT PATH '$')) AS j
) AS t
WHERE t.volunteertypeid = 249;
Result:
+------+-----------------+
| id | volunteertypeid |
+------+-----------------+
| 2 | 249 |
+------+-----------------+
The JSON_TABLE() function requires MySQL 8.0. If you use MySQL 5.7, you must upgrade.

Update JSONB with UUID value

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);

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.

Key value showing up as NULL in Postgres jsonb_to_recordset()

I am trying to extract key values using jsonb_to_recordset(). This is how the original jsonb array looks like:
select journal.id as journal_id, journal.data::jsonb#>'{context,data,files}' as filelist from journal where id = 'aqpubr0ivqaaolpr4lp0';
The result is:
[{"id": 0, "name": "MNDA_Template.doc", "extension": ".doc", "transferId": "aqpl61ple38cdebbrmgg"}]
As you can see, the key transferId has a value. But when I extract it using jsonb_to_recordset(), it extracts every other key value except transferId:
select j.id as journal_id,d.name as file_name,d.extension as ext,d.transferId as TxferId from journal j cross join lateral jsonb_to_recordset(j.data#>'{context, data, files}') as d(id int, name text, extension text, transferId text) where j.id = 'aqpubr0ivqaaolpr4lp0';
I get this:
journal_id | file_name | ext | txferId
aqpubr0ivqaaolpr4lp0 | MNDA_Template.doc | .doc | (NULL)
Although I am defining the transferId in the structure, it misses it (only).
What am I missing here?
Because your identifier written in upper-case, you should use double-quote to get values inside them:
select * from jsonb_to_recordset('[{"id": 0, "name": "MNDA_Template.doc", "extension": ".doc", "transferId": "aqpl61ple38cdebbrmgg"}]')
as d(id int, name text, extension text, "transferId" text);

Postgresql merge rows with same key (hstore or json)

I have a table like this:
+--------+--------------------+
| ID | Attribute |
+--------+--------------------+
| 1 |"color" => "red" |
+--------+--------------------+
| 1 |"color" => "green" |
+--------+--------------------+
| 1 |"shape" => "square" |
+--------+--------------------+
| 2 |"color" => "blue" |
+--------+--------------------+
| 2 |"color" => "black" |
+--------+--------------------+
| 2 |"flavor" => "sweat" |
+--------+--------------------+
| 2 |"flavor" => "salty" |
+--------+--------------------+
And I want to run some postgres query that get a result table like this:
+--------+------------------------------------------------------+
| ID | Attribute |
+--------+------------------------------------------------------+
| 1 |"color" => "red, green", "shape" => "square" |
+--------+------------------------------------------------------+
| 2 |"color" => "blue, black", "flavor" => "sweat, salty" |
+--------+------------------------------------------------------+
The attribute column can either be hstore or json format. I wrote it in hstore for an example, but if we cannot achieve this in hstore, but in json, I would change the column to json.
I know that hstore does not support one key to multiple values, when I tried some merge method, it only kept one value for each key. But for json, I didn't find anything that supports multiple value merge like this neither. I think this can be done by function merging values for the same key into a string/text and add it back to the key/value pair. But I'm stuck in implementing it.
Note: if implement this in some function, ideally any key such as color, shape should not appear in the function since keys can be expanded dynamically.
Does anyone have any idea about this? Any advice or brainstorm might help. Thank you!
Just a note before anything else: in your desidered output I would use some proper json and not that kind of lookalike. So a correct output according to me would be:
+--------+----------------------------------------------------------------------+
| ID | Attribute |
+--------+----------------------------------------------------------------------+
| 1 | '{"color":["red","green"], "flavor":[], "shape":["square"]}' |
+--------+----------------------------------------------------------------------+
| 2 | '{"color":["blue","black"], "flavor":["sweat","salty"], "shape":[]}' |
+--------+----------------------------------------------------------------------+
A PL/pgSQL function which parses the json attributes and executes a dynamic query would do the job, something like that:
CREATE OR REPLACE FUNCTION merge_rows(PAR_table regclass) RETURNS TABLE (
id integer,
attributes json
) AS $$
DECLARE
ARR_attributes text[];
VAR_attribute text;
ARR_query_parts text[];
BEGIN
-- Get JSON attributes names
EXECUTE format('SELECT array_agg(name ORDER BY name) AS name FROM (SELECT DISTINCT json_object_keys(attribute) AS name FROM %s) AS s', PAR_table) INTO ARR_attributes;
-- Write json_build_object() query part
FOREACH VAR_attribute IN ARRAY ARR_attributes LOOP
ARR_query_parts := array_append(ARR_query_parts, format('%L, array_remove(array_agg(l.%s), null)', VAR_attribute, VAR_attribute));
END LOOP;
-- Return dynamic query
RETURN QUERY EXECUTE format('
SELECT t.id, json_build_object(%s) AS attributes
FROM %s AS t,
LATERAL json_to_record(t.attribute) AS l(%s)
GROUP BY t.id;',
array_to_string(ARR_query_parts, ', '), PAR_table, array_to_string(ARR_attributes, ' text, ') || ' text');
END;
$$ LANGUAGE plpgsql;
I've tested it and it seems to work, it returns a json with. Here is my test code:
CREATE TABLE mytable (
id integer NOT NULL,
attribute json NOT NULL
);
INSERT INTO mytable (id, attribute) VALUES
(1, '{"color":"red"}'),
(1, '{"color":"green"}'),
(1, '{"shape":"square"}'),
(2, '{"color":"blue"}'),
(2, '{"color" :"black"}'),
(2, '{"flavor":"sweat"}'),
(2, '{"flavor":"salty"}');
SELECT * FROM merge_rows('mytable');
Of course you can pass the id and attribute column names as parameters as well and maybe refine the function a bit, this is just to give you an idea.
EDIT : If you're on 9.4 please consider using jsonb datatype, it's much better and gives you room for improvements. You would just need to change the json_* functions to their jsonb_* equivalents.
If you just want this for display purposes, this might be enough:
select id, string_agg(key||' => '||vals, ', ')
from (
select t.id, x.key, string_agg(value, ',') vals
from t
join lateral each(t.attributes) x on true
group by id, key
) t
group by id;
If you are not on 9.4, you can't use the lateral join:
select id, string_agg(key||' => '||vals, ', ')
from (
select id, key, string_agg(val, ',') as vals
from (
select t.id, skeys(t.attributes) as key, svals(t.attributes) as val
from t
) t1
group by id, key
) t2
group by id;
This will return:
id | string_agg
---+-------------------------------------------
1 | color => red,green, shape => square
2 | color => blue,black, flavor => sweat,salty
SQLFiddle: http://sqlfiddle.com/#!15/98caa/2