dvsoukup
dvsoukup

Reputation: 1596

postgresql - key/value lookup to json object

Running Postgres 9.6.

So I have this key/value lookup table which establishes the deepest child value of a huge JSON object. Given a table of this structure:

CREATE TABLE myschema.file_items
(
    id integer NOT NULL DEFAULT nextval('file_items_id_seq'::regclass),
    file_id integer NOT NULL,
    key character varying[] COLLATE pg_catalog."default" NOT NULL,
    value character varying COLLATE pg_catalog."default",
    status character varying COLLATE pg_catalog."default",
    CONSTRAINT file_items_pkey PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

ALTER TABLE verification.file_items
    OWNER to postgres;

insert into file_items (file_id, key, value, status)
values (1, '{"cogs","cog1","description"}', 'val1', 'approved');
insert into file_items (file_id, key, value, status)
values (1, '{"cogs","cog1","cost"}', '100', null);
insert into file_items (file_id, key, value, status)
values (1, '{"cogs","cog1","window"}', '[-200,500]', 'not verified');
insert into file_items (file_id, key, value, status)
values (1, '{"cogs","cog2","description"}', 'val2', 'approved');
insert into file_items (file_id, key, value, status)
values (1, '{"cogs","cog2","cost"}', '200', null);
insert into file_items (file_id, key, value, status)
values (1, '{"cogs","cog2","window"}', '[-300,500]', null);

insert into file_items (file_id, key, value, status)
values (1, '{"widgets","widget1","description"}', 'wid1', 'approved');
insert into file_items (file_id, key, value, status)
values (1, '{"widgets","widget1","cost"}', '100', 'approved');
insert into file_items (file_id, key, value, status)
values (1, '{"widgets","widget1","window"}', '[-200,500]', 'not verified');
insert into file_items (file_id, key, value, status)
values (1, '{"widgets","widget2","description"}', 'wid2', null);
insert into file_items (file_id, key, value, status)
values (1, '{"widgets","widget2","cost"}', '300', 'approved');
insert into file_items (file_id, key, value, status)
values (1, '{"widgets","widget2","window"}', '[-1000,700]', null);

I can query all my cogs like so:

select *
from file_items
where 'cogs' = any(key)

How would I reverse-engineer this object? Rather, I'd like to somehow generate a json object with the following format:

"cogs": {
    "cog1": {
        "description": "val1",
        "cost":100,
        "window":[-200,500]
    },
    "cog2": {
        "description": "val2",
        "cost":200,
        "window":[-300,500]
    }
}

Note that I'm deliberately not wanting to do an array of cogs objects. They are actual properties to the cogs object. Done this way as we can have incoming json objects to which we don't know all the properties of, thus we utilize a key/value mapping table to be able to dynamically identify what these property values are (ie, we don't know before hand that we're going to have a "cog67" object, or what kind of properties will be affixed to that object....).

Since this query would ultimately be fired from a Node.js package ('pg' module...), if I can't re-create the json object via query, I may need to do it in the javascript itself. Just wondering if it's possible to to correctly build the json object at the database level and return that though, rather than querying a bunch of rows and re-constructing the object in the server-side code.

Any help would be greatly appreciated! Thank you!

Upvotes: 1

Views: 1320

Answers (1)

klin
klin

Reputation: 121514

Use jsonb_object_agg() twice, for both levels of aggregation (jsonb_pretty() not necessary, used for a nice output):

select jsonb_pretty(jsonb_build_object(key, jsonb_object_agg(subkey, value)))
from (
    select key[1], key[2] as subkey, jsonb_object_agg(key[3], value) as value
    from file_items
    where 'cogs' = any(key)
    group by key[1], key[2]
    ) s
group by key;

            jsonb_pretty             
-------------------------------------
 {                                  +
     "cogs": {                      +
         "cog1": {                  +
             "cost": "100",         +
             "window": "[-200,500]",+
             "description": "val1"  +
         },                         +
         "cog2": {                  +
             "cost": "200",         +
             "window": "[-300,500]",+
             "description": "val2"  +
         }                          +
     }                              +
 }
(1 row)

Upvotes: 2

Related Questions