1

Is there a way to update a JSONB value in an array of array without having to specify the index (which is not always reliable) but rather by property inside one object?

Lets say I have this JSONB value:

select '[{"foo": [{"id": "baz"}, {"id": "bar"}]}, {"foo": [{"id": "bor"}]}]'::jsonb;

I can find the element that has the id bar with this query:

select * from x where f @> '[{"foo": [{"id": "bar"}]}]'::jsonb:

But I can't just say: Update the object where ID is bar and change it without specifying its index:

UPDATE x SET f = jsonb_set(f, '{0,foo,1}', '{"id": "new-bar", "something": "hello"}'::jsonb);

Is there a way to update a portion of JSONB by using a property of this nested object without having to specify an index (i.e position)?

Side note: the problem with the index position is that it can vary between queries and it's not readable when reading logs.

Not sure it's clear enough, please let me know if I can improve my question!

1

1 Answer 1

1

One option would be applying JSONB_ARRAY_ELEMENTS() function along with CROSS JOINs recursively upto the point where the deepest array element reached.

And then matching '{"id": "bar"}::JSONB' as search identifier :

WITH x0 AS
(
 SELECT j
   FROM x
  CROSS JOIN JSONB_ARRAY_ELEMENTS(f) AS j 
), x1 AS
(
 SELECT ('{foo,'||index-1||'}')::text[] AS path, x0.*
   FROM x0
  CROSS JOIN JSONB_ARRAY_ELEMENTS((j->>'foo')::JSONB) 
   WITH ORDINALITY arr(j1,index)
  WHERE j1 = '{"id": "bar"}'::JSONB   
), x_upd AS
(
SELECT JSONB_AGG(
                 JSONB_SET(x1.j,x1.path,'{"id": "new-bar", "something": "hello"}',false)
       ) AS js_agg
  FROM x1
)
UPDATE x
   SET f = js_agg
  FROM x_upd

Demo

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.