stac-fastapi-elasticsearch 6.9.0__py3-none-any.whl → 6.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stac_fastapi/elasticsearch/app.py +2 -2
- stac_fastapi/elasticsearch/database_logic.py +92 -49
- stac_fastapi/elasticsearch/version.py +1 -1
- {stac_fastapi_elasticsearch-6.9.0.dist-info → stac_fastapi_elasticsearch-6.10.0.dist-info}/METADATA +5 -5
- stac_fastapi_elasticsearch-6.10.0.dist-info/RECORD +9 -0
- stac_fastapi_elasticsearch-6.9.0.dist-info/RECORD +0 -9
- {stac_fastapi_elasticsearch-6.9.0.dist-info → stac_fastapi_elasticsearch-6.10.0.dist-info}/WHEEL +0 -0
- {stac_fastapi_elasticsearch-6.9.0.dist-info → stac_fastapi_elasticsearch-6.10.0.dist-info}/entry_points.txt +0 -0
|
@@ -215,7 +215,7 @@ if ENABLE_CATALOGS_ROUTE:
|
|
|
215
215
|
),
|
|
216
216
|
settings=settings,
|
|
217
217
|
conformance_classes=[
|
|
218
|
-
"https://api.stacspec.org/v1.0.0-beta.1/catalogs
|
|
218
|
+
"https://api.stacspec.org/v1.0.0-beta.1/multi-tenant-catalogs",
|
|
219
219
|
],
|
|
220
220
|
)
|
|
221
221
|
extensions.append(catalogs_extension)
|
|
@@ -244,7 +244,7 @@ items_get_request_model = create_request_model(
|
|
|
244
244
|
app_config = {
|
|
245
245
|
"title": os.getenv("STAC_FASTAPI_TITLE", "stac-fastapi-elasticsearch"),
|
|
246
246
|
"description": os.getenv("STAC_FASTAPI_DESCRIPTION", "stac-fastapi-elasticsearch"),
|
|
247
|
-
"api_version": os.getenv("STAC_FASTAPI_VERSION", "6.
|
|
247
|
+
"api_version": os.getenv("STAC_FASTAPI_VERSION", "6.10.0"),
|
|
248
248
|
"settings": settings,
|
|
249
249
|
"extensions": extensions,
|
|
250
250
|
"client": CoreClient(
|
|
@@ -53,6 +53,7 @@ from stac_fastapi.sfeos_helpers.database.query import (
|
|
|
53
53
|
add_collections_to_body,
|
|
54
54
|
)
|
|
55
55
|
from stac_fastapi.sfeos_helpers.database.utils import (
|
|
56
|
+
add_hidden_filter,
|
|
56
57
|
merge_to_operations,
|
|
57
58
|
operations_to_script,
|
|
58
59
|
)
|
|
@@ -67,6 +68,7 @@ from stac_fastapi.sfeos_helpers.mappings import (
|
|
|
67
68
|
from stac_fastapi.sfeos_helpers.search_engine import (
|
|
68
69
|
BaseIndexInserter,
|
|
69
70
|
BaseIndexSelector,
|
|
71
|
+
DatetimeIndexInserter,
|
|
70
72
|
IndexInsertionFactory,
|
|
71
73
|
IndexSelectorFactory,
|
|
72
74
|
)
|
|
@@ -406,12 +408,22 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
406
408
|
Notes:
|
|
407
409
|
The Item is retrieved from the Elasticsearch database using the `client.get` method,
|
|
408
410
|
with the index for the Collection as the target index and the combined `mk_item_id` as the document id.
|
|
411
|
+
Item is hidden if hide_item_path is configured via env var.
|
|
409
412
|
"""
|
|
410
413
|
try:
|
|
414
|
+
base_query = {"term": {"_id": mk_item_id(item_id, collection_id)}}
|
|
415
|
+
|
|
416
|
+
HIDE_ITEM_PATH = os.getenv("HIDE_ITEM_PATH", None)
|
|
417
|
+
|
|
418
|
+
if HIDE_ITEM_PATH:
|
|
419
|
+
query = add_hidden_filter(base_query, HIDE_ITEM_PATH)
|
|
420
|
+
else:
|
|
421
|
+
query = base_query
|
|
422
|
+
|
|
411
423
|
response = await self.client.search(
|
|
412
424
|
index=index_alias_by_collection_id(collection_id),
|
|
413
425
|
body={
|
|
414
|
-
"query":
|
|
426
|
+
"query": query,
|
|
415
427
|
"size": 1,
|
|
416
428
|
},
|
|
417
429
|
)
|
|
@@ -656,7 +668,7 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
656
668
|
),
|
|
657
669
|
],
|
|
658
670
|
)
|
|
659
|
-
|
|
671
|
+
return search.query(filter_query), datetime_search
|
|
660
672
|
|
|
661
673
|
@staticmethod
|
|
662
674
|
def apply_bbox_filter(search: Search, bbox: List):
|
|
@@ -811,7 +823,7 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
811
823
|
token: Optional[str],
|
|
812
824
|
sort: Optional[Dict[str, Dict[str, str]]],
|
|
813
825
|
collection_ids: Optional[List[str]],
|
|
814
|
-
datetime_search:
|
|
826
|
+
datetime_search: str,
|
|
815
827
|
ignore_unavailable: bool = True,
|
|
816
828
|
) -> Tuple[Iterable[Dict[str, Any]], Optional[int], Optional[str]]:
|
|
817
829
|
"""Execute a search query with limit and other optional parameters.
|
|
@@ -822,7 +834,7 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
822
834
|
token (Optional[str]): The token used to return the next set of results.
|
|
823
835
|
sort (Optional[Dict[str, Dict[str, str]]]): Specifies how the results should be sorted.
|
|
824
836
|
collection_ids (Optional[List[str]]): The collection ids to search.
|
|
825
|
-
datetime_search (
|
|
837
|
+
datetime_search (str): Datetime used for index selection.
|
|
826
838
|
ignore_unavailable (bool, optional): Whether to ignore unavailable collections. Defaults to True.
|
|
827
839
|
|
|
828
840
|
Returns:
|
|
@@ -854,6 +866,10 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
854
866
|
|
|
855
867
|
size_limit = min(limit + 1, max_result_window)
|
|
856
868
|
|
|
869
|
+
HIDE_ITEM_PATH = os.getenv("HIDE_ITEM_PATH", None)
|
|
870
|
+
if HIDE_ITEM_PATH:
|
|
871
|
+
query = add_hidden_filter(query, HIDE_ITEM_PATH)
|
|
872
|
+
|
|
857
873
|
search_task = asyncio.create_task(
|
|
858
874
|
self.client.search(
|
|
859
875
|
index=index_param,
|
|
@@ -865,11 +881,17 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
865
881
|
)
|
|
866
882
|
)
|
|
867
883
|
|
|
884
|
+
# Apply hidden filter to count query as well
|
|
885
|
+
count_query = search.to_dict(count=True)
|
|
886
|
+
if HIDE_ITEM_PATH:
|
|
887
|
+
q = count_query.get("query")
|
|
888
|
+
count_query["query"] = add_hidden_filter(q, HIDE_ITEM_PATH)
|
|
889
|
+
|
|
868
890
|
count_task = asyncio.create_task(
|
|
869
891
|
self.client.count(
|
|
870
892
|
index=index_param,
|
|
871
893
|
ignore_unavailable=ignore_unavailable,
|
|
872
|
-
body=
|
|
894
|
+
body=count_query,
|
|
873
895
|
)
|
|
874
896
|
)
|
|
875
897
|
|
|
@@ -912,7 +934,7 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
912
934
|
geometry_geohash_grid_precision: int,
|
|
913
935
|
geometry_geotile_grid_precision: int,
|
|
914
936
|
datetime_frequency_interval: str,
|
|
915
|
-
datetime_search,
|
|
937
|
+
datetime_search: str,
|
|
916
938
|
ignore_unavailable: Optional[bool] = True,
|
|
917
939
|
):
|
|
918
940
|
"""Return aggregations of STAC Items."""
|
|
@@ -1093,19 +1115,25 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1093
1115
|
raise NotFoundError(f"Collection {item['collection']} does not exist")
|
|
1094
1116
|
|
|
1095
1117
|
# Check if the item already exists in the database
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1118
|
+
alias = index_alias_by_collection_id(item["collection"])
|
|
1119
|
+
doc_id = mk_item_id(item["id"], item["collection"])
|
|
1120
|
+
|
|
1121
|
+
if not exist_ok:
|
|
1122
|
+
alias_exists = self.sync_client.indices.exists_alias(name=alias)
|
|
1123
|
+
|
|
1124
|
+
if alias_exists:
|
|
1125
|
+
alias_info = self.sync_client.indices.get_alias(name=alias)
|
|
1126
|
+
indices = list(alias_info.keys())
|
|
1127
|
+
|
|
1128
|
+
for index in indices:
|
|
1129
|
+
if self.sync_client.exists(index=index, id=doc_id):
|
|
1130
|
+
error_message = f"Item {item['id']} in collection {item['collection']} already exists."
|
|
1131
|
+
if self.sync_settings.raise_on_bulk_error:
|
|
1132
|
+
raise ConflictError(error_message)
|
|
1133
|
+
else:
|
|
1134
|
+
logger.warning(
|
|
1135
|
+
f"{error_message} Continuing as `RAISE_ON_BULK_ERROR` is set to false."
|
|
1136
|
+
)
|
|
1109
1137
|
|
|
1110
1138
|
# Serialize the item into a database-compatible format
|
|
1111
1139
|
prepped_item = self.item_serializer.stac_to_db(item, base_url)
|
|
@@ -1150,6 +1178,31 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1150
1178
|
f"Creating item {item_id} in collection {collection_id} with refresh={refresh}"
|
|
1151
1179
|
)
|
|
1152
1180
|
|
|
1181
|
+
if exist_ok and isinstance(self.async_index_inserter, DatetimeIndexInserter):
|
|
1182
|
+
existing_item = await self.get_one_item(collection_id, item_id)
|
|
1183
|
+
primary_datetime_name = self.async_index_inserter.primary_datetime_name
|
|
1184
|
+
|
|
1185
|
+
existing_primary_datetime = existing_item.get("properties", {}).get(
|
|
1186
|
+
primary_datetime_name
|
|
1187
|
+
)
|
|
1188
|
+
new_primary_datetime = item.get("properties", {}).get(primary_datetime_name)
|
|
1189
|
+
|
|
1190
|
+
if existing_primary_datetime != new_primary_datetime:
|
|
1191
|
+
self.async_index_inserter.validate_datetime_field_update(
|
|
1192
|
+
f"properties/{primary_datetime_name}"
|
|
1193
|
+
)
|
|
1194
|
+
|
|
1195
|
+
if primary_datetime_name == "start_datetime":
|
|
1196
|
+
existing_end_datetime = existing_item.get("properties", {}).get(
|
|
1197
|
+
"end_datetime"
|
|
1198
|
+
)
|
|
1199
|
+
new_end_datetime = item.get("properties", {}).get("end_datetime")
|
|
1200
|
+
|
|
1201
|
+
if existing_end_datetime != new_end_datetime:
|
|
1202
|
+
self.async_index_inserter.validate_datetime_field_update(
|
|
1203
|
+
"properties/end_datetime"
|
|
1204
|
+
)
|
|
1205
|
+
|
|
1153
1206
|
# Prepare the item for insertion
|
|
1154
1207
|
item = await self.async_prep_create_item(
|
|
1155
1208
|
item=item, base_url=base_url, exist_ok=exist_ok
|
|
@@ -1218,6 +1271,10 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1218
1271
|
Returns:
|
|
1219
1272
|
patched item.
|
|
1220
1273
|
"""
|
|
1274
|
+
for operation in operations:
|
|
1275
|
+
if operation.op in ["add", "replace", "remove"]:
|
|
1276
|
+
self.async_index_inserter.validate_datetime_field_update(operation.path)
|
|
1277
|
+
|
|
1221
1278
|
new_item_id = None
|
|
1222
1279
|
new_collection_id = None
|
|
1223
1280
|
script_operations = []
|
|
@@ -1238,8 +1295,6 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1238
1295
|
else:
|
|
1239
1296
|
script_operations.append(operation)
|
|
1240
1297
|
|
|
1241
|
-
script = operations_to_script(script_operations, create_nest=create_nest)
|
|
1242
|
-
|
|
1243
1298
|
try:
|
|
1244
1299
|
search_response = await self.client.search(
|
|
1245
1300
|
index=index_alias_by_collection_id(collection_id),
|
|
@@ -1252,13 +1307,18 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1252
1307
|
raise NotFoundError(
|
|
1253
1308
|
f"Item {item_id} does not exist inside Collection {collection_id}"
|
|
1254
1309
|
)
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1310
|
+
|
|
1311
|
+
if script_operations:
|
|
1312
|
+
script = operations_to_script(
|
|
1313
|
+
script_operations, create_nest=create_nest
|
|
1314
|
+
)
|
|
1315
|
+
document_index = search_response["hits"]["hits"][0]["_index"]
|
|
1316
|
+
await self.client.update(
|
|
1317
|
+
index=document_index,
|
|
1318
|
+
id=mk_item_id(item_id, collection_id),
|
|
1319
|
+
script=script,
|
|
1320
|
+
refresh=True,
|
|
1321
|
+
)
|
|
1262
1322
|
except ESNotFoundError:
|
|
1263
1323
|
raise NotFoundError(
|
|
1264
1324
|
f"Item {item_id} does not exist inside Collection {collection_id}"
|
|
@@ -1271,26 +1331,9 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1271
1331
|
item = await self.get_one_item(collection_id, item_id)
|
|
1272
1332
|
|
|
1273
1333
|
if new_collection_id:
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
"index": f"{ITEMS_INDEX_PREFIX}{new_collection_id}"
|
|
1278
|
-
}, # # noqa
|
|
1279
|
-
"source": {
|
|
1280
|
-
"index": f"{ITEMS_INDEX_PREFIX}{collection_id}",
|
|
1281
|
-
"query": {"term": {"id": {"value": item_id}}},
|
|
1282
|
-
},
|
|
1283
|
-
"script": {
|
|
1284
|
-
"lang": "painless",
|
|
1285
|
-
"source": (
|
|
1286
|
-
f"""ctx._id = ctx._id.replace('{collection_id}', '{new_collection_id}');""" # noqa
|
|
1287
|
-
f"""ctx._source.collection = '{new_collection_id}';""" # noqa
|
|
1288
|
-
),
|
|
1289
|
-
},
|
|
1290
|
-
},
|
|
1291
|
-
wait_for_completion=True,
|
|
1292
|
-
refresh=True,
|
|
1293
|
-
)
|
|
1334
|
+
item["collection"] = new_collection_id
|
|
1335
|
+
item = await self.async_prep_create_item(item=item, base_url=base_url)
|
|
1336
|
+
await self.create_item(item=item, refresh=True)
|
|
1294
1337
|
|
|
1295
1338
|
await self.delete_item(
|
|
1296
1339
|
item_id=item_id,
|
|
@@ -1298,7 +1341,6 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1298
1341
|
refresh=refresh,
|
|
1299
1342
|
)
|
|
1300
1343
|
|
|
1301
|
-
item["collection"] = new_collection_id
|
|
1302
1344
|
collection_id = new_collection_id
|
|
1303
1345
|
|
|
1304
1346
|
if new_item_id:
|
|
@@ -1684,6 +1726,7 @@ class DatabaseLogic(BaseDatabaseLogic):
|
|
|
1684
1726
|
index=COLLECTIONS_INDEX, id=collection_id, refresh=refresh
|
|
1685
1727
|
)
|
|
1686
1728
|
await delete_item_index(collection_id)
|
|
1729
|
+
await self.async_index_inserter.refresh_cache()
|
|
1687
1730
|
|
|
1688
1731
|
async def bulk_async(
|
|
1689
1732
|
self,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""library version."""
|
|
2
|
-
__version__ = "6.
|
|
2
|
+
__version__ = "6.10.0"
|
{stac_fastapi_elasticsearch-6.9.0.dist-info → stac_fastapi_elasticsearch-6.10.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stac_fastapi_elasticsearch
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.10.0
|
|
4
4
|
Summary: An implementation of STAC API based on the FastAPI framework with Elasticsearch.
|
|
5
5
|
Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
6
|
License: MIT
|
|
@@ -15,8 +15,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
16
16
|
Requires-Python: >=3.11
|
|
17
17
|
Requires-Dist: elasticsearch[async]~=8.19.1
|
|
18
|
-
Requires-Dist: sfeos-helpers==6.
|
|
19
|
-
Requires-Dist: stac-fastapi-core==6.
|
|
18
|
+
Requires-Dist: sfeos-helpers==6.10.0
|
|
19
|
+
Requires-Dist: stac-fastapi-core==6.10.0
|
|
20
20
|
Requires-Dist: starlette<0.36.0,>=0.35.0
|
|
21
21
|
Requires-Dist: uvicorn~=0.23.0
|
|
22
22
|
Provides-Extra: dev
|
|
@@ -28,7 +28,7 @@ Requires-Dist: pytest-cov~=4.0.0; extra == 'dev'
|
|
|
28
28
|
Requires-Dist: pytest~=8.0; extra == 'dev'
|
|
29
29
|
Requires-Dist: redis~=6.4.0; extra == 'dev'
|
|
30
30
|
Requires-Dist: retry~=0.9.2; extra == 'dev'
|
|
31
|
-
Requires-Dist: stac-fastapi-core[redis]==6.
|
|
31
|
+
Requires-Dist: stac-fastapi-core[redis]==6.10.0; extra == 'dev'
|
|
32
32
|
Provides-Extra: docs
|
|
33
33
|
Requires-Dist: mkdocs-material~=9.0.0; extra == 'docs'
|
|
34
34
|
Requires-Dist: mkdocs~=1.4.0; extra == 'docs'
|
|
@@ -36,7 +36,7 @@ Requires-Dist: pdocs~=1.2.0; extra == 'docs'
|
|
|
36
36
|
Requires-Dist: redis~=6.4.0; extra == 'docs'
|
|
37
37
|
Requires-Dist: retry~=0.9.2; extra == 'docs'
|
|
38
38
|
Provides-Extra: redis
|
|
39
|
-
Requires-Dist: stac-fastapi-core[redis]==6.
|
|
39
|
+
Requires-Dist: stac-fastapi-core[redis]==6.10.0; extra == 'redis'
|
|
40
40
|
Provides-Extra: server
|
|
41
41
|
Requires-Dist: uvicorn[standard]~=0.23.0; extra == 'server'
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
stac_fastapi/elasticsearch/__init__.py,sha256=w_MZutYLreNV372sCuO46bPb0TngmPs4u8737ueS0wE,31
|
|
2
|
+
stac_fastapi/elasticsearch/app.py,sha256=JTWBVb112fuaFyhc9zAYn_tTQMzELHTK7o28W1EuY40,10767
|
|
3
|
+
stac_fastapi/elasticsearch/config.py,sha256=CCQlqvakTjFKxrdCW8222Wz9f2WrMxi1uVkyxqA1R60,5327
|
|
4
|
+
stac_fastapi/elasticsearch/database_logic.py,sha256=JVO29fobE2hiSCzNLbO3BZ5xe0wl9fldPsYIT9thO_0,78220
|
|
5
|
+
stac_fastapi/elasticsearch/version.py,sha256=bQiD_D-FuZl4YJZxrz9LK-THtzxnF7e-QdjJAiGBoSY,46
|
|
6
|
+
stac_fastapi_elasticsearch-6.10.0.dist-info/METADATA,sha256=5vmlGoUWNNSXk9GVLIYM9WZC8jpZsjK2vDGfsR4ptnI,4077
|
|
7
|
+
stac_fastapi_elasticsearch-6.10.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
stac_fastapi_elasticsearch-6.10.0.dist-info/entry_points.txt,sha256=aCKixki0LpUl64UPsPMtiNvfdyq-QsTCxVjJ54VF6Jk,82
|
|
9
|
+
stac_fastapi_elasticsearch-6.10.0.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
stac_fastapi/elasticsearch/__init__.py,sha256=w_MZutYLreNV372sCuO46bPb0TngmPs4u8737ueS0wE,31
|
|
2
|
-
stac_fastapi/elasticsearch/app.py,sha256=QtJ4Hg_X0BMZoLlFRk6pKTxkdrX5aiW3kvm_pcVgrAo,10762
|
|
3
|
-
stac_fastapi/elasticsearch/config.py,sha256=CCQlqvakTjFKxrdCW8222Wz9f2WrMxi1uVkyxqA1R60,5327
|
|
4
|
-
stac_fastapi/elasticsearch/database_logic.py,sha256=ZecJRkzd0w7itmKi2AAfvyQZ9mgWlyhlOPpeq_4uneg,76362
|
|
5
|
-
stac_fastapi/elasticsearch/version.py,sha256=Zec8murh7xqMR7l6fB74XJcq2QdyNqRgOdhLMwBYPAI,45
|
|
6
|
-
stac_fastapi_elasticsearch-6.9.0.dist-info/METADATA,sha256=bp8RYbDAdkrAQbFS8KPiifydA9WuUDBXBITlAgLbksU,4072
|
|
7
|
-
stac_fastapi_elasticsearch-6.9.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
-
stac_fastapi_elasticsearch-6.9.0.dist-info/entry_points.txt,sha256=aCKixki0LpUl64UPsPMtiNvfdyq-QsTCxVjJ54VF6Jk,82
|
|
9
|
-
stac_fastapi_elasticsearch-6.9.0.dist-info/RECORD,,
|
{stac_fastapi_elasticsearch-6.9.0.dist-info → stac_fastapi_elasticsearch-6.10.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|