Skip to content

Commit 3dd27aa

Browse files
committed
Merge branch 'main' of github.com:stac-utils/stac-fastapi-elasticsearch into patch_endpoints
2 parents 66ea717 + ae81078 commit 3dd27aa

File tree

4 files changed

+349
-245
lines changed

4 files changed

+349
-245
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1414

1515
### Changed
1616

17+
- Improved datetime query handling to only check start and end datetime values when datetime is None [#396](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/396)
1718
- Optimize data_loader.py script [#395](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/395)
1819
- Refactored test configuration to use shared app config pattern [#399](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/399)
1920

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py

Lines changed: 80 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -259,121 +259,97 @@ def apply_collections_filter(search: Search, collection_ids: List[str]):
259259
@staticmethod
260260
def apply_datetime_filter(
261261
search: Search, interval: Optional[Union[DateTimeType, str]]
262-
):
262+
) -> Search:
263263
"""Apply a filter to search on datetime, start_datetime, and end_datetime fields.
264264
265265
Args:
266-
search (Search): The search object to filter.
267-
interval: Optional[Union[DateTimeType, str]]
266+
search: The search object to filter.
267+
interval: Optional datetime interval to filter by. Can be:
268+
- A single datetime string (e.g., "2023-01-01T12:00:00")
269+
- A datetime range string (e.g., "2023-01-01/2023-12-31")
270+
- A datetime object
271+
- A tuple of (start_datetime, end_datetime)
268272
269273
Returns:
270-
Search: The filtered search object.
274+
The filtered search object.
271275
"""
276+
if not interval:
277+
return search
278+
272279
should = []
273-
datetime_search = return_date(interval)
280+
try:
281+
datetime_search = return_date(interval)
282+
except (ValueError, TypeError) as e:
283+
# Handle invalid interval formats if return_date fails
284+
logger.error(f"Invalid interval format: {interval}, error: {e}")
285+
return search
274286

275-
# If the request is a single datetime return
276-
# items with datetimes equal to the requested datetime OR
277-
# the requested datetime is between their start and end datetimes
278287
if "eq" in datetime_search:
279-
should.extend(
280-
[
281-
Q(
282-
"bool",
283-
filter=[
284-
Q(
285-
"term",
286-
properties__datetime=datetime_search["eq"],
287-
),
288-
],
289-
),
290-
Q(
291-
"bool",
292-
filter=[
293-
Q(
294-
"range",
295-
properties__start_datetime={
296-
"lte": datetime_search["eq"],
297-
},
298-
),
299-
Q(
300-
"range",
301-
properties__end_datetime={
302-
"gte": datetime_search["eq"],
303-
},
304-
),
305-
],
306-
),
307-
]
308-
)
309-
310-
# If the request is a date range return
311-
# items with datetimes within the requested date range OR
312-
# their startdatetime ithin the requested date range OR
313-
# their enddatetime ithin the requested date range OR
314-
# the requested daterange within their start and end datetimes
288+
# For exact matches, include:
289+
# 1. Items with matching exact datetime
290+
# 2. Items with datetime:null where the time falls within their range
291+
should = [
292+
Q(
293+
"bool",
294+
filter=[
295+
Q("exists", field="properties.datetime"),
296+
Q("term", **{"properties__datetime": datetime_search["eq"]}),
297+
],
298+
),
299+
Q(
300+
"bool",
301+
must_not=[Q("exists", field="properties.datetime")],
302+
filter=[
303+
Q("exists", field="properties.start_datetime"),
304+
Q("exists", field="properties.end_datetime"),
305+
Q(
306+
"range",
307+
properties__start_datetime={"lte": datetime_search["eq"]},
308+
),
309+
Q(
310+
"range",
311+
properties__end_datetime={"gte": datetime_search["eq"]},
312+
),
313+
],
314+
),
315+
]
315316
else:
316-
should.extend(
317-
[
318-
Q(
319-
"bool",
320-
filter=[
321-
Q(
322-
"range",
323-
properties__datetime={
324-
"gte": datetime_search["gte"],
325-
"lte": datetime_search["lte"],
326-
},
327-
),
328-
],
329-
),
330-
Q(
331-
"bool",
332-
filter=[
333-
Q(
334-
"range",
335-
properties__start_datetime={
336-
"gte": datetime_search["gte"],
337-
"lte": datetime_search["lte"],
338-
},
339-
),
340-
],
341-
),
342-
Q(
343-
"bool",
344-
filter=[
345-
Q(
346-
"range",
347-
properties__end_datetime={
348-
"gte": datetime_search["gte"],
349-
"lte": datetime_search["lte"],
350-
},
351-
),
352-
],
353-
),
354-
Q(
355-
"bool",
356-
filter=[
357-
Q(
358-
"range",
359-
properties__start_datetime={
360-
"lte": datetime_search["gte"]
361-
},
362-
),
363-
Q(
364-
"range",
365-
properties__end_datetime={
366-
"gte": datetime_search["lte"]
367-
},
368-
),
369-
],
370-
),
371-
]
372-
)
373-
374-
search = search.query(Q("bool", filter=[Q("bool", should=should)]))
317+
# For date ranges, include:
318+
# 1. Items with datetime in the range
319+
# 2. Items with datetime:null that overlap the search range
320+
should = [
321+
Q(
322+
"bool",
323+
filter=[
324+
Q("exists", field="properties.datetime"),
325+
Q(
326+
"range",
327+
properties__datetime={
328+
"gte": datetime_search["gte"],
329+
"lte": datetime_search["lte"],
330+
},
331+
),
332+
],
333+
),
334+
Q(
335+
"bool",
336+
must_not=[Q("exists", field="properties.datetime")],
337+
filter=[
338+
Q("exists", field="properties.start_datetime"),
339+
Q("exists", field="properties.end_datetime"),
340+
Q(
341+
"range",
342+
properties__start_datetime={"lte": datetime_search["lte"]},
343+
),
344+
Q(
345+
"range",
346+
properties__end_datetime={"gte": datetime_search["gte"]},
347+
),
348+
],
349+
),
350+
]
375351

376-
return search
352+
return search.query(Q("bool", should=should, minimum_should_match=1))
377353

378354
@staticmethod
379355
def apply_bbox_filter(search: Search, bbox: List):

0 commit comments

Comments
 (0)