Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkralidis committed Jan 14, 2025
1 parent 4154129 commit 7760741
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 31 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ jobs:
python3 setup.py install
pip3 install --global-option=build_ext --global-option="-I/usr/include/gdal" GDAL==`gdal-config --version`
#pip3 install --upgrade rasterio==1.1.8
pip3 install https://github.com/geopython/pygeofilter/archive/main.zip
- name: setup test data ⚙️
run: |
python3 tests/load_es_data.py tests/data/ne_110m_populated_places_simple.geojson geonameid
Expand All @@ -125,7 +124,7 @@ jobs:
pytest tests/test_csv__provider.py
pytest tests/test_django.py
pytest tests/test_elasticsearch__provider.py
# pytest tests/test_opensearch__provider.py
pytest tests/test_opensearch__provider.py
pytest tests/test_esri_provider.py
pytest tests/test_filesystem_provider.py
pytest tests/test_geojson_provider.py
Expand Down
52 changes: 29 additions & 23 deletions docs/source/cql.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
CQL support
===========

OGC Common Query Language (`CQL2`_) is a generic language designed to provide enhanced query and subset/filtering to (primarily) feature and record data.

Providers
---------

As of now the available providers supported for CQL filtering are limited to :ref:`Elasticsearch <Elasticsearch>` and :ref:`PostgreSQL <PostgreSQL>`.

CQL2 support is implemented in various pygeoapi feature and record providers. See the :ref:`feature <ogcapi-features>` and :ref:`metadata <ogcapi-records>` provider sections
for current provider support.

Limitations
-----------

Support of CQL is limited to `Simple CQL filter <https://portal.ogc.org/files/96288#cql-core>`_ and thus it allows to query with the
Support of CQL is limited to `Basic CQL2 <https://docs.ogc.org/is/21-065r2/21-065r2.html#cql2-core>`_ and thus it allows to query with the
following predicates:

* comparison predicates
Expand All @@ -21,20 +24,20 @@ following predicates:
Formats
-------

At the moment Elasticsearch supports only the CQL dialect with the JSON encoding `CQL-JSON <https://portal.ogc.org/files/96288#simple-cql-JSON>`_.
Supported providers leverage the CQL2 dialect with the JSON encoding `CQL-JSON <https://docs.ogc.org/is/21-065r2/21-065r2.html#cql2-json>`_.

PostgreSQL supports both CQL-JSON and CQL-text dialects, `CQL-JSON <https://portal.ogc.org/files/96288#simple-cql-JSON>`_ and `CQL-TEXT <https://portal.ogc.org/files/96288#simple-cql-text>`_
PostgreSQL supports both `CQL2 JSON <https://docs.ogc.org/is/21-065r2/21-065r2.html#cql2-json>`_ and `CQL text <https://docs.ogc.org/is/21-065r2/21-065r2.html#cql2-text>`_ dialects.

Queries
^^^^^^^

The PostgreSQL provider uses `pygeofilter <https://github.com/geopython/pygeofilter>`_ allowing a range of filter expressions, see examples for:

* `Comparison predicates <https://portal.ogc.org/files/96288#simple-cql_comparison-predicates>`_
* `Spatial predicates <https://portal.ogc.org/files/96288#enhanced-spatial-operators>`_
* `Temporal predicates <https://portal.ogc.org/files/96288#simple-cql_temporal>`_
* `Comparison predicates (`Advanced <https://docs.ogc.org/is/21-065r2/21-065r2.html#advanced-comparison-operators>`_, `Case-insensitive <https://docs.ogc.org/is/21-065r2/21-065r2.html#case-insensitive-comparison>`_)
* `Spatial predicates <https://docs.ogc.org/is/21-065r2/21-065r2.html#spatial-functions>`_
* `Temporal predicates <https://docs.ogc.org/is/21-065r2/21-065r2.html#temporal-functions>`_

Using Elasticsearch the following type of queries are supported right now:
Using Elasticsearch the following type of queries are supported currently:

* ``between`` predicate query
* Logical ``and`` query with ``between`` and ``eq`` expression
Expand All @@ -59,11 +62,11 @@ A ``BETWEEN`` example for a specific property through an HTTP POST request:
curl --location --request POST 'http://localhost:5000/collections/nhsl_hazard_threat_all_indicators_s_bc/items?f=json&limit=50&filter-lang=cql-json' \
--header 'Content-Type: application/query-cql-json' \
--data-raw '{
"between": {
"value": { "property": "properties.MHn_Intensity" },
"lower": 0.59,
"upper": 0.60
}
"op": "between",
"args": [
{"property": "properties.MHn_Intensity"},
[0.59, 0.60]
]
}'
Or
Expand All @@ -73,11 +76,11 @@ Or
curl --location --request POST 'http://localhost:5000/collections/recentearthquakes/items?f=json&limit=10&filter-lang=cql-json'
--header 'Content-Type: application/query-cql-json'
--data-raw '{
"between":{
"value":{"property": "ml"},
"lower":4,
"upper":4.5
}
"op": "between",
"args": [
{"property": "ml"},
[4, 4.5]
]
}'
The same ``BETWEEN`` query using HTTP GET request formatted as CQL text and URL encoded as below:
Expand All @@ -93,7 +96,11 @@ An ``EQUALS`` example for a specific property:
curl --location --request POST 'http://localhost:5000/collections/recentearthquakes/items?f=json&limit=10&filter-lang=cql-json'
--header 'Content-Type: application/query-cql-json'
--data-raw '{
"eq":[{"property": "user_entered"},"APBE"]
"op": "=",
"args": [
{"property": "user_entered"},
"APBE"
]
}'
A ``CROSSES`` example via an HTTP GET request. The CQL text is passed via the ``filter`` parameter.
Expand All @@ -115,7 +122,6 @@ The same example, but this time providing a geometry in EWKT format:
curl "http://localhost:5000/collections/beni/items?filter=DWITHIN(geometry,SRID=3857;POINT(1392921%205145517),100,meters)"
Note that the CQL text has been URL encoded. This is required in curl commands but when entering in a browser, plain text can be used e.g. ``CROSSES(foo_geom, LINESTRING(28 -2, 30 -4))``.

.. _`CQL2`: https://docs.ogc.org/is/21-065r2/21-065r2.html
2 changes: 1 addition & 1 deletion pygeoapi/django_/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def collection_items(request: HttpRequest, collection_id: str) -> HttpResponse:
'create', collection_id, skip_valid_check=True)
else:
response_ = execute_from_django(
itemtypes_api.post_collection_items,
itemtypes_api.get_collection_items,
request, collection_id, skip_valid_check=True,)
elif request.method == 'OPTIONS':
response_ = execute_from_django(itemtypes_api.manage_collection_item,
Expand Down
2 changes: 1 addition & 1 deletion pygeoapi/starlette_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ async def collection_items(request: Request, collection_id=None, item_id=None):
'create', collection_id, skip_valid_check=True)
else:
return await execute_from_starlette(
itemtypes_api.post_collection_items,
itemtypes_api.get_collection_items,
request,
collection_id,
skip_valid_check=True,
Expand Down
8 changes: 4 additions & 4 deletions tests/test_postgresql_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Bernhard Mallinger <[email protected]>
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2024 Tom Kralidis
# Copyright (c) 2025 Tom Kralidis
# Copyright (c) 2022 John A Stevenson and Colin Blackburn
# Copyright (c) 2023 Francesco Bartoli
# Copyright (c) 2024 Bernhard Mallinger
Expand Down Expand Up @@ -580,7 +580,7 @@ def test_get_collection_items_postgresql_cql_bad_cql(pg_api_, bad_cql):
assert error_response['description'] == 'Bad CQL text'


def test_post_collection_items_postgresql_cql(pg_api_):
def test_get_collection_items_postgresql_cql_json(pg_api_):
"""
Test for PostgreSQL CQL - requires local PostgreSQL with appropriate
data. See pygeoapi/provider/postgresql.py for details.
Expand Down Expand Up @@ -625,7 +625,7 @@ def test_post_collection_items_postgresql_cql(pg_api_):
assert ids == expected_ids


def test_post_collection_items_postgresql_cql_invalid_filter_language(pg_api_):
def test_get_collection_items_postgresql_cql_json_invalid_filter_language(pg_api_): # noqa
"""
Test for PostgreSQL CQL - requires local PostgreSQL with appropriate
data. See pygeoapi/provider/postgresql.py for details.
Expand Down Expand Up @@ -657,7 +657,7 @@ def test_post_collection_items_postgresql_cql_invalid_filter_language(pg_api_):
# At some point this may return UnexpectedEOF
'{"in": {"value": {"property": "id"}, "list": [1, 2}}'
])
def test_post_collection_items_postgresql_cql_bad_cql(pg_api_, bad_cql):
def test_get_collection_items_postgresql_cql_json_bad_cql(pg_api_, bad_cql):
"""
Test for PostgreSQL CQL - requires local PostgreSQL with appropriate
data. See pygeoapi/provider/postgresql.py for details.
Expand Down

0 comments on commit 7760741

Please sign in to comment.