1
votes

I have a table like:

id: integer,
... other stuff...,
comments: array of jsonb

where the comments column has the following structure:

[{
        "uid": "comment_1",
        "message": "level 1 - comment 1",
        "comments": [{
            "uid": "subcomment_1",
            "message": "level 2 - comment 1",
            "comments": []
        }, {
            "uid": "subcomment_2",
            "message": "level 1 - comment 2",
            "comments": []
        }]
    },
    {
        "uid": "P7D1hbRq4",
        "message": "level 1 - comment 2",
        "comments": []
    }
]

I need to update a particular field, for example:comments[1](with uid = comment_1) -> comments[2] (with uid = subcomment_2) -> message = 'comment edited'.

I'm brand new to postgresql and I can't figure it out how to do this, not even close. I manage to merge objects and change message for level 1 with:

UPDATE tasks
    set comments[1] = comments[1]::jsonb || $$
      {
        "message": "something",
      }$$::jsonb
where id = 20;

but that's as far as I could go.

Any hints towards the right direction?

LE: I got this far:

UPDATE tasks
set comments[1] = jsonb_set(comments[1], '{comments,1, message}', '"test_new"')
where id = 20;

Sure, I can get this path from javascript but it's that a best practice? Not feeling comfortable using indexes from javascript arrays. Should I try to write a sql function to get the array and use the 'uid' as key? Any other simpler way to search/select using the 'uid' ?

LLE

I can't get it to work using suggestion at:this question (which I read and tried) Code bellow returns nothing:

-- get index for level 2
select pos as elem_index
from tasks,
     jsonb_array_elements(comments[0]->'comments') with ordinality arr(elem, pos)
where tasks.id = 20 and
      elem ->>'uid'='subcomment_1';

and I need it for several levels so it's not quite a duplicate.

1
see LE as I can't paste the code here - SharpBCD

1 Answers

1
votes

First, you cannot update a part of a column (an element of an array) but only a column as a whole.

Next, you should understand what the path (the second argument of the jsonb_set() function) means.

Last, the third argument of the function is a valid json, so a simple text value must be enclosed in both single and double quotes.

update tasks
set comments = jsonb_set(comments, '{0, comments, 1, message}', '"comment edited"')
where id = 1;

Path:

  • 0 - the first element of the outer array (elements are indexed from 0)
  • comments - an object with key comments
  • 1 - the second element of the comments array
  • message - an object message in the above element.

See Db<>fiddle.