Skip to content

Commit d1dca83

Browse files
committed
before tests
1 parent 2d49243 commit d1dca83

File tree

9 files changed

+408
-176
lines changed

9 files changed

+408
-176
lines changed

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ def apply_collections_filter(search: Search, collection_ids: List[str]):
436436
@staticmethod
437437
def apply_datetime_filter(
438438
search: Search, datetime: Optional[str]
439-
) -> Tuple[Search, Dict[str, Optional[str]]]:
439+
) -> Tuple[Search, Dict[str, Dict[str, Optional[str]]]]:
440440
"""Apply a filter to search on datetime, start_datetime, and end_datetime fields.
441441
442442
Args:
@@ -446,15 +446,30 @@ def apply_datetime_filter(
446446
Returns:
447447
The filtered search object.
448448
"""
449+
datetime_search = return_date(datetime)
450+
449451
# USE_DATETIME env var
450452
# True: Search by datetime, if null search by start/end datetime
451453
# False: Always search only by start/end datetime
452454
USE_DATETIME = get_bool_env("USE_DATETIME", default=True)
453455

454-
datetime_search = return_date(datetime)
456+
result_metadata = {
457+
"datetime": {
458+
"gte": datetime_search.get("gte") if USE_DATETIME else None,
459+
"lte": datetime_search.get("lte") if USE_DATETIME else None,
460+
},
461+
"start_datetime": {
462+
"gte": datetime_search.get("gte") if not USE_DATETIME else None,
463+
"lte": None,
464+
},
465+
"end_datetime": {
466+
"gte": None,
467+
"lte": datetime_search.get("lte") if not USE_DATETIME else None,
468+
},
469+
}
455470

456471
if not datetime_search:
457-
return search, datetime_search
472+
return search, result_metadata
458473

459474
if USE_DATETIME:
460475
if "eq" in datetime_search:
@@ -533,7 +548,7 @@ def apply_datetime_filter(
533548

534549
return (
535550
search.query(Q("bool", should=should, minimum_should_match=1)),
536-
datetime_search,
551+
result_metadata,
537552
)
538553
else:
539554
if "eq" in datetime_search:
@@ -568,7 +583,7 @@ def apply_datetime_filter(
568583
),
569584
],
570585
)
571-
return search.query(filter_query), datetime_search
586+
return search.query(filter_query), result_metadata
572587

573588
@staticmethod
574589
def apply_bbox_filter(search: Search, bbox: List):

stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]
459459
@staticmethod
460460
def apply_datetime_filter(
461461
search: Search, datetime: Optional[str]
462-
) -> Tuple[Search, Dict[str, Optional[str]]]:
462+
) -> Tuple[Search, Dict[str, Dict[str, Optional[str]]]]:
463463
"""Apply a filter to search on datetime, start_datetime, and end_datetime fields.
464464
465465
Args:
@@ -471,14 +471,30 @@ def apply_datetime_filter(
471471
"""
472472
datetime_search = return_date(datetime)
473473

474-
if not datetime_search:
475-
return search, datetime_search
476-
477474
# USE_DATETIME env var
478475
# True: Search by datetime, if null search by start/end datetime
479476
# False: Always search only by start/end datetime
480477
USE_DATETIME = get_bool_env("USE_DATETIME", default=True)
481478

479+
result_metadata = {
480+
"datetime": {
481+
"gte": datetime_search.get("gte") if USE_DATETIME else None,
482+
"lte": datetime_search.get("lte") if USE_DATETIME else None,
483+
},
484+
"start_datetime": {
485+
"gte": datetime_search.get("gte") if not USE_DATETIME else None,
486+
"lte": None,
487+
},
488+
"end_datetime": {
489+
"gte": None,
490+
"lte": datetime_search.get("lte") if not USE_DATETIME else None,
491+
},
492+
}
493+
494+
if not datetime_search:
495+
return search, result_metadata
496+
497+
482498
if USE_DATETIME:
483499
if "eq" in datetime_search:
484500
# For exact matches, include:
@@ -556,7 +572,7 @@ def apply_datetime_filter(
556572

557573
return (
558574
search.query(Q("bool", should=should, minimum_should_match=1)),
559-
datetime_search,
575+
result_metadata,
560576
)
561577
else:
562578
if "eq" in datetime_search:
@@ -591,7 +607,7 @@ def apply_datetime_filter(
591607
),
592608
],
593609
)
594-
return search.query(filter_query), datetime_search
610+
return search.query(filter_query), result_metadata
595611

596612
@staticmethod
597613
def apply_bbox_filter(search: Search, bbox: List):

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/datetime.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,18 @@ def return_date(
138138
return result
139139

140140

141-
def extract_date(date_str: str) -> date:
141+
def extract_date(date_str: str | None) -> date | None:
142142
"""Extract date from ISO format string.
143143
144144
Args:
145145
date_str: ISO format date string
146146
147147
Returns:
148-
A date object extracted from the input string.
148+
A date object extracted from the input string or None.
149149
"""
150+
if not date_str:
151+
return None
152+
150153
date_str = date_str.replace("Z", "+00:00")
151154
return datetime_type.fromisoformat(date_str).date()
152155

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/index.py

Lines changed: 58 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77
from datetime import datetime
88
from functools import lru_cache
9-
from typing import Any, List, Optional
9+
from typing import Any, List, Optional, Tuple, Dict
1010

1111
from dateutil.parser import parse # type: ignore[import]
1212

@@ -71,54 +71,69 @@ def indices(collection_ids: Optional[List[str]]) -> str:
7171

7272

7373
def filter_indexes_by_datetime(
74-
indexes: List[str], gte: Optional[str], lte: Optional[str]
74+
collection_indexes: List[Tuple[Dict[str, str], ...]],
75+
datetime_search: Dict[str, Dict[str, str | None]]
7576
) -> List[str]:
76-
"""Filter indexes based on datetime range extracted from index names.
77+
def extract_date_from_alias(alias: str) -> tuple[datetime, datetime] | None:
78+
date_pattern = re.compile(r'\d{4}-\d{2}-\d{2}')
79+
try:
80+
dates = date_pattern.findall(alias)
81+
82+
if not dates:
83+
return None
84+
85+
if len(dates) >= 2:
86+
return datetime.strptime(dates[-2], '%Y-%m-%d'), datetime.strptime(dates[-1], '%Y-%m-%d')
87+
else:
88+
date = datetime.strptime(dates[-1], '%Y-%m-%d')
89+
return date, date
90+
except (ValueError, IndexError):
91+
return None
92+
93+
def parse_search_date(date_str: str | None) -> Optional[datetime.date]:
94+
if not date_str:
95+
return None
96+
return datetime.fromisoformat(date_str.replace('Z', '+00:00')).date()
97+
98+
def check_criteria(value_begin: datetime.date, value_end: datetime.date, criteria: Dict) -> bool:
99+
gte = parse_search_date(criteria.get('gte'))
100+
lte = parse_search_date(criteria.get('lte'))
101+
102+
if gte and value_begin.date() < gte:
103+
return False
104+
if lte and value_end.date() > lte:
105+
return False
106+
return True
77107

78-
Args:
79-
indexes: List of index names containing dates
80-
gte: Greater than or equal date filter (ISO format, optional 'Z' suffix)
81-
lte: Less than or equal date filter (ISO format, optional 'Z' suffix)
108+
filtered_indexes = []
82109

83-
Returns:
84-
List of filtered index names
85-
"""
110+
for index_tuple in collection_indexes:
111+
if not index_tuple:
112+
continue
86113

87-
def parse_datetime(dt_str: str) -> datetime:
88-
"""Parse datetime string, handling both with and without 'Z' suffix."""
89-
return parse(dt_str).replace(tzinfo=None)
90-
91-
def extract_date_range_from_index(index_name: str) -> tuple:
92-
"""Extract start and end dates from index name."""
93-
date_pattern = r"(\d{4}-\d{2}-\d{2})"
94-
dates = re.findall(date_pattern, index_name)
95-
96-
if len(dates) == 1:
97-
start_date = datetime.strptime(dates[0], "%Y-%m-%d")
98-
max_date = datetime.max.replace(microsecond=0)
99-
return start_date, max_date
100-
else:
101-
start_date = datetime.strptime(dates[0], "%Y-%m-%d")
102-
end_date = datetime.strptime(dates[1], "%Y-%m-%d")
103-
return start_date, end_date
104-
105-
def is_index_in_range(
106-
start_date: datetime, end_date: datetime, gte_dt: datetime, lte_dt: datetime
107-
) -> bool:
108-
"""Check if index date range overlaps with filter range."""
109-
return not (
110-
end_date.date() < gte_dt.date() or start_date.date() > lte_dt.date()
111-
)
112-
113-
gte_dt = parse_datetime(gte) if gte else datetime.min.replace(microsecond=0)
114-
lte_dt = parse_datetime(lte) if lte else datetime.max.replace(microsecond=0)
114+
index_dict = index_tuple[0]
115+
start_datetime_alias = index_dict.get('start_datetime')
116+
end_datetime_alias = index_dict.get('end_datetime')
117+
datetime_alias = index_dict.get('datetime')
115118

116-
filtered_indexes = []
119+
if not start_datetime_alias:
120+
continue
121+
122+
start_range = extract_date_from_alias(start_datetime_alias)
123+
end_date = extract_date_from_alias(end_datetime_alias)
124+
datetime_date = extract_date_from_alias(datetime_alias)
125+
126+
if not start_range or not end_date or not datetime_date:
127+
continue
128+
129+
if not check_criteria(start_range[0], start_range[1], datetime_search.get('start_datetime', {})):
130+
continue
131+
if not check_criteria(end_date[0], end_date[1], datetime_search.get('end_datetime', {})):
132+
continue
133+
if not check_criteria(datetime_date[0], datetime_date[1], datetime_search.get('datetime', {})):
134+
continue
117135

118-
for index in indexes:
119-
start_date, end_date = extract_date_range_from_index(index)
120-
if is_index_in_range(start_date, end_date, gte_dt, lte_dt):
121-
filtered_indexes.append(index)
136+
filtered_indexes.append(start_datetime_alias)
122137

123138
return filtered_indexes
124139

0 commit comments

Comments
 (0)