Removing field from json and casting it to jsonb - postgresql

I want to remove field from array of jsons and then retreive that array as jsonb object. I am able to remove field. Now I want to have single object to return from function.
The way it is now I get
ERROR: more than one row returned by a subquery used as an expression
BEGIN
RETURN (
WITH records (game_numbers) AS (
SELECT *
FROM jsonb_array_elements($1))
SELECT (game_numbers - 'code')::json
FROM records);
END;
$$;
I have data like:
"game_numbers":[
{
"id":1,
"code":"code1"
},
{
"id":2,
"code": "code2"
}
]
I want to receive:
"game_numbers":[
{
"id":1,
},
{
"id":2,
}
]

If you want the function to return a jsonb, then you need to aggregate the elements back to a jsonb.
CREATE FUNCTION fnRemoveCodeElement(jsonb)
RETURNS jsonb AS $$
SELECT json_agg(value - 'code')::jsonb
FROM jsonb_array_elements($1)
$$ LANGUAGE SQL;
SELECT fnRemoveCodeElement('[
{
"id":1,
"code":"code1"
},
{
"id":2,
"code": "code2"
}
]') js
js
[{"id": 1}, {"id": 2}]
db<>fiddle here

Related

Update property of object in jsonb array and keep other properties

I have a postgres table with a jsonb colum like this:
create table if not exists doc
(
id uuid not null
constraint pkey_doc_id
primary key,
data jsonb not null
);
INSERT INTO doc (id, data) VALUES ('3cf40366-ea58-402d-b63b-c9d6fdf99ec8', '{"Id": "3cf40366-ea58-402d-b63b-c9d6fdf99ec8", "Tags": [{"Key": "inoivce", "Value": "70086"},{"Key": "customer", "Value": "100233"}] }' );
INSERT INTO doc (id, data) VALUES ('ae2d1119-adb9-41d2-96e9-53445eaf97ab', '{"Id": "ae2d1119-adb9-41d2-96e9-53445eaf97ab", "Tags": [{"Key": "project", "Value": "12345"},{"Key": "customer", "Value": "100233"}]}' );b9-41d2-96e9-53445eaf97ab", "Tags": [{"Key": "customer", "Value": "100233"}]}' )
Tags.Key in the first row contains a typo inoivce which I want to fix to invoice:
{
"Id": "3cf40366-ea58-402d-b63b-c9d6fdf99ec8",
"Tags": [{
"Key": "inoivce",
"Value": "70086"
},{
"Key": "customer",
"Value": "100233"
}]
}
I tried this:
update doc set data = jsonb_set(
data,
'{"Tags"}',
$${"Key":"invoice"}$$
) where data #> '{"Tags": [{ "Key":"inoivce"}]}';
The typo gets fixed but I'm loosing the other Tags elements in the array:
{
"Id": "3cf40366-ea58-402d-b63b-c9d6fdf99ec8",
"Tags": [{"Key": "invoice"}]
}
How can I fix the typo without removing the other elements of the Tags array?
Dbfiddle for repro.
One possible solution, not so obvious : we need a CTE because the idea here is to loop on the 'Tags' jsonb array elements using the jsonb_agg aggregate function to rebuild the array, but the SET clause of an UPDATE doesn't accept aggregate functions ...
WITH list AS
( SELECT d.id, (d.data - 'Tags') || jsonb_build_object('Tags', jsonb_agg(jsonb_set(e.content, '{Key}' :: text[], to_jsonb(replace(e.content->>'Key', 'inoivce', 'invoice'))) ORDER BY e.id)) AS data
FROM doc AS d
CROSS JOIN LATERAL jsonb_array_elements(d.data->'Tags') WITH ORDINALITY AS e(content, id)
WHERE d.data #? '$.Tags[*] ? (exists(# ? (#.Key == "inoivce")))'
GROUP BY d.id, d.data
)
UPDATE doc AS d
SET data = l.data
FROM list AS l
WHERE d.id = l.id
see the result in dbfiddle

malformed array literal when convetring jsonb array of jsonb items to postgres array of jsonb by jsonb_array_elements

I have jsonb-array:
element_values := '[
{
"element_id": "a7993f3d-9256-4354-a147-5b9d18d7812b",
"value": true
},
{
"element_id": "ceeb364e-bb88-4f41-9c56-9e5f4d0bc1fb",
"value": None
},
...
]'::JSONB
And I want to convert it into array of jsonb objects: JSONB[]
I tried this method:
<<elements_n_values_relationship_create>>
DECLARE
elements_n_values_relationship JSONB[];
BEGIN
SELECT * FROM jsonb_array_elements(element_values) INTO elements_n_values_relationship;
...
END;
But I got the following error:
ERROR: malformed array literal: "{"value": true, "element_id": "a7993f3d-9256-4354-a147-5b9d18d7812b"}"
DETAIL: Unexpected array element.
Why it does not work?
You have to use null in place of None to make your statement work
EDIT:
Try this in pgadmin or any SQL client, is is working as expected
select jsonb_array_elements('[{
"element_id": "a7993f3d-9256-4354-a147-5b9d18d7812b",
"value": true
},
{
"element_id": "ceeb364e-bb88-4f41-9c56-9e5f4d0bc1fb",
"value": null
}]'::JSONB);
jsonb_array_elements
"{""value"":"{""value"": true, ""element_id"": ""a7993f3d-9256-4354-a147-5b9d18d7812b""}"
{ "value": null, "element_id": "ceeb364e-bb88-4f41-9c56-9e5f4d0bc1fb" }

Postgres JSONB function to key an array of objects, by a field

Is there a pure Postgres approach to turning a JSONB array like:
[{"id": 1, "name": "Dom" }, { "id": 2, "name": "Bass" }]
into
{ 1: {"id": 1, "name": "Dom" }, 2: { "id": 2, "name": "Bass" } }
I.e. same as Lodash doing:
_.keyBy(arr, 'id)
Use the function:
create or replace function jsonb_key_by(jsonb, text)
returns jsonb language sql immutable as $$
select jsonb_object_agg(elem->>$2, elem)
from jsonb_array_elements($1) as arr(elem)
$$;
Test it in db<>fiddle.

How to add new property in an object nested in 2 arrays (JSONB postgresql)

I am looking to you for help in adding a property to a json object nested in 2 arrays.
Table Example :
CREATE TABLE events (
seq_id BIGINT PRIMARY KEY,
data JSONB NOT NULL,
evt_type TEXT NOT NULL
);
example of my JSONB data column in my table:
{
"Id":"1",
"Calendar":{
"Entries":[
{
"Id": 1,
"SubEntries":[
{
"ExtId":{
"Id":"10",
"System": "SampleSys"
},
"Country":"FR",
"Details":[
{
"ItemId":"1",
"Quantity":10,
},
{
"ItemId":"2",
"Quantity":3,
}
],
"RequestId":"222",
"TypeId":1,
}
],
"OrderResult":null
}
],
"OtherThingsArray":[
]
}
}
So I need to add new properties into a SubEntries object based on the Id value of the ExtId object (The where clause)
How can I do that please ?
Thanks a lot
You can use jsonb_set() for this, which takes jsonpath assignments as a text[] (array of text values) as
SELECT jsonb_set(
input_jsonb,
the starting jsonb document
path_array '{i,j,k[, ...]}'::text[],
the path array, where the series {i, j, k} progresses at each level with either the (string) key or (integer) index (starting at zero)denoting the new key (or index) to populate
new_jsonb_value,
if adding a key-value pair, you can use something like to_jsonb('new_value_string'::text) here to force things to format correctly
create_if_not_exists_boolean
if adding new keys/indexes, give this as true so they'll be appended; otherwise you'll be limited to overwriting existing keys
)
Example
json
{
"array1": [
{
"id": 1,
"detail": "test"
}
]
}
SQL
SELECT
jsonb_set('{"array1": [{"id": 1, "detail": "test"}]}'::jsonb,
'{array1,0,update}'::TEXT[],
to_jsonb('new'::text),
true
)
Output
{
"array1": [
{
"id": 1,
"upd": "new",
"detail": "test"
}
]
}
Note that you can only add 1 nominal level of depth at a time (i.e. either a new key or a new index entry), but you can circumvent this by providing the full depth in the assignment value, or by using jsonb_set() iteratively:
select
jsonb_set(
jsonb_set('{"array1": [{"id": 1, "detail": "test"}]}'::jsonb, '{array1,0,upd}'::TEXT[], '[{"new": "val"}]'::jsonb, true),
'{array1,0,upd,0,check}'::TEXT[],
'"test_val"',
true)
would be required to produce
{
"array1": [
{
"id": 1,
"upd": [
{
"new": "val",
"check": "test_val"
}
],
"detail": "test"
}
]
}
If you need other, more complex logic to evaluate which values need to be added to which objects, you can try:
dynamically creating a set of jsonb_set() statements for execution
using the outputs from queries of jsonb_each() and jsonb_array_elements() to evaluate the row logic down at the SubEntities level, and then using jsonb_object_agg() and jsonb_agg() as appropriate to build the document back up to the root level from the resultant object-arrays and key-value collections

Postgres: Delete object from anonymous jsonb array element

I have a table with 2 fields:
table documents
docu_id uuid
attachments jsonb
A sample data for the attachments jsonb column would be:
[
{
"size": 10,
"attach_id": "d3a21f904068"
},{
"Size": 0.143,
"attach_id": "5ba4b285565b"
}
]
I have seen many examples of how to update/delete a jsonb based on field name, but is it possible to delete an anonymous object from an anonymous array where "attach_id" = "X" and "docu_id"="Y":
delete from documents
where docu_id = "Y"
and
where attachments #> '[{"attach_id": "X"}]'
Ok found the solution so I'm sharing it here, (rextester link http://rextester.com/YICZ86369):
Inserting the data
create table documents(docu_id text, attachments jsonb);
insert into documents values
('001',
'[
{
"name": "uno",
"id":"1"
},
{
"name": "dos",
"id":"2"
},
{
"name": "tres",
"id":"3"
}
]'
),
('002',
'[
{
"name": "eins",
"id":"1"
},
{
"name": "zwei",
"id":"2"
}
]'
);
select * from documents;
The solution
UPDATE documents
SET attachments = attachments #-
array(
SELECT i
FROM generate_series(0, jsonb_array_length(attachments) - 1) AS i
WHERE (attachments->i->'id' = '"2"')
)::text[] /* cast as text */
where docu_id = '002';
select * from documents;