(a) Your jsonb data sample has some error inside. For this answer, I will use instead :
[{
"blah": "blah",
"owners": [
"admin"
]},
{
"blah": "blah",
"owners": [
"[email protected]"
]},
{
"blah": "blah",
"owners": [
"[email protected]", "[email protected]"
]},
{
"blah": "blah",
"owners": [
"[email protected]", "[email protected]"
]}]
(b) Making multiple updates within a jsonb data is not easy with postgres. The following solution breaks down the jsonb data structure while tracking the path information that is then used when updating the jsonb data structure with the new email values.
(c) The solution is based on the jsonb_set() standard function which can apply only one update to a given jsonb data. So the first step is to create the function jsonb_set_agg() as an aggregate based on jsonb_set and which will be able to perform multiple updates to the same jsonb data while iterating on a set of records :
CREATE OR REPLACE FUNCTION jsonb_set(x jsonb, y jsonb, p text[], z jsonb, c boolean)
RETURNS jsonb LANGUAGE sql AS
$$ SELECT jsonb_set(COALESCE(x,y), p, z, c) ; $$ ;
CREATE OR REPLACE AGGREGATE jsonb_set_agg(x jsonb,p text[], z jsonb, c boolean)
(SFUNC = jsonb_set, STYPE = jsonb) ;
(d) Finally, the following query breaks down the jsonb data (FROM clause), filters the emails which must be updated (WHERE clause), and then updates the jsonb data with the new email values, according to the path based on the ORDINALITY information :
SELECT jsonb_set_agg(j.json_data, array[(b.id - 1) :: text, 'owners', (c.id - 1) :: text], to_jsonb('insert_here_the_new_value' :: text), true)
FROM (VALUES ('[{
"blah": "blah",
"owners": [
"admin"
]},
{
"blah": "blah",
"owners": [
"[email protected]"
]},
{
"blah": "blah",
"owners": [
"[email protected]", "[email protected]"
]},
{
"blah": "blah",
"owners": [
"[email protected]", "[email protected]"
]}]' :: jsonb)) AS j(json_data)
CROSS JOIN LATERAL jsonb_array_elements(j.json_data) WITH ORDINALITY AS b(json_data, id)
CROSS JOIN LATERAL jsonb_array_elements_text(b.json_data->'owners') WITH ORDINALITY AS c(json_data, id)
WHERE c.json_data <> 'admin'
AND NOT c.json_data ~ '@google.com$'
The result is :
[ {"blah": "blah", "owners": ["admin"]}
, {"blah": "blah", "owners": ["[email protected]"]}
, {"blah": "blah", "owners": ["insert_here_the_new_value","insert_here_the_new_value"]}
, {"blah": "blah", "owners": ["insert_here_the_new_value", "[email protected]"]}
]
all the details in dbfiddle.