I'm trying to produce GeoJSON from a query in Postgres 13.1:
SELECT json_build_object(
'type', 'FeatureCollection',
'features', json_agg(
json_build_object(
'type', 'Feature',
'geometry', ST_AsGeoJSON(geom)::json,
'properties', json_build_object(
-- list of fields
'id', hardinfra.id,
-- need json_object_agg here
'protections', json_build_object(
p.ptype,
i.pscore
),
-- need json_object_agg here
'responses', json_build_object(
ep.etype,
er.response
),
'category', c.category
)
)
)
)
FROM hardinfra
JOIN expertresponse er ON hardinfra.id = er.infra_id
JOIN expert ep ON ep.id = er.expert_id
JOIN infraprotection i ON hardinfra.id = i.infra_id
JOIN protection p ON p.id = i.protection_id
JOIN category c on c.id = hardinfra.category_id
;
However protections and responses should be key-value collections built up from the many-to-many relationships, usually using json_object_agg (see join conditions). How can I get around the lack of nested aggregation calls to produce these?
This is what the GeoJSON output should look like:
Note that the "protections" and "responses" properties can be of arbitrary length as they're coming from the related tables.
{
"features": [
{
"geometry": {
"coordinates": [
-6.147880554,
53.383533145
],
"type": "Point"
},
"properties": {
"id": 90,
"protections": {
"Foo": 1,
"Bar": 2,
"Baz": 3
},
"responses": {
"A": "response A",
"B": "response B",
"C": "response C"
}
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}
The tables are set up as follows (You'll need PostGIS, or substitute a plaintext field – that aspect isn't important here):
create table hardinfra (
id serial CONSTRAINT hardinfra_pkey PRIMARY KEY,
geom geometry(Geometry, 4326)
);
create table expert
(
id serial
constraint expert_pkey
primary key,
etype varchar(50)
);
create table expertresponse
(
infra_id integer not null
constraint expertresponse_infra_id_fkey
references hardinfra
on delete cascade,
expert_id integer not null
constraint expertresponse_expert_id_fkey
references expert
on delete cascade,
response varchar(280) not null,
constraint expertresponse_pkey
primary key (infra_id, expert_id)
);
create table protection
(
id serial
constraint protection_pkey
primary key,
ptype varchar(200)
);
create table infraprotection
(
infra_id integer not null
constraint infraprotection_infra_id_fkey
references hardinfra
on delete cascade,
protection_id integer not null
constraint infraprotection_protection_id_fkey
references protection
on delete cascade,
pscore integer default 0 not null,
constraint infraprotection_pkey
primary key (infra_id, protection_id)
);
SELECTwould be the case, but I'm struggling to write it.