-
-
Notifications
You must be signed in to change notification settings - Fork 582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question about hstore and geometry data types #1093
Comments
@Murthy10 and I made some more tests and investigations with Postgraphile and this is the current (untested) query to get what we (and GeoJSON) want:
Expecting following valid GeoJSON output (see http://geojsonlint.com/ ) edited by hand:
|
I think the geojson field is a scalar, so you can just stop at the geojson field; it doesn’t need (and will not accept) a selection set. I advise writing queries like this in GraphiQL, it should give you guidance. |
Thx for the advice. If you look at the desired GeoJSON output given above, it becomes obvious that it's currently not possible that the client can tell the GraphQL server to output the mandatory standardized outer JSON block, which looks like this: Can you give some hints on how and where to extend the Postgraphile resolvers? |
Sure. I don't use PostGIS so you're going to have to connect the dots yourself; but here's some options:
create function bakeries_within_geom(geom geometry) returns geometry as $$
select st_collect(feature) from ...
$$ language sql stable; This is preferred because it allows you to use all the PostGIS features inside of PostgreSQL whilst only exposing to the user exactly what you want them to see (your GraphQL API shouldn't really be a 1-to-1 map of your database). We call this a "Custom Query" and you can read more about them here: https://www.graphile.org/postgraphile/custom-queries/
This enables you to write GraphQL schema language and add fields and resolvers to your GraphQL API. It's a very manual way of doing things, but results in an API that's exactly the shape you specify. Read more here: https://www.graphile.org/postgraphile/make-extend-schema-plugin/ Perhaps @mattbretl or @singingwolfboy could shed more light? |
I appreciate solution 1 to write an SQL function that returns the data you desire and exposes it sensibly. But my goal is to offer an API for spatial data (from OpenStreetMap in this case) which involves two "non-base data types" 'geometry' and 'hstore' including the typical associated functions which are basically handled the same as base data types. So the "responsibility" to resolve 'geometry' and 'hstore' is at data type level (not schema/query type, nor custom query). What's more tricky is the fact, that GeoJSON mandates in a rather awkward way that the resultset has a fixed outer JSON block |
Is that what ST_collect does - wrap a list of features into a feature collection? PostGraphile has early support for custom aggregates, so you could potentially support this via adding an aggregate field on a connection with a plugin. The feature collection will still be nested inside the response object, because that’s how GraphQL works. Is that problematic for your use-case? |
Not really. This is how GeoJSON has been specified. See e.g. this snippet https://gist.githubusercontent.com/sfkeller/dbe90e8b54b23660c61938731560f28d/raw/d74047e7bf750ec31ceec145edff1909f3a40b99/map.geojson or http://geojson.io / or this http://geojsonlint.com/ (or the spec. https://tools.ietf.org/html/rfc7946 ). |
I'm not sure what your "not really" relates to; I think we're talking at cross-purposes. I understand the basics of the GeoJSON format, but I'm not super familiar with all the PostGIS operators. Seems that though
), but you cannot Imagine I have the following GraphQL schema (nothing to do with PostGraphile): scalar GeoJSON
type Query {
geojson: GeoJSON
} and I query it with this query: { geojson } Then (assuming success) the response I get over HTTP would be: {
"data": {
"geojson": /* SOMETHING HERE */
}
} for example: {
"data": {
"geojson": {
"type": "FeatureCollection",
"name": "GR_NBK_2_modif_Stefan",
...
}
}
} There will never be a root field, However, if the above is fine for your API (that the GeoJSON is nested inside regular JSON), then what I think you need is a single FeatureCollection somewhere inside your response. I don't know what Judging by this StackOverflow answer you should be able to do something like this to construct a FeatureCollection from a table that represent features (via the SELECT json_build_object(
'type', 'FeatureCollection',
'features', json_agg(json_build_object(
'type', 'Feature',
'id', YOUR_TABLE_HERE.gid,
'geometry', ST_AsGeoJSON(YOUR_TABLE_HERE.geom)::json,
'properties', (to_jsonb(YOUR_TABLE_HERE) - 'gid' - 'geom')::json
))
)
FROM YOUR_TABLE_HERE (I've changed it from JSONB to JSON for performance reasons, and rewritten the query so that it will work in our aggregates system.) So you can either write a function that returns this (as JSON), or you could implement it as a custom aggregate. Our custom aggregations does not have any helpers yet, but you can base it on our You might customise it to something like this (untested): export default function PgConnectionFeatureCollectionPlugin(builder) {
builder.hook(
"GraphQLObjectType:fields",
(fields, build, context) => {
const {
extend,
graphql: { GraphQLInt, GraphQLNonNull },
pgSql: sql,
getTypeByName,
} = build;
const {
scope: { isPgRowConnectionType, pgIntrospection: table },
fieldWithHooks,
Self,
} = context;
// Return if this isn't a table connection
if (
!isPgRowConnectionType ||
!table ||
table.kind !== "class" ||
!table.namespace
) {
return fields;
}
// Return if this table doesn't have `gid` and `geom` columns
if (!table.attributes.some(attr => attr.name === 'gid')
|| !table.attributes.some(attr => attr.name === 'geom')) {
return fields;
}
const GraphQLJSONType = getTypeByName('JSON');
return extend(
fields,
{
totalCount: fieldWithHooks(
"featureCollection",
({ addDataGenerator }) => {
addDataGenerator(() => {
return {
pgAggregateQuery: aggregateQueryBuilder => {
const sqlTable = aggregateQueryBuilder.getTableAlias();
aggregateQueryBuilder.select(
sql.fragment`
json_build_object(
'type', 'FeatureCollection',
'features', json_agg(json_build_object(
'type', 'Feature',
'id', ${sqlTable}.gid,
'geometry', ST_AsGeoJSON(${sqlTable}.geom)::json,
'properties', (to_jsonb(${sqlTable}) - 'gid' - 'geom')::json
))
)
`,
"featureCollection"
);
},
};
});
return {
description: `Feature collection from the features in this table.`,
type: GraphQLJSONType,
resolve(parent) {
return (
(parent.aggregates && parent.aggregates.featureCollection) || null
);
},
};
},
{
isPgConnectionFeatureCollectionAggregateField: true,
}
),
},
`Adding featureCollection field to connection '${Self.name}'`
);
}
);
} (Note this requires PostGraphile 4.4.0 or higher) This should add the |
Many thanks for your reply. Will take a while for us to digest. You wrote
and
Correct. See the most recent solution here: https://giswiki.hsr.ch/PostGIS_-_Tipps_und_Tricks#Export_a_table_with_a_geometry_attribute_to_GeoJSON which is based on this blog post http://blog.cleverelephant.ca/2019/03/geojson.html . |
I'm not sure what action is necessary on this, and it's not been active for over a year, so I'm going to close it. Hopefully the discussion above is useful though :) |
I'm submitting a feature request, newest PostGraphile version.
I'd like to make a query which is typical for a database derived from OpenStreetMap and I've read the issues, the docs and made several tests without success so far (struggling with #575 ).
Prerequisites: PostgreSQL 11 and PostGIS 2.5. Datatype hstore requires "CREATE EXTENSION hstore;" and geometry/geograph requires "CREATE EXTENSION postgis;" .
The OSM/PostGIS dataset can be generated using https://wiki.openstreetmap.org/wiki/Osm2pgsql#Installation with a OSM PBF file (e.g. Switzerland) from http://download.geofabrik.de/ . The resulting schema in PostgreSQL contains among others a table planet_osm_point (short: osm_point) and the attributes tags (type hstore), and attribute geom (type geography or geometry).
Let's say we want to get all bakeries in Uster (Switzerland) and a resultset in GeoJSON containing osm_id, name, street and geometry.
This is a solution in SQL (Query with PostgreSQL/PostGIS):
This is the GraphQL query I think which would come close to what we want:
Query:
Query variables:
This is a typical GeoJSON dataset containing bakeries in Uster: uster_bakeries_geojson.txt
The text was updated successfully, but these errors were encountered: