stac-fastapi-opensearch 6.10.0__py3-none-any.whl → 6.10.1__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.
@@ -243,7 +243,7 @@ items_get_request_model = create_request_model(
243
243
  app_config = {
244
244
  "title": os.getenv("STAC_FASTAPI_TITLE", "stac-fastapi-opensearch"),
245
245
  "description": os.getenv("STAC_FASTAPI_DESCRIPTION", "stac-fastapi-opensearch"),
246
- "api_version": os.getenv("STAC_FASTAPI_VERSION", "6.10.0"),
246
+ "api_version": os.getenv("STAC_FASTAPI_VERSION", "6.10.1"),
247
247
  "settings": settings,
248
248
  "extensions": extensions,
249
249
  "client": CoreClient(
@@ -29,11 +29,14 @@ from stac_fastapi.opensearch.config import (
29
29
  )
30
30
  from stac_fastapi.opensearch.config import OpensearchSettings as SyncSearchSettings
31
31
  from stac_fastapi.sfeos_helpers.database import (
32
+ ItemAlreadyExistsError,
32
33
  add_bbox_shape_to_collection,
33
34
  apply_collections_bbox_filter_shared,
34
35
  apply_collections_datetime_filter_shared,
35
36
  apply_free_text_filter_shared,
36
37
  apply_intersects_filter_shared,
38
+ check_item_exists_in_alias,
39
+ check_item_exists_in_alias_sync,
37
40
  create_index_templates_shared,
38
41
  delete_item_index_shared,
39
42
  get_queryables_mapping_shared,
@@ -1000,6 +1003,44 @@ class DatabaseLogic(BaseDatabaseLogic):
1000
1003
  if not await self.client.exists(index=COLLECTIONS_INDEX, id=collection_id):
1001
1004
  raise NotFoundError(f"Collection {collection_id} does not exist")
1002
1005
 
1006
+ async def _check_item_exists_in_collection(
1007
+ self, collection_id: str, item_id: str
1008
+ ) -> bool:
1009
+ """Check if an item exists across all indexes for a collection.
1010
+
1011
+ Args:
1012
+ collection_id (str): The collection identifier.
1013
+ item_id (str): The item identifier.
1014
+
1015
+ Returns:
1016
+ bool: True if the item exists in any index, False otherwise.
1017
+ """
1018
+ alias = index_alias_by_collection_id(collection_id)
1019
+ doc_id = mk_item_id(item_id, collection_id)
1020
+ try:
1021
+ return await check_item_exists_in_alias(self.client, alias, doc_id)
1022
+ except Exception:
1023
+ return False
1024
+
1025
+ def _check_item_exists_in_collection_sync(
1026
+ self, collection_id: str, item_id: str
1027
+ ) -> bool:
1028
+ """Check if an item exists across all indexes for a collection (sync version).
1029
+
1030
+ Args:
1031
+ collection_id (str): The collection identifier.
1032
+ item_id (str): The item identifier.
1033
+
1034
+ Returns:
1035
+ bool: True if the item exists in any index, False otherwise.
1036
+ """
1037
+ alias = index_alias_by_collection_id(collection_id)
1038
+ doc_id = mk_item_id(item_id, collection_id)
1039
+ try:
1040
+ return check_item_exists_in_alias_sync(self.sync_client, alias, doc_id)
1041
+ except Exception:
1042
+ return False
1043
+
1003
1044
  async def async_prep_create_item(
1004
1045
  self, item: Item, base_url: str, exist_ok: bool = False
1005
1046
  ) -> Item:
@@ -1015,31 +1056,21 @@ class DatabaseLogic(BaseDatabaseLogic):
1015
1056
  Item: The prepped item.
1016
1057
 
1017
1058
  Raises:
1018
- ConflictError: If the item already exists in the database.
1059
+ ItemAlreadyExistsError: If the item already exists in the database.
1019
1060
 
1020
1061
  """
1021
1062
  await self.check_collection_exists(collection_id=item["collection"])
1022
- alias = index_alias_by_collection_id(item["collection"])
1023
- doc_id = mk_item_id(item["id"], item["collection"])
1024
-
1025
- if not exist_ok:
1026
- alias_exists = await self.client.indices.exists_alias(name=alias)
1027
1063
 
1028
- if alias_exists:
1029
- alias_info = await self.client.indices.get_alias(name=alias)
1030
- indices = list(alias_info.keys())
1031
-
1032
- for index in indices:
1033
- if await self.client.exists(index=index, id=doc_id):
1034
- raise ConflictError(
1035
- f"Item {item['id']} in collection {item['collection']} already exists"
1036
- )
1064
+ if not exist_ok and await self._check_item_exists_in_collection(
1065
+ item["collection"], item["id"]
1066
+ ):
1067
+ raise ItemAlreadyExistsError(item["id"], item["collection"])
1037
1068
 
1038
1069
  return self.item_serializer.stac_to_db(item, base_url)
1039
1070
 
1040
1071
  async def bulk_async_prep_create_item(
1041
1072
  self, item: Item, base_url: str, exist_ok: bool = False
1042
- ) -> Item:
1073
+ ) -> Optional[Item]:
1043
1074
  """
1044
1075
  Prepare an item for insertion into the database.
1045
1076
 
@@ -1067,20 +1098,19 @@ class DatabaseLogic(BaseDatabaseLogic):
1067
1098
  # Check if the collection exists
1068
1099
  await self.check_collection_exists(collection_id=item["collection"])
1069
1100
 
1070
- # Check if the item already exists in the database
1071
- if not exist_ok and await self.client.exists(
1072
- index=index_alias_by_collection_id(item["collection"]),
1073
- id=mk_item_id(item["id"], item["collection"]),
1101
+ # Check if the item already exists in the database (across all datetime indexes)
1102
+ if not exist_ok and await self._check_item_exists_in_collection(
1103
+ item["collection"], item["id"]
1074
1104
  ):
1075
- error_message = (
1076
- f"Item {item['id']} in collection {item['collection']} already exists."
1077
- )
1078
1105
  if self.async_settings.raise_on_bulk_error:
1079
- raise ConflictError(error_message)
1106
+ raise ItemAlreadyExistsError(item["id"], item["collection"])
1080
1107
  else:
1081
1108
  logger.warning(
1082
- f"{error_message} Continuing as `RAISE_ON_BULK_ERROR` is set to false."
1109
+ f"Item {item['id']} in collection {item['collection']} already exists. "
1110
+ "Skipping as `RAISE_ON_BULK_ERROR` is set to false."
1083
1111
  )
1112
+ return None
1113
+
1084
1114
  # Serialize the item into a database-compatible format
1085
1115
  prepped_item = self.item_serializer.stac_to_db(item, base_url)
1086
1116
  logger.debug(f"Item {item['id']} prepared successfully.")
@@ -1088,7 +1118,7 @@ class DatabaseLogic(BaseDatabaseLogic):
1088
1118
 
1089
1119
  def bulk_sync_prep_create_item(
1090
1120
  self, item: Item, base_url: str, exist_ok: bool = False
1091
- ) -> Item:
1121
+ ) -> Optional[Item]:
1092
1122
  """
1093
1123
  Prepare an item for insertion into the database.
1094
1124
 
@@ -1117,26 +1147,18 @@ class DatabaseLogic(BaseDatabaseLogic):
1117
1147
  if not self.sync_client.exists(index=COLLECTIONS_INDEX, id=item["collection"]):
1118
1148
  raise NotFoundError(f"Collection {item['collection']} does not exist")
1119
1149
 
1120
- # Check if the item already exists in the database
1121
- alias = index_alias_by_collection_id(item["collection"])
1122
- doc_id = mk_item_id(item["id"], item["collection"])
1123
-
1124
- if not exist_ok:
1125
- alias_exists = self.sync_client.indices.exists_alias(name=alias)
1126
-
1127
- if alias_exists:
1128
- alias_info = self.sync_client.indices.get_alias(name=alias)
1129
- indices = list(alias_info.keys())
1130
-
1131
- for index in indices:
1132
- if self.sync_client.exists(index=index, id=doc_id):
1133
- error_message = f"Item {item['id']} in collection {item['collection']} already exists."
1134
- if self.sync_settings.raise_on_bulk_error:
1135
- raise ConflictError(error_message)
1136
- else:
1137
- logger.warning(
1138
- f"{error_message} Continuing as `RAISE_ON_BULK_ERROR` is set to false."
1139
- )
1150
+ # Check if the item already exists in the database (across all datetime indexes)
1151
+ if not exist_ok and self._check_item_exists_in_collection_sync(
1152
+ item["collection"], item["id"]
1153
+ ):
1154
+ if self.sync_settings.raise_on_bulk_error:
1155
+ raise ItemAlreadyExistsError(item["id"], item["collection"])
1156
+ else:
1157
+ logger.warning(
1158
+ f"Item {item['id']} in collection {item['collection']} already exists. "
1159
+ "Skipping as `RAISE_ON_BULK_ERROR` is set to false."
1160
+ )
1161
+ return None
1140
1162
 
1141
1163
  # Serialize the item into a database-compatible format
1142
1164
  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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stac_fastapi_opensearch
3
- Version: 6.10.0
3
+ Version: 6.10.1
4
4
  Summary: An implementation of STAC API based on the FastAPI framework with Opensearch.
5
5
  Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -16,8 +16,8 @@ Classifier: Programming Language :: Python :: 3.14
16
16
  Requires-Python: >=3.11
17
17
  Requires-Dist: opensearch-py[async]~=2.8.0
18
18
  Requires-Dist: opensearch-py~=2.8.0
19
- Requires-Dist: sfeos-helpers==6.10.0
20
- Requires-Dist: stac-fastapi-core==6.10.0
19
+ Requires-Dist: sfeos-helpers==6.10.1
20
+ Requires-Dist: stac-fastapi-core==6.10.1
21
21
  Requires-Dist: starlette<0.36.0,>=0.35.0
22
22
  Requires-Dist: uvicorn~=0.23.0
23
23
  Provides-Extra: dev
@@ -29,13 +29,13 @@ Requires-Dist: pytest-cov~=4.0.0; extra == 'dev'
29
29
  Requires-Dist: pytest~=8.0; extra == 'dev'
30
30
  Requires-Dist: redis~=6.4.0; extra == 'dev'
31
31
  Requires-Dist: retry~=0.9.2; extra == 'dev'
32
- Requires-Dist: stac-fastapi-core[redis]==6.10.0; extra == 'dev'
32
+ Requires-Dist: stac-fastapi-core[redis]==6.10.1; extra == 'dev'
33
33
  Provides-Extra: docs
34
34
  Requires-Dist: mkdocs-material~=9.0.0; extra == 'docs'
35
35
  Requires-Dist: mkdocs~=1.4.0; extra == 'docs'
36
36
  Requires-Dist: pdocs~=1.2.0; extra == 'docs'
37
37
  Provides-Extra: redis
38
- Requires-Dist: stac-fastapi-core[redis]==6.10.0; extra == 'redis'
38
+ Requires-Dist: stac-fastapi-core[redis]==6.10.1; extra == 'redis'
39
39
  Provides-Extra: server
40
40
  Requires-Dist: uvicorn[standard]~=0.23.0; extra == 'server'
41
41
  Description-Content-Type: text/markdown
@@ -0,0 +1,9 @@
1
+ stac_fastapi/opensearch/__init__.py,sha256=iJWMUgn7mUvmuPQSO_FlyhJ5eDdbbfmGv1qnFOX5-qk,28
2
+ stac_fastapi/opensearch/app.py,sha256=a6ysAtZowQmqTr0mCJZvon_IL2q1XnDqxaiRfcviGZI,10768
3
+ stac_fastapi/opensearch/config.py,sha256=yICEk31TydUoogsi6q1TOJHMNOHxHQBneZE9hm7UW7U,5184
4
+ stac_fastapi/opensearch/database_logic.py,sha256=CCRtMInkTxa9MuKD9GnCz6gN_65UQXXNhVsmisIXXeo,76957
5
+ stac_fastapi/opensearch/version.py,sha256=AD8oiYJE5bGoF51nIlCATcNL2aBRMbKASqetnSyzqGA,46
6
+ stac_fastapi_opensearch-6.10.1.dist-info/METADATA,sha256=ErUuQhbIUQWU5c_o3cVUFdfOpvjxJekU98TFCeftTY0,3998
7
+ stac_fastapi_opensearch-6.10.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ stac_fastapi_opensearch-6.10.1.dist-info/entry_points.txt,sha256=zjZ0Xsr9BUNJqMkdPpl6zEIUykv1uFdJtNELFRChp0w,76
9
+ stac_fastapi_opensearch-6.10.1.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- stac_fastapi/opensearch/__init__.py,sha256=iJWMUgn7mUvmuPQSO_FlyhJ5eDdbbfmGv1qnFOX5-qk,28
2
- stac_fastapi/opensearch/app.py,sha256=INHCe4KN3ld9tZtN1_5M2yVjk4GKbup15PQlee4kPaw,10768
3
- stac_fastapi/opensearch/config.py,sha256=yICEk31TydUoogsi6q1TOJHMNOHxHQBneZE9hm7UW7U,5184
4
- stac_fastapi/opensearch/database_logic.py,sha256=_MQkPCOlFWmO6D0Ky07eCqSf3JuGdV6KodkIGAylvNs,76383
5
- stac_fastapi/opensearch/version.py,sha256=bQiD_D-FuZl4YJZxrz9LK-THtzxnF7e-QdjJAiGBoSY,46
6
- stac_fastapi_opensearch-6.10.0.dist-info/METADATA,sha256=361KXJDFzSAjvsvYxbblRxsPNNgw2b2yOhGfeJDTMvw,3998
7
- stac_fastapi_opensearch-6.10.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
- stac_fastapi_opensearch-6.10.0.dist-info/entry_points.txt,sha256=zjZ0Xsr9BUNJqMkdPpl6zEIUykv1uFdJtNELFRChp0w,76
9
- stac_fastapi_opensearch-6.10.0.dist-info/RECORD,,