question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add logical PostGIS operators support to the plugin

See original GitHub issue

They include the official logical operators (see https://postgis.net/docs/reference.html, section 8.8) and other operators returning a boolean value such as spatial relationships functions (e.g. ST_Disjoint, see section 8.9).

An experimental extension was made in the fork https://github.com/jepetko/postgraphile-plugin-connection-filter/commits/master incl. test setup of the dedicated “postgis” schema alongside the already existing “p” schema.

Example (see https://github.com/jepetko/postgraphile-plugin-connection-filter/blob/master/__tests__/fixtures/postgis-queries/postgis-connections-filter.graphql):

query {
  overlapping: allFilterables(filter: { theGeom: { overlapsBB: "LINESTRING(1 2, 4 6)" } }) { ...filterableConnection }
  disjointG: allFilterables(filter: { theGeom: { disjointsG: "LINESTRING(0 5, 4 6)" } }) { ...filterableConnection }
  intersectsG: allFilterables(filter: { theGeom: { intersectsG: "LINESTRING(-1 1, 3 1)" } }) { ...filterableConnection }
}

fragment filterableConnection on FilterablesConnection {
  pageInfo {
    ...
  }
  totalCount
  edges {
    cursor
    node {
      id
      theGeom
    }
  }
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:18 (11 by maintainers)

github_iconTop GitHub Comments

3reactions
mattbretlcommented, Mar 20, 2019

FYI, I just published this: https://github.com/mattbretl/postgraphile-plugin-connection-filter-postgis

Note: @graphile/postgis only supports SRID 4326 at the moment, so if you need to use other SRIDs, you still need a custom plugin like the examples above.

3reactions
mattbretlcommented, Feb 6, 2019

Sorry for the delay… this fell off my todo list. We still need a PostGIS plugin that does the upfront introspection work necessary to expose the right operators per geometry/geography type.

The approach you shared using allowedFieldTypes: ['String'] worked through beta.27, but beta.28 checks whether the field is a true string type (text, varchar, etc.) before exposing filter operators. As a result, your code won’t work with beta.28. If you (or anyone else) still needs this functionality, the fix is to register a GraphQL input type for the PostGIS geometry type. Here’s a full example based on the code you provided:

create extension postgis;

create schema app_public;

create table app_public.foo (
  id serial primary key,
  geom geometry(point,3857)
);

insert into app_public.foo (geom) values
  ('SRID=3857;POINT(1 1)'::geometry),
  ('SRID=3857;POINT(2 2)'::geometry),
  ('SRID=3857;POINT(3 3)'::geometry);
// PgConnectionArgFilterPostgisOperatorsPlugin.js
module.exports = function PgConnectionArgFilterPostgisOperatorsPlugin(builder) {
  // Register geometry type
  builder.hook("build", build => {
    const {
      graphql: { GraphQLScalarType, Kind },
      pgIntrospectionResultsByKind: introspectionResultsByKind,
      pgRegisterGqlTypeByTypeId,
      pgRegisterGqlInputTypeByTypeId
    } = build;
    const postgisExtension = introspectionResultsByKind.extension.find(
      e => e.name === "postgis"
    );
    const postgisGeometryType = introspectionResultsByKind.type.find(
      t =>
        t.name === "geometry" && t.namespaceId === postgisExtension.namespaceId
    );

    const GeometryType = new GraphQLScalarType({
      name: "Geometry",
      description:
        "Geometry property of a GeoJSON object; coordinates must be in EPSG:3857.",
      serialize: value => String(value),
      parseValue: value => String(value),
      parseLiteral: ast => {
        if (ast.kind !== Kind.STRING) {
          throw new Error("Can only parse string values");
        }
        return ast.value;
      }
    });

    pgRegisterGqlTypeByTypeId(postgisGeometryType.id, () => GeometryType);
    pgRegisterGqlInputTypeByTypeId(postgisGeometryType.id, () => GeometryType);

    return build;
  });

  // Add operators
  builder.hook("build", (input, build) => {
    const { addConnectionFilterOperator, pgSql: sql } = build;
    addConnectionFilterOperator(
      "bboxIntersects2D",
      `Returns TRUE if A's 2D bounding box intersects B's 2D bounding box.`,
      fieldInputType => fieldInputType,
      (identifier, val) => {
        return sql.query`CASE WHEN ST_SRID(${identifier}) <> 3857
          THEN ${identifier} && ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON(${val}), 3857), ST_SRID(${identifier}))
          ELSE ${identifier} && ST_GeomFromGeoJSON(${val})
          END`;
      },
      {
        allowedFieldTypes: ["Geometry"]
      }
    );

    return input;
  });
};
const express = require("express");
const { postgraphile } = require("postgraphile");
const PgConnectionFilterPlugin = require("postgraphile-plugin-connection-filter");
const PgConnectionFilterPostgisPlugin = require("./PgConnectionArgFilterPostgisOperatorsPlugin.js");

const app = express();

app.use(
  postgraphile(process.env.DATABASE_URL, "app_public", {
    appendPlugins: [PgConnectionFilterPlugin, PgConnectionFilterPostgisPlugin],
    graphiql: true
  })
);

app.listen(5000);
{
  allFoos(filter: {
    geom: {
      bboxIntersects2D: "{\"type\":\"LineString\",\"coordinates\":[[2,2],[4,5]]}"
    }
  }) {
    nodes {
      id
    }
  }
}
{
  "data": {
    "allFoos": {
      "nodes": [
        {
          "id": 2
        },
        {
          "id": 3
        }
      ]
    }
  }
}

Hope that helps.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Chapter 8. PostGIS Reference
A type cast converts values of one data type into another type. PostgreSQL allows defining casting behavior for custom types, along with the...
Read more >
Chapter 4. Data Management - PostGIS
Functions allow creating geometry objects, accessing or updating all internal fields, and compute new geometry values. PostGIS supports all the functions ...
Read more >
Chapter 2. PostGIS Installation
This chapter details the steps required to install PostGIS. ... If you enabled raster support you may want to read below how to...
Read more >
Chapter 15. PostGIS Special Functions Index
PostGIS extends the function to support all common geometry types. ... The functions and operators given below are PostGIS functions/operators that take as ......
Read more >
Chapter 2. PostGIS Installation
Since raster functions are part of the postgis extension, extension support is not enabled if PostGIS is built without raster. The first step...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found