Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

recursively flatten a nested jsonb in postgres without unknown depth and unknown key fields

How can I recursively flatten a nested jsonb in postgres which I don't know the depth and the field at each depth? (see example below)

A postgressql query to do the flattening would be much apreciated

    {
       "xx": "",
       "xx": "",
       "form": "xxx",
       "type": "",
       "content_type": "xxx",
       "reported_date": ,
       "contact": {
           "imported_date": "",
           "name": "",
           "phone": "",
           "alternate_phone": "",
           "specialization": "",
           "type": "",
           "reported_date": ,
           "parent": {
               "_id": "xxx",
               "_rev": "xxx",
               "parent": "",
               "type": "xxx" 
               } 
        }
    }

I have searched in stack overflow but they only consider jsonb's which have a single depth and the keys are already known before

like image 706
rosteve Avatar asked Aug 09 '17 08:08

rosteve


1 Answers

Example setup:

create table my_table(id int, data jsonb);
insert into my_table values
(1,
$${
   "type": "a type",
   "form": "a form",
   "contact": {
       "name": "a name",
       "phone": "123-456-78",
       "type": "contact type",
       "parent": {
           "id": "444",
           "type": "parent type" 
           } 
    }
}$$);

The recursive query executes jsonb_each() for every json object found on any level. New key names contain full path from the root:

with recursive flat (id, key, value) as (
    select id, key, value
    from my_table,
    jsonb_each(data)
union
    select f.id, concat(f.key, '.', j.key), j.value
    from flat f,
    jsonb_each(f.value) j
    where jsonb_typeof(f.value) = 'object'
)
select id, jsonb_pretty(jsonb_object_agg(key, value)) as data
from flat
where jsonb_typeof(value) <> 'object'
group by id;

 id |                   data                   
----+------------------------------------------
  1 | {                                       +
    |     "form": "a form",                   +
    |     "type": "a type",                   +
    |     "contact.name": "a name",           +
    |     "contact.type": "contact type",     +
    |     "contact.phone": "123-456-78",      +
    |     "contact.parent.id": "444",         +
    |     "contact.parent.type": "parent type"+
    | }
(1 row)

If you want to get a flat view of this data you can use the function create_jsonb_flat_view() described in this answer Flatten aggregated key/value pairs from a JSONB field?

You need to create a table (or view) with flattened jsonb:

create table my_table_flat as 
-- create view my_table_flat as 
with recursive flat (id, key, value) as (
-- etc as above
-- but without jsonb_pretty()

Now you can use the function on the table:

select create_jsonb_flat_view('my_table_flat', 'id', 'data');

select * from my_table_flat_view;


 id | contact.name | contact.parent.id | contact.parent.type | contact.phone | contact.type |  form  |  type  
----+--------------+-------------------+---------------------+---------------+--------------+--------+--------
  1 | a name       | 444               | parent type         | 123-456-78    | contact type | a form | a type
(1 row)

The solution works in Postgres 9.5+, as it uses jsonb function introduced in this version. If your server version is older it is highly recommended to upgrade Postgres anyway to use jsonb efficiently.

like image 94
klin Avatar answered Oct 19 '22 03:10

klin