Question: How to delete array element in JSONB column based on nested key value?

Question

How to delete array element in JSONB column based on nested key value?

Answers 1
Added at 2016-11-15 04:11
Tags
Question

How can I remove an object from an array, based on the value of one of the object's keys?

The array is nested within a parent object.

Here's a sample structure:

{
  "foo1": [ { "bar1": 123, "bar2": 456 }, { "bar1": 789, "bar2": 42 } ],
  "foo2": [ "some other stuff" ]
}

Can I remove an array element based on the value of bar1?

I can query based on the bar1 value using: columnname @> '{ "foo1": [ { "bar1": 123 } ]}', but I've had no luck finding a way to remove { "bar1": 123, "bar2": 456 } from foo1 while keeping everything else intact.

Thanks


Running PostgreSQL 9.6

Answers to

How to delete array element in JSONB column based on nested key value?

nr: #1 dodano: 2016-11-15 06:11

Assuming that you want to search for a specific object with an inner object of a certain value, and that this specific object can appear anywhere in the array, you need to unpack the document and each of the arrays, test the inner sub-documents for containment and delete as appropriate, then re-assemble the array and the JSON document (untested):

SELECT id, jsonb_build_object(key, jarray)
FROM (
    SELECT foo.id, foo.key, jsonb_build_array(bar.value) AS jarray
    FROM (  SELECT id, key, value
            FROM my_table, jsonb_each(jdoc) ) foo,
        jsonb_array_elements(foo.value) AS bar (value)
    WHERE NOT bar.value @> '{"bar1": 123}'::jsonb
    GROUP BY 1, 2 ) x
GROUP BY 1;

Now, this may seem a little dense, so picked apart you get:

SELECT id, key, value
FROM my_table, jsonb_each(jdoc)

This uses a lateral join on your table to take the JSON document jdoc and turn it into a set of rows foo(id, key, value) where the value contains the array. The id is the primary key of your table.

Then we get:

SELECT foo.id, foo.key, jsonb_build_array(bar.value) AS jarray
FROM foo,  -- abbreviated from above
     jsonb_array_elements(foo.value) AS bar (value)
WHERE NOT bar.value @> '{"bar1": 123}'::jsonb
GROUP BY 1, 2

This uses another lateral join to unpack the arrays into bar(value) rows. These objects can now be searched with the containment operator to remove the objects from the result set: WHERE NOT bar.value @> '{"bar1": 123}'::jsonb. In the select list the arrays are re-assembled by id and key but now without the offending sub-documents.

Finally, in the main query the JSON documents are re-assembled:

SELECT id, jsonb_build_object(key, jarray)
FROM x  -- from above
GROUP BY 1;

The important thing to understand is that PostgreSQL JSON functions only operate on the level of the JSON document that you can explicitly indicate. Usually that is the top level of the document, unless you have an explicit path to some level in the document (like {foo1, 0, bar1}, but you don't have that). At that level of operation you can then unpack to do your processing such as removing objects.

Source Show
◀ Wstecz