stac-fastapi-elasticsearch 6.10.0__tar.gz → 6.10.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stac_fastapi_elasticsearch
3
- Version: 6.10.0
3
+ Version: 6.10.1
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.10.0
19
- Requires-Dist: stac-fastapi-core==6.10.0
18
+ Requires-Dist: sfeos-helpers==6.10.1
19
+ Requires-Dist: stac-fastapi-core==6.10.1
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.10.0; extra == 'dev'
31
+ Requires-Dist: stac-fastapi-core[redis]==6.10.1; 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.10.0; extra == 'redis'
39
+ Requires-Dist: stac-fastapi-core[redis]==6.10.1; 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
@@ -28,8 +28,8 @@ keywords = [
28
28
  ]
29
29
  dynamic = ["version"]
30
30
  dependencies = [
31
- "stac-fastapi-core==6.10.0",
32
- "sfeos-helpers==6.10.0",
31
+ "stac-fastapi-core==6.10.1",
32
+ "sfeos-helpers==6.10.1",
33
33
  "elasticsearch[async]~=8.19.1",
34
34
  "uvicorn~=0.23.0",
35
35
  "starlette>=0.35.0,<0.36.0",
@@ -48,7 +48,7 @@ dev = [
48
48
  "httpx>=0.24.0,<0.28.0",
49
49
  "redis~=6.4.0",
50
50
  "retry~=0.9.2",
51
- "stac-fastapi-core[redis]==6.10.0",
51
+ "stac-fastapi-core[redis]==6.10.1",
52
52
  ]
53
53
  docs = [
54
54
  "mkdocs~=1.4.0",
@@ -58,7 +58,7 @@ docs = [
58
58
  "retry~=0.9.2",
59
59
  ]
60
60
  redis = [
61
- "stac-fastapi-core[redis]==6.10.0",
61
+ "stac-fastapi-core[redis]==6.10.1",
62
62
  ]
63
63
  server = [
64
64
  "uvicorn[standard]~=0.23.0",
@@ -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.10.0"),
247
+ "api_version": os.getenv("STAC_FASTAPI_VERSION", "6.10.1"),
248
248
  "settings": settings,
249
249
  "extensions": extensions,
250
250
  "client": CoreClient(
@@ -33,11 +33,14 @@ from stac_fastapi.extensions.core.transaction.request import (
33
33
  PatchOperation,
34
34
  )
35
35
  from stac_fastapi.sfeos_helpers.database import (
36
+ ItemAlreadyExistsError,
36
37
  add_bbox_shape_to_collection,
37
38
  apply_collections_bbox_filter_shared,
38
39
  apply_collections_datetime_filter_shared,
39
40
  apply_free_text_filter_shared,
40
41
  apply_intersects_filter_shared,
42
+ check_item_exists_in_alias,
43
+ check_item_exists_in_alias_sync,
41
44
  create_index_templates_shared,
42
45
  delete_item_index_shared,
43
46
  get_queryables_mapping_shared,
@@ -996,6 +999,44 @@ class DatabaseLogic(BaseDatabaseLogic):
996
999
  if not await self.client.exists(index=COLLECTIONS_INDEX, id=collection_id):
997
1000
  raise NotFoundError(f"Collection {collection_id} does not exist")
998
1001
 
1002
+ async def _check_item_exists_in_collection(
1003
+ self, collection_id: str, item_id: str
1004
+ ) -> bool:
1005
+ """Check if an item exists across all indexes for a collection.
1006
+
1007
+ Args:
1008
+ collection_id (str): The collection identifier.
1009
+ item_id (str): The item identifier.
1010
+
1011
+ Returns:
1012
+ bool: True if the item exists in any index, False otherwise.
1013
+ """
1014
+ alias = index_alias_by_collection_id(collection_id)
1015
+ doc_id = mk_item_id(item_id, collection_id)
1016
+ try:
1017
+ return await check_item_exists_in_alias(self.client, alias, doc_id)
1018
+ except Exception:
1019
+ return False
1020
+
1021
+ def _check_item_exists_in_collection_sync(
1022
+ self, collection_id: str, item_id: str
1023
+ ) -> bool:
1024
+ """Check if an item exists across all indexes for a collection (sync version).
1025
+
1026
+ Args:
1027
+ collection_id (str): The collection identifier.
1028
+ item_id (str): The item identifier.
1029
+
1030
+ Returns:
1031
+ bool: True if the item exists in any index, False otherwise.
1032
+ """
1033
+ alias = index_alias_by_collection_id(collection_id)
1034
+ doc_id = mk_item_id(item_id, collection_id)
1035
+ try:
1036
+ return check_item_exists_in_alias_sync(self.sync_client, alias, doc_id)
1037
+ except Exception:
1038
+ return False
1039
+
999
1040
  async def async_prep_create_item(
1000
1041
  self, item: Item, base_url: str, exist_ok: bool = False
1001
1042
  ) -> Item:
@@ -1011,31 +1052,21 @@ class DatabaseLogic(BaseDatabaseLogic):
1011
1052
  Item: The prepped item.
1012
1053
 
1013
1054
  Raises:
1014
- ConflictError: If the item already exists in the database.
1055
+ ItemAlreadyExistsError: If the item already exists in the database.
1015
1056
 
1016
1057
  """
1017
1058
  await self.check_collection_exists(collection_id=item["collection"])
1018
- alias = index_alias_by_collection_id(item["collection"])
1019
- doc_id = mk_item_id(item["id"], item["collection"])
1020
-
1021
- if not exist_ok:
1022
- alias_exists = await self.client.indices.exists_alias(name=alias)
1023
-
1024
- if alias_exists:
1025
- alias_info = await self.client.indices.get_alias(name=alias)
1026
- indices = list(alias_info.keys())
1027
1059
 
1028
- for index in indices:
1029
- if await self.client.exists(index=index, id=doc_id):
1030
- raise ConflictError(
1031
- f"Item {item['id']} in collection {item['collection']} already exists"
1032
- )
1060
+ if not exist_ok and await self._check_item_exists_in_collection(
1061
+ item["collection"], item["id"]
1062
+ ):
1063
+ raise ItemAlreadyExistsError(item["id"], item["collection"])
1033
1064
 
1034
1065
  return self.item_serializer.stac_to_db(item, base_url)
1035
1066
 
1036
1067
  async def bulk_async_prep_create_item(
1037
1068
  self, item: Item, base_url: str, exist_ok: bool = False
1038
- ) -> Item:
1069
+ ) -> Optional[Item]:
1039
1070
  """
1040
1071
  Prepare an item for insertion into the database.
1041
1072
 
@@ -1063,20 +1094,18 @@ class DatabaseLogic(BaseDatabaseLogic):
1063
1094
  # Check if the collection exists
1064
1095
  await self.check_collection_exists(collection_id=item["collection"])
1065
1096
 
1066
- # Check if the item already exists in the database
1067
- if not exist_ok and await self.client.exists(
1068
- index=index_alias_by_collection_id(item["collection"]),
1069
- id=mk_item_id(item["id"], item["collection"]),
1097
+ # Check if the item already exists in the database (across all datetime indexes)
1098
+ if not exist_ok and await self._check_item_exists_in_collection(
1099
+ item["collection"], item["id"]
1070
1100
  ):
1071
- error_message = (
1072
- f"Item {item['id']} in collection {item['collection']} already exists."
1073
- )
1074
1101
  if self.async_settings.raise_on_bulk_error:
1075
- raise ConflictError(error_message)
1102
+ raise ItemAlreadyExistsError(item["id"], item["collection"])
1076
1103
  else:
1077
1104
  logger.warning(
1078
- f"{error_message} Continuing as `RAISE_ON_BULK_ERROR` is set to false."
1105
+ f"Item {item['id']} in collection {item['collection']} already exists. "
1106
+ "Skipping as `RAISE_ON_BULK_ERROR` is set to false."
1079
1107
  )
1108
+ return None
1080
1109
 
1081
1110
  # Serialize the item into a database-compatible format
1082
1111
  prepped_item = self.item_serializer.stac_to_db(item, base_url)
@@ -1085,7 +1114,7 @@ class DatabaseLogic(BaseDatabaseLogic):
1085
1114
 
1086
1115
  def bulk_sync_prep_create_item(
1087
1116
  self, item: Item, base_url: str, exist_ok: bool = False
1088
- ) -> Item:
1117
+ ) -> Optional[Item]:
1089
1118
  """
1090
1119
  Prepare an item for insertion into the database.
1091
1120
 
@@ -1114,26 +1143,18 @@ class DatabaseLogic(BaseDatabaseLogic):
1114
1143
  if not self.sync_client.exists(index=COLLECTIONS_INDEX, id=item["collection"]):
1115
1144
  raise NotFoundError(f"Collection {item['collection']} does not exist")
1116
1145
 
1117
- # Check if the item already exists in the database
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
- )
1146
+ # Check if the item already exists in the database (across all datetime indexes)
1147
+ if not exist_ok and self._check_item_exists_in_collection_sync(
1148
+ item["collection"], item["id"]
1149
+ ):
1150
+ if self.sync_settings.raise_on_bulk_error:
1151
+ raise ItemAlreadyExistsError(item["id"], item["collection"])
1152
+ else:
1153
+ logger.warning(
1154
+ f"Item {item['id']} in collection {item['collection']} already exists. "
1155
+ "Skipping as `RAISE_ON_BULK_ERROR` is set to false."
1156
+ )
1157
+ return None
1137
1158
 
1138
1159
  # Serialize the item into a database-compatible format
1139
1160
  prepped_item = self.item_serializer.stac_to_db(item, base_url)
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "6.10.0"
2
+ __version__ = "6.10.1"