Search code examples
jsonpostgresqlviewjsonb

Postgres flatten JSONB cell into row in view


I have a simple postgres table:

    Column    │         Type         │ Modifiers
──────────────┼──────────────────────┼──────────────────────
 id           │ integer              │ not null default
 data         │ jsonb                │

Here's a simplified data structure for data:

{
  "id": 3,
  "date": "2019-01-01",
  "well_report_table":
    [
      {"element": "methane",
      "yield": 6,
      "price": 2.10
      },
      {"element": "pentane",
      "yield": 6,
      "price": 2.10
      },
      {"element": "butane",
      "yield": 6,
      "price": 3.50
      }
    ],
  "cost_report_table":
    [
      {"item": "fuel",
      "charge": 6.30
      },
      {"item": "lease",
      "charge": 200
      }
    ]
}

I'd like to flatten this in a view with the following columns:

id | date | well_report_table_methane_yield | well_report_table_methane_price | well_report_table_pentane_yield | well_report_table_pentane_price | well_report_table_butane_yield | well_report_table_butane_price |cost_report_table_fuel_charge | cost_report_table_lease_charge

The objects in my arrays have an identifier that I would like to append to the array object name and then iterate through the other keys in the object and make columns out of .

This question gets me close: Postgres: Flatten aggregated key/value pairs from a JSONB field?

I'm not entirely sure this is possible in something like plpgsql, so if I just need to generate the view text in a scripting language like ruby/python and then create a view off of that, I'm a ok with that.

Ideally I'll be able to use something like jsonb_array_elements and jsonb_each in order to avoid intermediate tables (all of my current attempts have required intermediate views), but I haven't found that magic combination yet.


Solution

  • This is not a general question about flattening JSON arrays, because there is a specific logic hidden in the arrays. You can implement the logic in this function:

    create or replace function flat_array(data jsonb, title text, item text)
    returns jsonb language sql immutable as $$
        select jsonb_object_agg(format('%s_%s_%s', title, elem->>item, key), value)
        from jsonb_array_elements(data->title) as arr(elem)
        cross join jsonb_each(elem)
        where key <> item
    $$;
    

    The query:

    select 
        jsonb_build_object('id', data->'id', 'date', data->'date') ||
        flat_array(data, 'well_report_table', 'element') ||
        flat_array(data, 'cost_report_table', 'item')
    from my_table
    

    gives the object:

    {
        "id": 3,
        "date": "2019-01-01",
        "cost_report_table_fuel_charge": 6.30,
        "cost_report_table_lease_charge": 200,
        "well_report_table_butane_price": 3.50,
        "well_report_table_butane_yield": 6,
        "well_report_table_methane_price": 2.10,
        "well_report_table_methane_yield": 6,
        "well_report_table_pentane_price": 2.10,
        "well_report_table_pentane_yield": 6
    }
    

    which can be converted to a tabular view in the way described in Flatten aggregated key/value pairs from a JSONB field?