1

I have a nested JSON structure stored in a PostgreSQL table.

Table users:

id | content [JSON]

JSON:

{
  "purchases": [
    {
      "id": 1,
      "costs": [
        {
          "amount": 23
        },
        {
          "amount": 34
        }
      ]
    },
    {
      "id": 2,
      "costs": [
        {
          "amount": 42
        }
      ]
    }
  ]
}

I would like to add a field "jsonClass": "Static" to all the objects within the costs array so I have following in the end:

{
  "purchases": [
    {
      "id": 1,
      "costs": [
        {
          "jsonClass": "Static",
          "amount": 23
        },
        {
          "jsonClass": "Static",
          "amount": 34
        }
      ]
    },
    {
      "id": 2,
      "costs": [
        {
          "jsonClass": "Static",
          "amount": 42
        }
      ]
    }
  ]
}

I couldn't figure out how to add values to such a nested structure. Anyone knows how to achieve such thing? The only way I found was to make it a text and do string replace which is not very performant and I have a lot of such entries.

4
  • Ca you show your code ? Commented Sep 5, 2018 at 7:06
  • This would be soooo easy with a properly normalized data model. blog.2ndquadrant.com/… Commented Sep 5, 2018 at 7:07
  • @SiddharthRamani Which code? My string replacement? Commented Sep 5, 2018 at 7:16
  • @a_horse_with_no_name Yes, I know, but the given data is not normalized. It's a PSQL DB used as a document store :/ Commented Sep 5, 2018 at 7:16

1 Answer 1

2

Unfortunately, due to having to change multiple sub-objects, I don't know of a better way than to deconstruct and then reconstruct the object. It gets pretty hairy.

UPDATE users
SET content=(
  SELECT jsonb_agg(purchase)
  FROM (
    SELECT jsonb_build_object('id', pid, 'purchases', jsonb_agg(cost)) AS purchase
    FROM (
      SELECT pid, cost || '{"jsonClass":"static"}'::jsonb AS cost
      FROM (
        SELECT purchase->'id' AS pid, jsonb_array_elements(purchase->'costs') AS cost 
        FROM jsonb_array_elements(content::jsonb->'purchases') AS purchase
      ) AS Q  
    ) AS R
    GROUP BY pid
  ) AS S
);

Fiddle

EDIT: Sorry about all the edits, forgot to test for multiple rows. Should be good now. It might be possible to simplify it a bit more, not sure.

Sign up to request clarification or add additional context in comments.

2 Comments

It does its job which is super nice, but it's still takes too much time to apply these changes in one query. I'll do it in chunks.
Unfortunately, yeah, JSON manipulation is not fast. I don't think I can write it any faster (though it might be possible to be more compact). I refer you to @a_horse_with_no_name above :P

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.