Skip to content

Commit 25ed91c

Browse files
committed
before tests
1 parent 2b8cb54 commit 25ed91c

File tree

7 files changed

+366
-165
lines changed

7 files changed

+366
-165
lines changed

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

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/search_engine/index_operations.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Search engine adapters for different implementations."""
22

33
import uuid
4-
from typing import Any, Dict
4+
from typing import Any, Dict, Literal, List
55

66
from stac_fastapi.sfeos_helpers.database import (
77
index_alias_by_collection_id,
@@ -39,26 +39,38 @@ async def create_simple_index(self, client: Any, collection_id: str) -> str:
3939
return index_name
4040

4141
async def create_datetime_index(
42-
self, client: Any, collection_id: str, start_date: str
42+
self, client: Any, collection_id: str, start_datetime: str, datetime: str | None, end_datetime: str
4343
) -> str:
4444
"""Create a datetime-based index for the given collection.
4545
4646
Args:
4747
client: Search engine client instance.
4848
collection_id (str): Collection identifier.
49-
start_date (str): Start date for the alias.
49+
start_datetime (str): Start datetime for the index alias.
50+
datetime (str | None): Datetime for the datetime alias (can be None).
51+
end_datetime (str): End datetime for the index alias.
5052
5153
Returns:
52-
str: Created index alias name.
54+
str: Created start_datetime alias name.
5355
"""
5456
index_name = self.create_index_name(collection_id)
55-
alias_name = self.create_alias_name(collection_id, start_date)
57+
alias_start_date = self.create_alias_name(collection_id, "start_datetime", start_datetime)
58+
alias_date = self.create_alias_name(collection_id, "datetime", datetime)
59+
alias_end_date = self.create_alias_name(collection_id, "end_datetime", end_datetime)
5660
collection_alias = index_alias_by_collection_id(collection_id)
61+
62+
aliases = {
63+
collection_alias: {},
64+
alias_start_date: {},
65+
alias_date: {},
66+
alias_end_date: {},
67+
}
68+
5769
await client.indices.create(
5870
index=index_name,
59-
body=self._create_index_body({collection_alias: {}, alias_name: {}}),
71+
body=self._create_index_body(aliases),
6072
)
61-
return alias_name
73+
return alias_start_date
6274

6375
@staticmethod
6476
async def update_index_alias(client: Any, end_date: str, old_alias: str) -> str:
@@ -84,23 +96,28 @@ async def update_index_alias(client: Any, end_date: str, old_alias: str) -> str:
8496
return new_alias
8597

8698
@staticmethod
87-
async def change_alias_name(client: Any, old_alias: str, new_alias: str) -> None:
88-
"""Change alias name from old to new.
99+
async def change_alias_name(client: Any, old_start_datetime_alias: str, aliases_to_change: List[str], aliases_to_create: List[str]) -> None:
100+
"""Change alias names by removing old aliases and adding new ones.
89101
90102
Args:
91103
client: Search engine client instance.
92-
old_alias (str): Current alias name.
93-
new_alias (str): New alias name.
104+
old_start_datetime_alias (str): Current start_datetime alias name to identify the index.
105+
aliases_to_change (List[str]): List of old alias names to remove.
106+
aliases_to_create (List[str]): List of new alias names to add.
94107
95108
Returns:
96109
None
97110
"""
98-
aliases_info = await client.indices.get_alias(name=old_alias)
111+
aliases_info = await client.indices.get_alias(name=old_start_datetime_alias)
112+
index_name = list(aliases_info.keys())[0]
99113
actions = []
100114

101-
for index_name in aliases_info.keys():
115+
for old_alias in aliases_to_change:
102116
actions.append({"remove": {"index": index_name, "alias": old_alias}})
117+
118+
for new_alias in aliases_to_create:
103119
actions.append({"add": {"index": index_name, "alias": new_alias}})
120+
104121
await client.indices.update_aliases(body={"actions": actions})
105122

106123
@staticmethod
@@ -117,18 +134,21 @@ def create_index_name(collection_id: str) -> str:
117134
return f"{ITEMS_INDEX_PREFIX}{cleaned.lower()}_{uuid.uuid4()}"
118135

119136
@staticmethod
120-
def create_alias_name(collection_id: str, start_date: str) -> str:
121-
"""Create index name from collection ID and uuid4.
137+
def create_alias_name(
138+
collection_id: str, name: Literal["start_datetime", "datetime", "end_datetime"], start_date: str
139+
) -> str:
140+
"""Create alias name from collection ID and date.
122141
123142
Args:
124143
collection_id (str): Collection identifier.
125-
start_date (str): Start date for the alias.
144+
name (Literal["start_datetime", "datetime", "end_datetime"]): Type of alias to create.
145+
start_date (str): Date value for the alias.
126146
127147
Returns:
128-
str: Alias name with initial date.
148+
str: Formatted alias name with prefix, type, collection ID, and date.
129149
"""
130150
cleaned = collection_id.translate(_ES_INDEX_NAME_UNSUPPORTED_CHARS_TABLE)
131-
return f"{ITEMS_INDEX_PREFIX}{cleaned.lower()}_{start_date}"
151+
return f"{ITEMS_INDEX_PREFIX}{name}_{cleaned.lower()}_{start_date}"
132152

133153
@staticmethod
134154
def _create_index_body(aliases: Dict[str, Dict]) -> Dict[str, Any]:
@@ -148,19 +168,19 @@ def _create_index_body(aliases: Dict[str, Dict]) -> Dict[str, Any]:
148168

149169
@staticmethod
150170
async def find_latest_item_in_index(client: Any, index_name: str) -> dict[str, Any]:
151-
"""Find the latest item date in the specified index.
171+
"""Find the latest item in the specified index.
152172
153173
Args:
154174
client: Search engine client instance.
155175
index_name (str): Name of the index to query.
156176
157177
Returns:
158-
datetime: Date of the latest item in the index.
178+
dict[str, Any]: Latest item document from the index with metadata.
159179
"""
160180
query = {
161181
"size": 1,
162-
"sort": [{"properties.datetime": {"order": "desc"}}],
163-
"_source": ["properties.datetime"],
182+
"sort": [{"properties.start_datetime": {"order": "desc"}}],
183+
"_source": ["properties.start_datetime", "properties.datetime", "properties.end_datetime"],
164184
}
165185

166186
response = await client.search(index=index_name, body=query)

0 commit comments

Comments
 (0)