Rich
Rich

Reputation: 185

Query to update the JSON value

I want to query to update a table that has JSON data. The data of the column detail in the table details are as follows.

id     | 1

detail | {
   myCompany: [{ email: '[email protected]', inProfit: true }],
   myData: [
      { email: '[email protected]'},
      { email: '[email protected]'},
      { email: '[email protected]' },
   ],
};

The JSON value of the column detail is as follows

{
   "myCompany":[
      {
         "email":"[email protected]",
         "inProfit":true
      }
   ],
   "myData":[
      {
         "email":"[email protected]"
      },
      {
         "email":"[email protected]"
      },
      {
         "email":"[email protected]",
      }
   ]
} 

I want a query that updates the myData. Add a new field personalUse:true for all fields of myData after email. For example, after running the update query the myData field should look like as follows,

"myData":[
      {
         "email":"[email protected]",
         "personalUse":true
      },
      {
         "email":"[email protected]",
         "personalUse":true
      },
      {
         "email":"[email protected]",
         "personalUse":true
      }
   ]

You can use the below queries to try it locally.

CREATE TABLE details (id bigserial primary key, detail json not null);

INSERT INTO details (detail) 
VALUES 
  ('{"myCompany":[{"email":"[email protected]", "inProfit":true } ],  
     "myData":[{"email":"[email protected]" }, 
               {"email":"[email protected]"},
               {"email":"[email protected]"} ] }');

Please, someone, help me out.

Upvotes: 1

Views: 213

Answers (1)

max taldykin
max taldykin

Reputation: 12898

There are several functions that may be helpful:

  • -> operator: gets JSON object field by key.
  • jsonb_agg: aggregates values as a JSON array.
  • json_array_elements: expands a JSON array to a set of JSON values.
  • jsonb_set: replaces part of JSON designated by path with a value (or inserts it if the part is missing).

You can read more here:

Query

update details
  set detail = jsonb_set(detail::jsonb, '{myData}', myData)
  from (
    select id, jsonb_agg(myData) as myData
      from (
        select
          id,
          jsonb_set(
            json_array_elements(detail->'myData')::jsonb,
            '{personalUse}',
            'true') as myData
          from details
      ) x
      group by id
  ) z
  where details.id = z.id;

How it works

Expand myData arrays along with id into table.

user=# select id, json_array_elements(detail->'myData') as myData from details;
 id |                        mydata                         
----+-------------------------------------------------------
  1 | {"email": "[email protected]"}
  1 | {"email": "[email protected]"}
  1 | {"email": "[email protected]"}
  2 | {"email": "[email protected]"}
  2 | {"email": "[email protected]"}

Use jsonb_set to update each object.

user=# select
user-#   id,
user-#   jsonb_set(
user(#     json_array_elements(detail->'myData')::jsonb,
user(#     '{personalUse}',
user(#     'true'
user(#   ) as myData
user-# from details;
 id |                        mydata                         
----+-------------------------------------------------------
  1 | {"email": "[email protected]", "personalUse": true}
  1 | {"email": "[email protected]", "personalUse": true}
  1 | {"email": "[email protected]", "personalUse": true}
  2 | {"email": "[email protected]", "personalUse": true}
  2 | {"email": "[email protected]", "personalUse": true}

Use jsonb_agg to collect emails back into JSON array.

user=# select id, jsonb_agg(myData) as myData from (
user(#   select
user(#     id,
user(#     jsonb_set(
user(#       json_array_elements(detail->'myData')::jsonb,
user(#       '{personalUse}',
user(#       'true'
user(#     ) as myData
user(#   from details
user(# ) x group by id;
 id |
----+-------------------------------------------------------------
  2 | [{"email": "[email protected]", "personalUse": true}, ...
  1 | [{"email": "[email protected]", "personalUse": true}, ...

Now you can use jsonb_set again to update original JSON.

Upvotes: 1

Related Questions