From a8034df2bf7a5d07b57e760b664ec2efc1b77f24 Mon Sep 17 00:00:00 2001 From: Laurence Isla Date: Tue, 22 Mar 2022 05:30:45 -0500 Subject: [PATCH] Add how to on PostGIS and hstore data types (#516) --- .../working-with-postgresql-data-types.rst | 214 ++++++++++++++++++ postgrest.dict | 1 + 2 files changed, 215 insertions(+) diff --git a/docs/how-tos/working-with-postgresql-data-types.rst b/docs/how-tos/working-with-postgresql-data-types.rst index 7cba0a979..921be2ba5 100644 --- a/docs/how-tos/working-with-postgresql-data-types.rst +++ b/docs/how-tos/working-with-postgresql-data-types.rst @@ -78,3 +78,217 @@ You can use other comparative filters and also `PostgreSQL special date/time inp "due_date": "2022-02-27T06:00:00-05:00" } ] + +hstore +------ + +You can work with data types belonging to additional supplied modules such as `hstore `_. Let's use the following table: + +.. code-block:: postgres + + -- Activate the hstore module in the current database + create extension if not exists hstore; + + create table countries ( + id int primary key, + name hstore unique + ); + +The ``name`` column will have the name of the country in different formats. You can insert values using the string representation for that data type, for instance: + +.. tabs:: + + .. code-tab:: http + + POST /countries HTTP/1.1 + Content-Type: application/json + + [ + { "id": 1, "name": "common => Egypt, official => \"Arab Republic of Egypt\", native => مصر" }, + { "id": 2, "name": "common => Germany, official => \"Federal Republic of Germany\", native => Deutschland" } + ] + + .. code-tab:: bash Curl + + curl "http://localhost:3000/countries" \ + -X POST -H "Content-Type: application/json" \ + -d @- << EOF + [ + { "id": 1, "name": "common => Egypt, official => \"Arab Republic of Egypt\", native => مصر" }, + { "id": 2, "name": "common => Germany, official => \"Federal Republic of Germany\", native => Deutschland" } + ] + EOF + +Notice that the use of ``"`` in the value of the ``name`` column needs to be escaped using a backslash ``\``. + +You can also query and filter the value of a ``hstore`` column using the arrow operators, as you would do for a :ref:`JSON column`. For example, if you want to get the native name of Egypt, the query would be: + +.. tabs:: + + .. code-tab:: http + + GET /countries?select=name->>native&name->>common=like.Egypt HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/countries?select=name->>native&name->>common=like.Egypt" + +.. code-block:: json + + [{ "native": "مصر" }] + +PostGIS +------------------ + +You can use the string representation for `PostGIS `_ data types such as ``geometry`` or ``geography``. As an example, let's create a table using the ``geometry`` type (you need to `install PostGIS `_ first). + +.. code-block:: postgres + + -- Activate the postgis module in the current database + create extension if not exists postgis; + + create table coverage ( + id int primary key, + name text unique, + area geometry + ); + +Say you want to add areas in polygon format. The request using string representation would look like: + +.. tabs:: + + .. code-tab:: http + + POST /coverage HTTP/1.1 + Content-Type: application/json + + [ + { "id": 1, "name": "small", "area": "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))" }, + { "id": 2, "name": "big", "area": "SRID=4326;POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))" } + ] + + .. code-tab:: bash Curl + + curl "http://localhost:3000/coverage" \ + -X POST -H "Content-Type: application/json" \ + -d @- << EOF + [ + { "id": 1, "name": "small", "area": "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))" }, + { "id": 2, "name": "big", "area": "SRID=4326;POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))" } + ] + EOF + +Now, when you request the information, PostgREST will automatically cast the ``area`` column to ``JSON`` format. Although this output is useful, you will want to use the PostGIS functions to have more control on filters or casts. For these cases, creating a ``view`` is your best option. For example, let's use some of the functions to get the data in `GeoJSON format `_ and to calculate the area in square units: + +.. code-block:: postgres + + create or replace view coverage_geo as + select name, + -- Get the Geometry Object + st_AsGeoJSON(c.area)::json as geo_geometry, + -- Get the Feature Object + st_AsGeoJSON(c.*)::json as geo_feature, + -- Calculate the area in square units + st_area(c.area) as square_units + from coverage c; + + -- Create another view for the FeatureCollection Object + -- for the sake of making the examples clearer + create or replace view coverage_geo_collection as + select + json_build_object( + 'type', 'FeatureCollection', + 'features', json_agg(st_AsGeoJSON(c.*)::json) + ) + as geo_feature_collection + from coverage c; + +Now the query will return the information as you expected: + +.. tabs:: + + .. code-tab:: http + + GET /coverage_geo?name=eq.big HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/coverage_geo?name=eq.big" + +.. code-block:: json + + [ + { + "name": "big", + "geo_geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[10,0],[10,10],[0,10],[0,0]] + ] + }, + "geo_feature": { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[10,0],[10,10],[0,10],[0,0]] + ] + }, + "properties": { + "id": 2, + "name": "big" + } + }, + "square_units": 100 + } + ] + +And for the Feature Collection format: + +.. tabs:: + + .. code-tab:: http + + GET /coverage_geo_collection HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/coverage_geo_collection" + +.. code-block:: json + + [ + { + "geo_feature_collection": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[1,0],[1,1],[0,1],[0,0]] + ] + }, + "properties": { + "id": 1, + "name": "small" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[10,0],[10,10],[0,10],[0,0]] + ] + }, + "properties": { + "id": 2, + "name": "big" + } + } + ] + } + } + ] diff --git a/postgrest.dict b/postgrest.dict index 2b15bd67b..a20158ba4 100644 --- a/postgrest.dict +++ b/postgrest.dict @@ -53,6 +53,7 @@ Hasql Heroku HMAC Homebrew +hstore HTTP HTTPS HV