Skip to content

Commit

Permalink
Merge branch 'collection_update_endpoint' of github.com:rhysrevans3/s…
Browse files Browse the repository at this point in the history
…tac-fastapi-elasticsearch into collection_update_endpoint
  • Loading branch information
rhysrevans3 committed Jan 30, 2024
2 parents 4e11011 + 6c6e9cb commit 9ed56e5
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

- Advanced comparison (LIKE, IN, BETWEEN) operators to the Filter extension [#178](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/178)

### Changed

- Elasticsearch drivers from 7.17.9 to 8.11.0 [#169](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/169)
Expand All @@ -18,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Exclude unset fields in search response [#166](https://github.com/stac-utils/stac-fastapi-elasticsearch/issues/166)
- Upgrade stac-fastapi to v2.4.9 [#172](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/172)
- Set correct default filter-lang for GET /search requests [#179](https://github.com/stac-utils/stac-fastapi-elasticsearch/issues/179)

## [v1.0.0]

Expand Down
7 changes: 6 additions & 1 deletion stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
settings = ElasticsearchSettings()
session = Session.create_from_settings(settings)

filter_extension = FilterExtension(client=EsAsyncBaseFiltersClient())
filter_extension.conformance_classes.append(
"http://www.opengis.net/spec/cql2/1.0/conf/advanced-comparison-operators"
)

extensions = [
TransactionExtension(client=TransactionsClient(session=session), settings=settings),
BulkTransactionExtension(client=BulkTransactionsClient(session=session)),
Expand All @@ -32,7 +37,7 @@
SortExtension(),
TokenPaginationExtension(),
ContextExtension(),
FilterExtension(client=EsAsyncBaseFiltersClient()),
filter_extension,
]

post_request_model = create_post_request_model(extensions)
Expand Down
6 changes: 3 additions & 3 deletions stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,12 +380,12 @@ async def get_search(
base_args["sortby"] = sort_param

if filter:
if filter_lang == "cql2-text":
if filter_lang == "cql2-json":
base_args["filter-lang"] = "cql2-json"
base_args["filter"] = orjson.loads(to_cql2(parse_cql2_text(filter)))
base_args["filter"] = orjson.loads(unquote_plus(filter))
else:
base_args["filter-lang"] = "cql2-json"
base_args["filter"] = orjson.loads(unquote_plus(filter))
base_args["filter"] = orjson.loads(to_cql2(parse_cql2_text(filter)))

if fields:
includes = set()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
Basic CQL2 (AND, OR, NOT), comparison operators (=, <>, <, <=, >, >=), and IS NULL.
The comparison operators are allowed against string, numeric, boolean, date, and datetime types.
Advanced comparison operators (http://www.opengis.net/spec/cql2/1.0/req/advanced-comparison-operators)
defines the LIKE, IN, and BETWEEN operators.
Basic Spatial Operators (http://www.opengis.net/spec/cql2/1.0/conf/basic-spatial-operators)
defines the intersects operator (S_INTERSECTS).
"""
from __future__ import annotations

import datetime
import re
from enum import Enum
from typing import List, Union

Expand Down Expand Up @@ -78,6 +82,17 @@ def to_es(self):
)


class AdvancedComparisonOp(str, Enum):
"""Advanced Comparison operator.
CQL2 advanced comparison operators like (~), between, and in.
"""

like = "like"
between = "between"
_in = "in"


class SpatialIntersectsOp(str, Enum):
"""Spatial intersections operator s_intersects."""

Expand Down Expand Up @@ -152,8 +167,8 @@ def validate(cls, v):
class Clause(BaseModel):
"""Filter extension clause."""

op: Union[LogicalOp, ComparisonOp, SpatialIntersectsOp]
args: List[Arg]
op: Union[LogicalOp, ComparisonOp, AdvancedComparisonOp, SpatialIntersectsOp]
args: List[Union[Arg, List[Arg]]]

def to_es(self):
"""Generate an Elasticsearch expression for this Clause."""
Expand All @@ -171,6 +186,30 @@ def to_es(self):
"must_not": [{"term": {to_es(self.args[0]): to_es(self.args[1])}}]
}
}
elif self.op == AdvancedComparisonOp.like:
return {
"wildcard": {
to_es(self.args[0]): {
"value": cql2_like_to_es(str(to_es(self.args[1]))),
"case_insensitive": "false",
}
}
}
elif self.op == AdvancedComparisonOp.between:
return {
"range": {
to_es(self.args[0]): {
"gte": to_es(self.args[1]),
"lte": to_es(self.args[2]),
}
}
}
elif self.op == AdvancedComparisonOp._in:
if not isinstance(self.args[1], List):
raise RuntimeError(f"Arg {self.args[1]} is not a list")
return {
"terms": {to_es(self.args[0]): [to_es(arg) for arg in self.args[1]]}
}
elif (
self.op == ComparisonOp.lt
or self.op == ComparisonOp.lte
Expand Down Expand Up @@ -210,3 +249,19 @@ def to_es(arg: Arg):
return arg
else:
raise RuntimeError(f"unknown arg {repr(arg)}")


def cql2_like_to_es(string):
"""Convert wildcard characters in CQL2 ('_' and '%') to Elasticsearch wildcard characters ('?' and '*', respectively). Handle escape characters and pass through Elasticsearch wildcards."""
percent_pattern = r"(?<!\\)%"
underscore_pattern = r"(?<!\\)_"
escape_pattern = r"\\(?=[_%])"

for pattern in [
(percent_pattern, "*"),
(underscore_pattern, "?"),
(escape_pattern, ""),
]:
string = re.sub(pattern[0], pattern[1], string)

return string
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"op": "like",
"args": [
{
"property": "scene_id"
},
"LC82030282019133%"
]
}
10 changes: 10 additions & 0 deletions stac_fastapi/elasticsearch/tests/extensions/cql2/example20.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"op": "like",
"args": [
{
"property": "scene_id"
},
"LC82030282019133LGN0_"
]
}

33 changes: 33 additions & 0 deletions stac_fastapi/elasticsearch/tests/extensions/cql2/example21.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"op": "and",
"args": [
{
"op": "between",
"args": [
{
"property": "cloud_cover"
},
0.1,
0.2
]
},
{
"op": "=",
"args": [
{
"property": "landsat:wrs_row"
},
28
]
},
{
"op": "=",
"args": [
{
"property": "landsat:wrs_path"
},
203
]
}
]
}
13 changes: 13 additions & 0 deletions stac_fastapi/elasticsearch/tests/extensions/cql2/example22.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"op": "and",
"args": [
{
"op": "in",
"args": [
{"property": "id"},
["LC08_L1TP_060247_20180905_20180912_01_T1_L1TP"]
]
},
{"op": "=", "args": [{"property": "collection"}, "landsat8_l1tp"]}
]
}
Loading

0 comments on commit 9ed56e5

Please sign in to comment.