Filtering¶
The items of a collection can be filtered with a CQL2 expression, as defined by OGC API - Features - Part 3. QGIS sends the filter of a layer this way, instead of downloading every feature to filter them itself.
The filter parameters¶
The filter parameter of /collections/{collectionId}/items takes an expression in the text encoding of CQL2:
curl "https://example.com/oapif/collections/my_app.building/items" \
--get \
--data-urlencode "filter=floors >= 3 AND name LIKE 'Maison%'"
filter-langcan only becql2-text, its default: the JSON encoding of CQL2 is not supported.filter-crsgives the CRS of the geometries in the filter, and defaults to CRS84. Like the other CRS parameters, it takes a CRS that the collection offers.
A filter applies with the other parameters: the items match both the filter and the bbox, and the
numberMatched and the paging links account for it.
A filter that cannot be applied is rejected with a 400 Bad Request, which says why. This covers a syntax error,
a property that is not a queryable, a value of the wrong type, and an operator or function that is
not supported.
Supported expressions¶
| Conformance class | Expressions |
|---|---|
| Basic CQL2 | =, <>, <, <=, >, >=, IS [NOT] NULL, AND, OR, NOT, DATE('…'), TIMESTAMP('…') |
| Advanced comparison operators | [NOT] LIKE, [NOT] BETWEEN, [NOT] IN |
| Case-insensitive comparison | CASEI(…), with =, <>, LIKE and IN |
| Basic spatial functions | S_INTERSECTS, with POINT, LINESTRING, POLYGON, their multi-parts and BBOX |
S_DISJOINT, S_CONTAINS, S_WITHIN, S_TOUCHES, S_CROSSES, S_OVERLAPS and S_EQUALS are accepted as well.
Arithmetic, and the temporal and array functions, are not supported.
Comparisons follow the three-valued logic of CQL2: a comparison with a null value is neither true nor false, and
neither is its negation. NOT (floors = 3) does not match the buildings whose floors is null, just as QGIS
would not.
In LIKE, % stands for any characters, _ for a single one, and a backslash makes the next character an
ordinary one: '100\%' matches 100%. A quote in a string is written twice, 'Route d''Oron'.
Queryables¶
The properties that a filter can take are the queryables of the collection, described by a JSON Schema at
/collections/{collectionId}/queryables, which the collection links to:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/oapif/collections/my_app.building/queryables",
"type": "object",
"title": "my_app.Building",
"properties": {
"name": {"title": "Name", "maxLength": 255, "type": "string"},
"floors": {"title": "Floors", "type": "integer"},
"geom": {
"title": "geometry",
"x-ogc-role": "primary-geometry",
"format": "geometry-polygon",
"$ref": "https://geojson.org/schema/Polygon.json"
}
},
"additionalProperties": false
}
The geometry refers to its GeoJSON schema, the only way QGIS recognizes a geometry queryable by. QGIS only sends the parts of a filter that take queryables, and filters the features on the others itself.
By default, the queryables are the properties of the features, the ones in fields and not in exclude, and the
geometry. Override get_queryables() to restrict them, for instance to the fields that have an index:
@oapif.register(Building)
class BuildingCollection(OapifCollection):
def get_queryables(self, request):
return ("name", "floors", "geom")
The queryables are among the fields of the collection: a property that is not exposed cannot be filtered on, and neither can the fields of a related model.