Home > Software engineering >  modify only last value in jsonb[] field
modify only last value in jsonb[] field

Time:01-29

I have table with jsonb[] field

Example:

id   jsonb_t
1    [ {"x" : 1 , "y": 2} , {"x" : 2 , "y": 3} , {"x": 3, "y" : 4} ]
2    [ {"x" : 1 , "y": 3} , {"x" : 3 , "y": 3} , {"x": 8, "y" : 2} ]
3    [ {"x" : 1 , "y": 4} , {"x" : 4 , "y": 3} , {"x": 5, "y" : 9} ]

I want to modify table where id = 3 but only the last row in jsonb[] array , it means replace e.g. "y":9 into "y":8 , and increment "x":5 by 1 to "x":6 . I can't figure out how to do it in one step (replace and increment should be done "in place" due to thousends rows in jsonb[] array field) , thanks in advance for help .

CodePudding user response:

You can use some jsonb functions such alike

SELECT jsonb_agg(jsonb_build_object('x', x, 'y', y))
  FROM (SELECT CASE
                 WHEN row_number() over() = jsonb_array_length(jsonb_t) THEN
                  x   1
                 ELSE
                  x
               END AS x,
               CASE
                 WHEN row_number() over() = jsonb_array_length(jsonb_t) THEN
                  y - 1
                 ELSE
                  y
               END AS y
          FROM t, jsonb_to_recordset(jsonb_t) AS(x INT, y INT)
         WHERE id = 3) AS j

Demo

where jsonb_to_recordset expands outermost array of objects as individual integer elements, then (in/de)crement them after determining match through use of row_number and jsonb_array_length functions, then go back to build up the jsonb value again within the main query.

CodePudding user response:

Your sample data looks like the column is in fact defined as jsonb not jsonb[] and the array is a proper JSON array (not an array of jsonb values)

If that is correct, then you can use jsonb_set() to extract and modify the value of the last array element:

update the_table
  set jsonb_t = jsonb_set(jsonb_t, 
                    array[jsonb_array_length(jsonb_t)-1]::text[], 
                    jsonb_t -> jsonb_array_length(jsonb_t)-1 || 
                        '{"y":8}' || 
                         jsonb_build_object('x', (jsonb_t -> jsonb_array_length(jsonb_t)-1 ->> 'x')::int   1)
                 )
where id = 3

As documented in the manual jsonb_set() takes three parameters: the input value, the path to the value that should be changed and the new value.

The second parameter array[jsonb_array_length(jsonb_t)-1]::text[] calculates the target position in the JSON array by taking its length and subtracting one to get the last element. This integer is then converted to a text array (which is the required type for the second parameter).

The expression jsonb_t -> jsonb_array_length(jsonb_t)-1 then picks that array element and appends the '{"y":8}' which will replace the existing key/value pair with y. The expression

jsonb_build_object('x', (jsonb_t -> jsonb_array_length(jsonb_t)-1 ->>  'x')::int   1

extracts the current value of the x key, converting it to an integer, increments it by one and builds a new JSON object with the key x that is also appended to the old value, thus replacing the existing key.

Online example

  •  Tags:  
  • Related