Home > database >  Extract json key/value pairs with postgresql/snowflake, where value is not null/blank
Extract json key/value pairs with postgresql/snowflake, where value is not null/blank

Time:02-10

I have a field called "filter" with json array like:

[
   {
      "after":"2021-10-14T00:00:00",
      "column":"createddate",
      "from":null,
      "gt":null,
      "gte":null,
      "id":"928d57b0",
      "lt":null,
      "lte":null,
      "not":null,
      "prior":null,
      "to":null,
      "value":null
   },
   {
      "after":null,
      "column":"last_coach_date",
      "from":"",
      "gt":null,
      "gte":null,
      "id":"01704cdd",
      "lt":null,
      "lte":null,
      "not":null,
      "prior":null,
      "to":"",
      "value":null
   }
]

I'm trying to extract key/value pairs where value is not null/blank and create another field for it.

Maybe to something like this or one long string:

{ "after": "2021-10-14T00:00:00", "column": "createddate", "id": "928d57b0"}, {"column": "last_coach_date","id": "01704cdd"} AS filter_value

CodePudding user response:

Using FLATTEN and OBJECT_AGG:

CREATE OR REPLACE TABLE t(col VARIANT)
AS
SELECT PARSE_JSON('[ { "after": "2021-10-14T00:00:00", "column": "createddate", "from": null, "gt": null, "gte": null, "id": "928d57b0", "lt": null, "lte": null, "not": null, "prior": null, "to": null, "value": null }, { "after": null, "column": "last_coach_date", "from": "", "gt": null, "gte": null, "id": "01704cdd", "lt": null, "lte": null, "not": null, "prior": null, "to": "", "value": null }]'
                );

Query:

SELECT s.index, OBJECT_AGG(s2.key, s2.value) AS output
FROM t
,LATERAL FLATTEN(input => col) s
,LATERAL FLATTEN(input =>  s.value) s2
WHERE NOT IS_NULL_VALUE(s2.value)  AND s2.value::TEXT != ''
GROUP BY s.INDEX;

Output:

enter image description here


CodePudding user response:

For Postgres:

select (select jsonb_object_agg(key, value)
        from jsonb_array_elements(t.filter) as x(item)
          cross join jsonb_each_text(x.item) as e(key,value)
        where e.value <> '')
from the_table t;

CodePudding user response:

So very similar to Lukasz solution, but OBJECT_AGG will filter out any NULL values, so if you cast to ::text the nulls become real nulls, and are filtered out, so the WHERE clause is not required:

WITH data_table AS(
  SELECT parse_json('[ { "after": "2021-10-14T00:00:00", "column": "createddate", "from": null, "gt": null, "gte": null, "id": "928d57b0", "lt": null, "lte": null, "not": null, "prior": null, "to": null, "value": null }, { "after": null, "column": "last_coach_date", "from": "", "gt": null, "gte": null, "id": "01704cdd", "lt": null, "lte": null, "not": null, "prior": null, "to": "", "value": null } ]') as json
)
SELECT f.index as batch_id,
    OBJECT_AGG(p.key, NULLIF(p.value::text,'')::variant) as obj
FROM data_table t,
  TABLE(FLATTEN(input=>t.json)) as f,
  TABLE(FLATTEN(input=>f.value)) as p
GROUP BY 1
ORDER BY 1
;

gives:

BATCH_ID OBJ
0 { "after": "2021-10-14T00:00:00", "column": "createddate", "id": "928d57b0" }
1 { "column": "last_coach_date", "id": "01704cdd" }

It's not clear from the question if you want 2 rows, or a single value, the latter can be had by adding another layer of aggregation via ARRAY_AGG:

WITH data_table AS(
  SELECT parse_json('[ { "after": "2021-10-14T00:00:00", "column": "createddate", "from": null, "gt": null, "gte": null, "id": "928d57b0", "lt": null, "lte": null, "not": null, "prior": null, "to": null, "value": null }, { "after": null, "column": "last_coach_date", "from": "", "gt": null, "gte": null, "id": "01704cdd", "lt": null, "lte": null, "not": null, "prior": null, "to": "", "value": null } ]') as json
)
SELECT array_agg(obj) as single_answer
FROM (
  SELECT f.index as batch_id,
      OBJECT_AGG(p.key, NULLIF(p.value::text,'')::variant) as obj
  FROM data_table t,
    TABLE(FLATTEN(input=>t.json)) as f,
    TABLE(FLATTEN(input=>f.value)) as p
  GROUP BY 1
)
;

gives:

SINGLE_ANSWER
[ { "after": "2021-10-14T00:00:00", "column": "createddate", "id": "928d57b0" }, { "column": "last_coach_date", "id": "01704cdd" } ]
  • Related