Skip to content

Commit 22c10f7

Browse files
Extending temporal search (#182)
**Related Issue(s):** - #181 **Description:** Extending temporal search include start_datetime and end_datetime properties **PR Checklist:** - [x] Code is formatted and linted (run `pre-commit run --all-files`) - [x] Tests pass (run `make test`) - [x] Documentation has been updated to reflect changes, if applicable - [x] Changes are added to the changelog --------- Co-authored-by: Jonathan Healy <[email protected]>
1 parent 53a9d7e commit 22c10f7

File tree

7 files changed

+916
-541
lines changed

7 files changed

+916
-541
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
### Changed
1313

14+
- Extended Datetime Search to search on start_datetime and end_datetime as well as datetime fields. [#182](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/182)
15+
1416
### Fixed
1517

1618
## [v4.0.0] - 2025-04-23

stac_fastapi/core/stac_fastapi/core/core.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ async def update_item(
738738
"""
739739
item = item.model_dump(mode="json")
740740
base_url = str(kwargs["request"].base_url)
741-
now = datetime_type.now(timezone.utc).isoformat().replace("+00:00", "Z")
741+
now = datetime_type.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
742742
item["properties"]["updated"] = now
743743

744744
await self.database.check_collection_exists(collection_id)

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py

+101-9
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ def apply_collections_filter(search: Search, collection_ids: List[str]):
294294
return search.filter("terms", collection=collection_ids)
295295

296296
@staticmethod
297-
def apply_datetime_filter(search: Search, datetime_search):
298-
"""Apply a filter to search based on datetime field.
297+
def apply_datetime_filter(search: Search, datetime_search: dict):
298+
"""Apply a filter to search on datetime, start_datetime, and end_datetime fields.
299299
300300
Args:
301301
search (Search): The search object to filter.
@@ -304,17 +304,109 @@ def apply_datetime_filter(search: Search, datetime_search):
304304
Returns:
305305
Search: The filtered search object.
306306
"""
307+
should = []
308+
309+
# If the request is a single datetime return
310+
# items with datetimes equal to the requested datetime OR
311+
# the requested datetime is between their start and end datetimes
307312
if "eq" in datetime_search:
308-
search = search.filter(
309-
"term", **{"properties__datetime": datetime_search["eq"]}
313+
should.extend(
314+
[
315+
Q(
316+
"bool",
317+
filter=[
318+
Q(
319+
"term",
320+
properties__datetime=datetime_search["eq"],
321+
),
322+
],
323+
),
324+
Q(
325+
"bool",
326+
filter=[
327+
Q(
328+
"range",
329+
properties__start_datetime={
330+
"lte": datetime_search["eq"],
331+
},
332+
),
333+
Q(
334+
"range",
335+
properties__end_datetime={
336+
"gte": datetime_search["eq"],
337+
},
338+
),
339+
],
340+
),
341+
]
310342
)
343+
344+
# If the request is a date range return
345+
# items with datetimes within the requested date range OR
346+
# their startdatetime ithin the requested date range OR
347+
# their enddatetime ithin the requested date range OR
348+
# the requested daterange within their start and end datetimes
311349
else:
312-
search = search.filter(
313-
"range", properties__datetime={"lte": datetime_search["lte"]}
314-
)
315-
search = search.filter(
316-
"range", properties__datetime={"gte": datetime_search["gte"]}
350+
should.extend(
351+
[
352+
Q(
353+
"bool",
354+
filter=[
355+
Q(
356+
"range",
357+
properties__datetime={
358+
"gte": datetime_search["gte"],
359+
"lte": datetime_search["lte"],
360+
},
361+
),
362+
],
363+
),
364+
Q(
365+
"bool",
366+
filter=[
367+
Q(
368+
"range",
369+
properties__start_datetime={
370+
"gte": datetime_search["gte"],
371+
"lte": datetime_search["lte"],
372+
},
373+
),
374+
],
375+
),
376+
Q(
377+
"bool",
378+
filter=[
379+
Q(
380+
"range",
381+
properties__end_datetime={
382+
"gte": datetime_search["gte"],
383+
"lte": datetime_search["lte"],
384+
},
385+
),
386+
],
387+
),
388+
Q(
389+
"bool",
390+
filter=[
391+
Q(
392+
"range",
393+
properties__start_datetime={
394+
"lte": datetime_search["gte"]
395+
},
396+
),
397+
Q(
398+
"range",
399+
properties__end_datetime={
400+
"gte": datetime_search["lte"]
401+
},
402+
),
403+
],
404+
),
405+
]
317406
)
407+
408+
search = search.query(Q("bool", filter=[Q("bool", should=should)]))
409+
318410
return search
319411

320412
@staticmethod

stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py

+100-8
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]
329329

330330
@staticmethod
331331
def apply_datetime_filter(search: Search, datetime_search):
332-
"""Apply a filter to search based on datetime field.
332+
"""Apply a filter to search based on datetime field, start_datetime, and end_datetime fields.
333333
334334
Args:
335335
search (Search): The search object to filter.
@@ -338,17 +338,109 @@ def apply_datetime_filter(search: Search, datetime_search):
338338
Returns:
339339
Search: The filtered search object.
340340
"""
341+
should = []
342+
343+
# If the request is a single datetime return
344+
# items with datetimes equal to the requested datetime OR
345+
# the requested datetime is between their start and end datetimes
341346
if "eq" in datetime_search:
342-
search = search.filter(
343-
"term", **{"properties__datetime": datetime_search["eq"]}
347+
should.extend(
348+
[
349+
Q(
350+
"bool",
351+
filter=[
352+
Q(
353+
"term",
354+
properties__datetime=datetime_search["eq"],
355+
),
356+
],
357+
),
358+
Q(
359+
"bool",
360+
filter=[
361+
Q(
362+
"range",
363+
properties__start_datetime={
364+
"lte": datetime_search["eq"],
365+
},
366+
),
367+
Q(
368+
"range",
369+
properties__end_datetime={
370+
"gte": datetime_search["eq"],
371+
},
372+
),
373+
],
374+
),
375+
]
344376
)
377+
378+
# If the request is a date range return
379+
# items with datetimes within the requested date range OR
380+
# their startdatetime ithin the requested date range OR
381+
# their enddatetime ithin the requested date range OR
382+
# the requested daterange within their start and end datetimes
345383
else:
346-
search = search.filter(
347-
"range", properties__datetime={"lte": datetime_search["lte"]}
348-
)
349-
search = search.filter(
350-
"range", properties__datetime={"gte": datetime_search["gte"]}
384+
should.extend(
385+
[
386+
Q(
387+
"bool",
388+
filter=[
389+
Q(
390+
"range",
391+
properties__datetime={
392+
"gte": datetime_search["gte"],
393+
"lte": datetime_search["lte"],
394+
},
395+
),
396+
],
397+
),
398+
Q(
399+
"bool",
400+
filter=[
401+
Q(
402+
"range",
403+
properties__start_datetime={
404+
"gte": datetime_search["gte"],
405+
"lte": datetime_search["lte"],
406+
},
407+
),
408+
],
409+
),
410+
Q(
411+
"bool",
412+
filter=[
413+
Q(
414+
"range",
415+
properties__end_datetime={
416+
"gte": datetime_search["gte"],
417+
"lte": datetime_search["lte"],
418+
},
419+
),
420+
],
421+
),
422+
Q(
423+
"bool",
424+
filter=[
425+
Q(
426+
"range",
427+
properties__start_datetime={
428+
"lte": datetime_search["gte"]
429+
},
430+
),
431+
Q(
432+
"range",
433+
properties__end_datetime={
434+
"gte": datetime_search["lte"]
435+
},
436+
),
437+
],
438+
),
439+
]
351440
)
441+
442+
search = search.query(Q("bool", filter=[Q("bool", should=should)]))
443+
352444
return search
353445

354446
@staticmethod

0 commit comments

Comments
 (0)