Battalgazi
Battalgazi

Reputation: 379

Remove element from jsonb array

I have the following jsonb. From the array pages I would like to remove the element called 'pageb'. The solutions offered in similar questions are not working for me.

'{
  "data": {
    "id": "a1aldjfg3f",
    "pages": [
      {
        "type": "pagea"
      },
      {
        "type": "pageb"
      }                                
    ],
    "activity": "test"
  }
}'

My script right now looks like this. It doesnt return any error but the elements won't be removed.

  UPDATE database
  SET reports = jsonb_set(reports, '{data,pages}', (reports->'data'->'pages') - ('{"type":"pageb"}'), true)
  WHERE reports->'data'->'pages'  @> '[{"type":"pageb"}]';

Upvotes: 7

Views: 12462

Answers (4)

Denis N
Denis N

Reputation: 1

Enhanced variation of klin`s answer that supports the absence of a search element in the array

CREATE OR REPLACE FUNCTION jsonb_remove_array_element(arr jsonb, element jsonb) 
RETURNS jsonb LANGUAGE plpgsql IMMUTABLE AS $$
  DECLARE _idx integer;
  DECLARE _result jsonb;
  BEGIN
    _idx := (SELECT ordinality - 1 FROM jsonb_array_elements(arr) WITH ordinality WHERE value = element);
    IF _idx IS NOT NULL 
    THEN 
      _result := arr - _idx;
    ELSE
      _result := arr;
    END IF;
    RETURN _result;
  END;
$$;

Upvotes: 0

Hung Tran DI
Hung Tran DI

Reputation: 9

There you go

do $$
declare newvar jsonb;
begin
    newvar := jsonb '{ "customer": "John Doe", "buy": [{"product": "Beer","qty": 6},{"product": "coca","qty": 5}]}';
    newvar := jsonb_set(newvar,'{buy}', jsonb_remove((newvar->>'buy')::jsonb,'{"product": "Beer"}'));
    newvar := jsonb_set(newvar,'{buy}', jsonb_add((newvar->>'buy')::jsonb,'{"product": "cofe","qty": 6}'));
    RAISE NOTICE '%', newvar; 
end $$

create or replace function jsonb_remove(arr jsonb, element jsonb)
returns jsonb language sql immutable as $$
    select ('['||coalesce(string_agg(r::text,','),'')||']')::jsonb from jsonb_array_elements(arr) r where r @> element=false
$$;

create or replace function jsonb_add(arr jsonb, element jsonb)
returns jsonb language sql immutable as $$
    select arr||element
$$;

Upvotes: 0

klin
klin

Reputation: 121604

The - operator cannot be applied here because the right-hand operand is a string defining a key, per the documentation:

Delete key/value pair or string element from left operand. Key/value pairs are matched based on their key value.

Removing a json object from a json array can be done by unpacking the array and finding the index of the object. A query using this method may be too complicated, so defining a custom function is very handy in this case.

create or replace function jsonb_remove_array_element(arr jsonb, element jsonb)
returns jsonb language sql immutable as $$
    select arr- (
        select ordinality- 1
        from jsonb_array_elements(arr) with ordinality
        where value = element)::int
$$;

And the update:

update my_table
set reports = 
    jsonb_set(
        reports, 
        '{data,pages}', 
        jsonb_remove_array_element(reports->'data'->'pages', '{"type":"pageb"}')
        )
where reports->'data'->'pages'  @> '[{"type":"pageb"}]';

Working example in rextester.

Upvotes: 7

Ancoron
Ancoron

Reputation: 2733

The following is a combination of the answer provided for deleting an element inside an array reliably and the PostgreSQL's ability to use data-modifying WITH statements, but it needs an identity column (id in my test table) to work because of necessary correlation:

WITH new_reports AS (
    SELECT
        id,
        reports #- array['data','pages',(position - 1)::text] AS new_value
    FROM
        test,
        jsonb_array_elements(reports->'data'->'pages') WITH ORDINALITY arr(item, position)
    WHERE
        test.reports->'data'->'pages' @> '[{"type":"pageb"}]'
        AND
        item->>'type' = 'pageb'
    )
UPDATE test SET reports = new_reports.new_value FROM new_reports WHERE test.id = new_reports.id;

The test data I used:

SELECT reports FROM test;
                                               reports                                               
-----------------------------------------------------------------------------------------------------
 {"data": {"id": "a1aldjfg3f", "pages": [{"type": "pagea"}, {"type": "pagec"}], "activity": "test"}}
 {"data": {"id": "a1aldjfg3f", "pages": [{"type": "pagea"}, {"type": "pageb"}], "activity": "test"}}
 {"data": {"id": "a1aldjfg3f", "pages": [{"type": "pageb"}, {"type": "pagec"}], "activity": "test"}}
(3 rows)

...and after executing the query:

SELECT reports FROM test;
                                               reports                                               
-----------------------------------------------------------------------------------------------------
 {"data": {"id": "a1aldjfg3f", "pages": [{"type": "pagea"}, {"type": "pagec"}], "activity": "test"}}
 {"data": {"id": "a1aldjfg3f", "pages": [{"type": "pagea"}], "activity": "test"}}
 {"data": {"id": "a1aldjfg3f", "pages": [{"type": "pagec"}], "activity": "test"}}
(3 rows)

I hope that works for you.

Upvotes: 6

Related Questions