1

I have a table named guest_group in a Postgres DB and a column named custom_fields with the following values: (example)

[
   {
      "value":"Frau Barbara Test",
      "display_name":"Name",
      "servicio_tags":[
         "full-name"
      ]
   },
   {
      "value":"118",
      "display_name":"Zimmernummer",
      "servicio_tags":[
         "room-number"
      ]
   },
   {
      "value":"likes postgreSQL",
      "display_name":"Traces",
      "servicio_tags":[
         "trace"
      ]
   }
]

I want to update all rows with the

  • value equals to Frau Barbara Test and where the servicio_tags array is equals to ["full-name"]
  • plus where the value equal to 118 where the servicio_tags array is equal to ["room-number"]

The Update should add a new object to the JSONB:

{
   "value":"likes JSONB",
   "display_name":"Traces",
   "servicio_tags":[
      "trace"
   ]
}

My try so far:

UPDATE guest_group 
SET custom_fields = 
jsonb_insert('[{"value": "Frau Barbara Test", "display_name": "Name", "servicio_tags": ["full-name"]}, {"value": "118", "display_name": "Zimmernummer", "servicio_tags": ["room-number"]}]'::jsonb,
'{0}', 
'{"value": "likes JSONB", "display_name": "Traces", "servicio_tags": ["trace"]}'::jsonb, 
true)
 where custom_fields::text like '%"Frau Barbara Test"%'
 and custom_fields::text like '%"118 "%'

This try changes the value in column of the specific row to the following:

[  
   {  
      "value":"Frau Barbara Test",
      "display_name":"Name",
      "servicio_tags":[  
         "full-name"
      ]
   },
   {  
      "value":"likes JSONB",
      "display_name":"Traces",
      "servicio_tags":[  
         "trace"
      ]
   },
   {  
      "value":"118",
      "display_name":"Zimmernummer",
      "servicio_tags":[  
         "room-number"
      ]
   }
]

So the:

   {
      "value":"likes postgreSQL",
      "display_name":"Traces",
      "servicio_tags":[
         "trace"
      ]
   }

gets lost. How can do the update without loosing this data?

2
  • 1
    Do you need the servicio_tags array to equal that exact value, or do you just need it to contain that respective string (possibly amongst others)? Commented Nov 2, 2019 at 17:04
  • The servicio_tags array has to be equal to that exact value. Commented Nov 2, 2019 at 17:05

2 Answers 2

1

I would recommend not to compare JSON as strings. Instead, use the vast array of JSON operators and functions. To find rows that contain Mrs. Test in a value, you could for example use the @> operator:

WHERE '[{"value": "Frau Barbara Test"}]'::jsonb <@ custom_fields

See also Postgresql query array of objects in JSONB field.

However, this doesn't yet check that it's the same object that has a full-name tag, so we might try

WHERE '[{"value": "Frau Barbara Test", "servicio_tags": ["full-name"]}]'::jsonb <@ custom_fields

That's much better than text comparison, but still not exactly what you asked for. Due to the semantics of the @> operator, it checks the tags array to contain a "full-name" string, not to have it as its single element.

To do that, it gets a bit more complicated, involving a subquery on the jsonb_array_elements and accessing its json components explicitly for comparison:

SELECT *
FROM guest_group
WHERE EXISTS(
  SELECT 1
  FROM jsonb_array_elements(custom_fields) AS el
  WHERE el->>'value' = 'Frau Barbara Test'
    AND el->'servicio_tags' = '["full-name"]'::jsonb)

You could also mix the two approaches using ANY:

SELECT *
FROM guest_group
WHERE '{"value": "Frau Barbara Test"}' <@ ANY(
  SELECT el
  FROM jsonb_array_elements(custom_fields) AS el
  WHERE el->'servicio_tags' = '["full-name"]'::jsonb)

(online demo)

Now equipped with these tools, you can add a second condition to match the room number. Then use

UPDATE guest_group 
SET custom_fields = jsonb_insert(custom_fields, '{0}', '{"value": "likes JSONB", "display_name": "Traces", "servicio_tags": ["trace"]}'::jsonb, true)
WHERE …
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot! Thats a great answer!
0

This seems to work:

UPDATE guest_group 
SET custom_fields = 
jsonb_insert(custom_fields::jsonb,'{0}','{"value": "likes JSONB", "display_name": "Traces", "servicio_tags": ["trace"]}'::jsonb) 
WHERE custom_fields::text like '%"Frau Barbara Test"%'
AND custom_fields::text like '%"117"%'

Comments

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.