Skip to content

Commit ca1c8f0

Browse files
Yuri ZmytrakovYuri Zmytrakov
authored andcommitted
dummy
1 parent 56af26d commit ca1c8f0

File tree

4 files changed

+217
-114
lines changed

4 files changed

+217
-114
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,3 +634,4 @@ The system uses a precise naming convention:
634634
- Ensures fair resource allocation among all clients
635635
636636
- **Examples**: Implementation examples are available in the [examples/rate_limit](examples/rate_limit) directory.
637+

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py

Lines changed: 83 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from stac_fastapi.core.base_database_logic import BaseDatabaseLogic
1919
from stac_fastapi.core.serializers import CollectionSerializer, ItemSerializer
20-
from stac_fastapi.core.utilities import bbox2polygon, get_max_limit
20+
from stac_fastapi.core.utilities import bbox2polygon, get_bool_env, get_max_limit
2121
from stac_fastapi.elasticsearch.config import AsyncElasticsearchSettings
2222
from stac_fastapi.elasticsearch.config import (
2323
ElasticsearchSettings as SyncElasticsearchSettings,
@@ -289,80 +289,93 @@ def apply_datetime_filter(
289289
Returns:
290290
The filtered search object.
291291
"""
292+
USE_DATETIME = get_bool_env("USE_DATETIME", default=True)
293+
292294
datetime_search = return_date(datetime)
293295

294296
if not datetime_search:
295297
return search, datetime_search
296298

297-
if "eq" in datetime_search:
298-
# For exact matches, include:
299-
# 1. Items with matching exact datetime
300-
# 2. Items with datetime:null where the time falls within their range
301-
should = [
302-
Q(
303-
"bool",
304-
filter=[
305-
Q("exists", field="properties.datetime"),
306-
Q("term", **{"properties__datetime": datetime_search["eq"]}),
307-
],
308-
),
309-
Q(
310-
"bool",
311-
must_not=[Q("exists", field="properties.datetime")],
312-
filter=[
313-
Q("exists", field="properties.start_datetime"),
314-
Q("exists", field="properties.end_datetime"),
315-
Q(
316-
"range",
317-
properties__start_datetime={"lte": datetime_search["eq"]},
318-
),
319-
Q(
320-
"range",
321-
properties__end_datetime={"gte": datetime_search["eq"]},
322-
),
323-
],
324-
),
325-
]
326-
else:
327-
# For date ranges, include:
328-
# 1. Items with datetime in the range
329-
# 2. Items with datetime:null that overlap the search range
330-
should = [
331-
Q(
332-
"bool",
333-
filter=[
334-
Q("exists", field="properties.datetime"),
335-
Q(
336-
"range",
337-
properties__datetime={
338-
"gte": datetime_search["gte"],
339-
"lte": datetime_search["lte"],
340-
},
341-
),
342-
],
343-
),
344-
Q(
345-
"bool",
346-
must_not=[Q("exists", field="properties.datetime")],
347-
filter=[
348-
Q("exists", field="properties.start_datetime"),
349-
Q("exists", field="properties.end_datetime"),
350-
Q(
351-
"range",
352-
properties__start_datetime={"lte": datetime_search["lte"]},
353-
),
354-
Q(
355-
"range",
356-
properties__end_datetime={"gte": datetime_search["gte"]},
357-
),
358-
],
359-
),
360-
]
361-
362-
return (
363-
search.query(Q("bool", should=should, minimum_should_match=1)),
364-
datetime_search,
365-
)
299+
if USE_DATETIME:
300+
if "eq" in datetime_search:
301+
# For exact matches, include:
302+
# 1. Items with matching exact datetime
303+
# 2. Items with datetime:null where the time falls within their range
304+
should = [
305+
Q(
306+
"bool",
307+
filter=[
308+
Q("exists", field="properties.datetime"),
309+
Q(
310+
"term",
311+
**{"properties__datetime": datetime_search["eq"]},
312+
),
313+
],
314+
),
315+
Q(
316+
"bool",
317+
must_not=[Q("exists", field="properties.datetime")],
318+
filter=[
319+
Q("exists", field="properties.start_datetime"),
320+
Q("exists", field="properties.end_datetime"),
321+
Q(
322+
"range",
323+
properties__start_datetime={
324+
"lte": datetime_search["eq"]
325+
},
326+
),
327+
Q(
328+
"range",
329+
properties__end_datetime={"gte": datetime_search["eq"]},
330+
),
331+
],
332+
),
333+
]
334+
else:
335+
# For date ranges, include:
336+
# 1. Items with datetime in the range
337+
# 2. Items with datetime:null that overlap the search range
338+
should = [
339+
Q(
340+
"bool",
341+
filter=[
342+
Q("exists", field="properties.datetime"),
343+
Q(
344+
"range",
345+
properties__datetime={
346+
"gte": datetime_search["gte"],
347+
"lte": datetime_search["lte"],
348+
},
349+
),
350+
],
351+
),
352+
Q(
353+
"bool",
354+
must_not=[Q("exists", field="properties.datetime")],
355+
filter=[
356+
Q("exists", field="properties.start_datetime"),
357+
Q("exists", field="properties.end_datetime"),
358+
Q(
359+
"range",
360+
properties__start_datetime={
361+
"lte": datetime_search["lte"]
362+
},
363+
),
364+
Q(
365+
"range",
366+
properties__end_datetime={
367+
"gte": datetime_search["gte"]
368+
},
369+
),
370+
],
371+
),
372+
]
373+
374+
return (
375+
search.query(Q("bool", should=should, minimum_should_match=1)),
376+
datetime_search,
377+
)
378+
return search, datetime_search
366379

367380
@staticmethod
368381
def apply_bbox_filter(search: Search, bbox: List):

stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py

Lines changed: 93 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from stac_fastapi.core.base_database_logic import BaseDatabaseLogic
1818
from stac_fastapi.core.serializers import CollectionSerializer, ItemSerializer
19-
from stac_fastapi.core.utilities import bbox2polygon, get_max_limit
19+
from stac_fastapi.core.utilities import bbox2polygon, get_bool_env, get_max_limit
2020
from stac_fastapi.extensions.core.transaction.request import (
2121
PartialCollection,
2222
PartialItem,
@@ -301,21 +301,94 @@ def apply_datetime_filter(
301301
if not datetime_search:
302302
return search, datetime_search
303303

304-
if "eq" in datetime_search:
305-
# For exact matches, include:
306-
# 1. Items with matching exact datetime
307-
# 2. Items with datetime:null where the time falls within their range
308-
should = [
309-
Q(
310-
"bool",
311-
filter=[
312-
Q("exists", field="properties.datetime"),
313-
Q("term", **{"properties__datetime": datetime_search["eq"]}),
314-
],
315-
),
316-
Q(
304+
# USE_DATETIME env var
305+
# True: Search by datetime, if null search by start/end datetime
306+
# False: Always search only by start/end datetime
307+
USE_DATETIME = get_bool_env("USE_DATETIME", default=True)
308+
309+
if USE_DATETIME:
310+
if "eq" in datetime_search:
311+
# For exact matches, include:
312+
# 1. Items with matching exact datetime
313+
# 2. Items with datetime:null where the time falls within their range
314+
should = [
315+
Q(
316+
"bool",
317+
filter=[
318+
Q("exists", field="properties.datetime"),
319+
Q(
320+
"term",
321+
**{"properties__datetime": datetime_search["eq"]},
322+
),
323+
],
324+
),
325+
Q(
326+
"bool",
327+
must_not=[Q("exists", field="properties.datetime")],
328+
filter=[
329+
Q("exists", field="properties.start_datetime"),
330+
Q("exists", field="properties.end_datetime"),
331+
Q(
332+
"range",
333+
properties__start_datetime={
334+
"lte": datetime_search["eq"]
335+
},
336+
),
337+
Q(
338+
"range",
339+
properties__end_datetime={"gte": datetime_search["eq"]},
340+
),
341+
],
342+
),
343+
]
344+
else:
345+
# For date ranges, include:
346+
# 1. Items with datetime in the range
347+
# 2. Items with datetime:null that overlap the search range
348+
should = [
349+
Q(
350+
"bool",
351+
filter=[
352+
Q("exists", field="properties.datetime"),
353+
Q(
354+
"range",
355+
properties__datetime={
356+
"gte": datetime_search["gte"],
357+
"lte": datetime_search["lte"],
358+
},
359+
),
360+
],
361+
),
362+
Q(
363+
"bool",
364+
must_not=[Q("exists", field="properties.datetime")],
365+
filter=[
366+
Q("exists", field="properties.start_datetime"),
367+
Q("exists", field="properties.end_datetime"),
368+
Q(
369+
"range",
370+
properties__start_datetime={
371+
"lte": datetime_search["lte"]
372+
},
373+
),
374+
Q(
375+
"range",
376+
properties__end_datetime={
377+
"gte": datetime_search["gte"]
378+
},
379+
),
380+
],
381+
),
382+
]
383+
384+
return (
385+
search.query(Q("bool", should=should, minimum_should_match=1)),
386+
datetime_search,
387+
)
388+
else:
389+
if "eq" in datetime_search:
390+
filter_query = Q(
317391
"bool",
318-
must_not=[Q("exists", field="properties.datetime")],
319392
filter=[
320393
Q("exists", field="properties.start_datetime"),
321394
Q("exists", field="properties.end_datetime"),
@@ -328,29 +401,10 @@ def apply_datetime_filter(
328401
properties__end_datetime={"gte": datetime_search["eq"]},
329402
),
330403
],
331-
),
332-
]
333-
else:
334-
# For date ranges, include:
335-
# 1. Items with datetime in the range
336-
# 2. Items with datetime:null that overlap the search range
337-
should = [
338-
Q(
339-
"bool",
340-
filter=[
341-
Q("exists", field="properties.datetime"),
342-
Q(
343-
"range",
344-
properties__datetime={
345-
"gte": datetime_search["gte"],
346-
"lte": datetime_search["lte"],
347-
},
348-
),
349-
],
350-
),
351-
Q(
404+
)
405+
else:
406+
filter_query = Q(
352407
"bool",
353-
must_not=[Q("exists", field="properties.datetime")],
354408
filter=[
355409
Q("exists", field="properties.start_datetime"),
356410
Q("exists", field="properties.end_datetime"),
@@ -363,13 +417,8 @@ def apply_datetime_filter(
363417
properties__end_datetime={"gte": datetime_search["gte"]},
364418
),
365419
],
366-
),
367-
]
368-
369-
return (
370-
search.query(Q("bool", should=should, minimum_should_match=1)),
371-
datetime_search,
372-
)
420+
)
421+
return search.query(filter_query), datetime_search
373422

374423
@staticmethod
375424
def apply_bbox_filter(search: Search, bbox: List):

stac_fastapi/tests/api/test_api.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,3 +1537,43 @@ async def test_search_max_item_limit(
15371537
assert resp.status_code == 200
15381538
resp_json = resp.json()
15391539
assert int(limit) == len(resp_json["features"])
1540+
1541+
1542+
@pytest.mark.asyncio
1543+
async def test_use_datetime_true(app_client, load_test_data, txn_client, monkeypatch):
1544+
monkeypatch.setenv("USE_DATETIME", "true")
1545+
1546+
test_collection = load_test_data("test_collection.json")
1547+
test_collection["id"] = "test-collection-datetime-true"
1548+
await create_collection(txn_client, test_collection)
1549+
1550+
item = load_test_data("test_item.json")
1551+
1552+
item1 = item.copy()
1553+
item1["id"] = "test-item-datetime"
1554+
item1["collection"] = test_collection["id"]
1555+
item1["properties"]["datetime"] = "2020-01-01T12:00:00Z"
1556+
await create_item(txn_client, item1)
1557+
1558+
item2 = item.copy()
1559+
item2["id"] = "test-item-start-end"
1560+
item2["collection"] = test_collection["id"]
1561+
item1["properties"]["datetime"] = None
1562+
item2["properties"]["start_datetime"] = "2020-01-01T10:00:00Z"
1563+
item2["properties"]["end_datetime"] = "2020-01-01T13:00:00Z"
1564+
await create_item(txn_client, item2)
1565+
1566+
resp = await app_client.post(
1567+
"/search",
1568+
json={
1569+
"datetime": "2020-01-01T12:00:00Z",
1570+
"collections": [test_collection["id"]],
1571+
},
1572+
)
1573+
1574+
assert resp.status_code == 200
1575+
resp_json = resp.json()
1576+
1577+
found_ids = {feature["id"] for feature in resp_json["features"]}
1578+
assert "test-item-datetime" in found_ids
1579+
assert "test-item-start-end" in found_ids

0 commit comments

Comments
 (0)