stac-fastapi-core 6.2.1__tar.gz → 6.3.0__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.
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/PKG-INFO +41 -43
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/README.md +39 -20
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/core.py +81 -77
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/datetime_utils.py +4 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/serializers.py +35 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/utilities.py +9 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/version.py +1 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/PKG-INFO +42 -44
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/top_level.txt +0 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/setup.cfg +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/setup.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/__init__.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/base_database_logic.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/base_settings.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/basic_auth.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/__init__.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/aggregation.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/fields.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/filter.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/query.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/models/__init__.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/models/links.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/models/search.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/rate_limit.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/route_dependencies.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/session.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/not-zip-safe +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/requires.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: stac_fastapi_core
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.3.0
|
|
4
4
|
Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
|
|
5
5
|
Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
6
|
License: MIT
|
|
@@ -15,27 +15,6 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: fastapi~=0.109.0
|
|
19
|
-
Requires-Dist: attrs>=23.2.0
|
|
20
|
-
Requires-Dist: pydantic<3.0.0,>=2.4.1
|
|
21
|
-
Requires-Dist: stac_pydantic~=3.3.0
|
|
22
|
-
Requires-Dist: stac-fastapi.types==6.0.0
|
|
23
|
-
Requires-Dist: stac-fastapi.api==6.0.0
|
|
24
|
-
Requires-Dist: stac-fastapi.extensions==6.0.0
|
|
25
|
-
Requires-Dist: orjson~=3.9.0
|
|
26
|
-
Requires-Dist: overrides~=7.4.0
|
|
27
|
-
Requires-Dist: geojson-pydantic~=1.0.0
|
|
28
|
-
Requires-Dist: pygeofilter~=0.3.1
|
|
29
|
-
Requires-Dist: jsonschema~=4.0.0
|
|
30
|
-
Requires-Dist: slowapi~=0.1.9
|
|
31
|
-
Dynamic: classifier
|
|
32
|
-
Dynamic: description
|
|
33
|
-
Dynamic: description-content-type
|
|
34
|
-
Dynamic: home-page
|
|
35
|
-
Dynamic: license
|
|
36
|
-
Dynamic: requires-dist
|
|
37
|
-
Dynamic: requires-python
|
|
38
|
-
Dynamic: summary
|
|
39
18
|
|
|
40
19
|
# stac-fastapi-elasticsearch-opensearch
|
|
41
20
|
|
|
@@ -105,26 +84,43 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
105
84
|
|
|
106
85
|
## Table of Contents
|
|
107
86
|
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
112
|
-
- [
|
|
113
|
-
- [
|
|
114
|
-
- [
|
|
115
|
-
- [
|
|
116
|
-
- [
|
|
117
|
-
- [
|
|
118
|
-
- [
|
|
119
|
-
- [
|
|
120
|
-
- [
|
|
121
|
-
- [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- [
|
|
125
|
-
- [
|
|
126
|
-
- [
|
|
127
|
-
- [
|
|
87
|
+
- [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
|
|
88
|
+
- [Sponsors \& Supporters](#sponsors--supporters)
|
|
89
|
+
- [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
|
|
90
|
+
- [Common Deployment Patterns](#common-deployment-patterns)
|
|
91
|
+
- [Technologies](#technologies)
|
|
92
|
+
- [Table of Contents](#table-of-contents)
|
|
93
|
+
- [Documentation \& Resources](#documentation--resources)
|
|
94
|
+
- [Package Structure](#package-structure)
|
|
95
|
+
- [Examples](#examples)
|
|
96
|
+
- [Performance](#performance)
|
|
97
|
+
- [Direct Response Mode](#direct-response-mode)
|
|
98
|
+
- [Quick Start](#quick-start)
|
|
99
|
+
- [Installation](#installation)
|
|
100
|
+
- [Running Locally](#running-locally)
|
|
101
|
+
- [Using Pre-built Docker Images](#using-pre-built-docker-images)
|
|
102
|
+
- [Using Docker Compose](#using-docker-compose)
|
|
103
|
+
- [Configuration Reference](#configuration-reference)
|
|
104
|
+
- [Datetime-Based Index Management](#datetime-based-index-management)
|
|
105
|
+
- [Overview](#overview)
|
|
106
|
+
- [When to Use](#when-to-use)
|
|
107
|
+
- [Configuration](#configuration)
|
|
108
|
+
- [Enabling Datetime-Based Indexing](#enabling-datetime-based-indexing)
|
|
109
|
+
- [Related Configuration Variables](#related-configuration-variables)
|
|
110
|
+
- [How Datetime-Based Indexing Works](#how-datetime-based-indexing-works)
|
|
111
|
+
- [Index and Alias Naming Convention](#index-and-alias-naming-convention)
|
|
112
|
+
- [Index Size Management](#index-size-management)
|
|
113
|
+
- [Interacting with the API](#interacting-with-the-api)
|
|
114
|
+
- [Configure the API](#configure-the-api)
|
|
115
|
+
- [Collection Pagination](#collection-pagination)
|
|
116
|
+
- [Ingesting Sample Data CLI Tool](#ingesting-sample-data-cli-tool)
|
|
117
|
+
- [Elasticsearch Mappings](#elasticsearch-mappings)
|
|
118
|
+
- [Managing Elasticsearch Indices](#managing-elasticsearch-indices)
|
|
119
|
+
- [Snapshots](#snapshots)
|
|
120
|
+
- [Reindexing](#reindexing)
|
|
121
|
+
- [Auth](#auth)
|
|
122
|
+
- [Aggregation](#aggregation)
|
|
123
|
+
- [Rate Limiting](#rate-limiting)
|
|
128
124
|
|
|
129
125
|
## Documentation & Resources
|
|
130
126
|
|
|
@@ -267,6 +263,8 @@ You can customize additional settings in your `.env` file:
|
|
|
267
263
|
| `DATABASE_REFRESH` | Controls whether database operations refresh the index immediately after changes. If set to `true`, changes will be immediately searchable. If set to `false`, changes may not be immediately visible but can improve performance for bulk operations. If set to `wait_for`, changes will wait for the next refresh cycle to become visible. | `false` | Optional |
|
|
268
264
|
| `ENABLE_TRANSACTIONS_EXTENSIONS` | Enables or disables the Transactions and Bulk Transactions API extensions. If set to `false`, the POST `/collections` route and related transaction endpoints (including bulk transaction operations) will be unavailable in the API. This is useful for deployments where mutating the catalog via the API should be prevented. | `true` | Optional |
|
|
269
265
|
| `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
|
|
266
|
+
| `STAC_INDEX_ASSETS` | Controls if Assets are indexed when added to Elasticsearch/Opensearch. This allows asset fields to be included in search queries. | `false` | Optional |
|
|
267
|
+
| `ENV_MAX_LIMIT` | Configures the environment variable in SFEOS to override the default `MAX_LIMIT`, which controls the limit parameter for returned items and STAC collections. | `10,000` | Optional |
|
|
270
268
|
|
|
271
269
|
> [!NOTE]
|
|
272
270
|
> The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, `ES_VERIFY_CERTS` and `ES_TIMEOUT` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
|
|
@@ -66,26 +66,43 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
66
66
|
|
|
67
67
|
## Table of Contents
|
|
68
68
|
|
|
69
|
-
- [
|
|
70
|
-
- [
|
|
71
|
-
- [
|
|
72
|
-
- [
|
|
73
|
-
- [
|
|
74
|
-
- [
|
|
75
|
-
- [
|
|
76
|
-
- [
|
|
77
|
-
- [
|
|
78
|
-
- [
|
|
79
|
-
- [
|
|
80
|
-
- [
|
|
81
|
-
- [
|
|
82
|
-
- [
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- [
|
|
86
|
-
- [
|
|
87
|
-
- [
|
|
88
|
-
- [
|
|
69
|
+
- [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
|
|
70
|
+
- [Sponsors \& Supporters](#sponsors--supporters)
|
|
71
|
+
- [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
|
|
72
|
+
- [Common Deployment Patterns](#common-deployment-patterns)
|
|
73
|
+
- [Technologies](#technologies)
|
|
74
|
+
- [Table of Contents](#table-of-contents)
|
|
75
|
+
- [Documentation \& Resources](#documentation--resources)
|
|
76
|
+
- [Package Structure](#package-structure)
|
|
77
|
+
- [Examples](#examples)
|
|
78
|
+
- [Performance](#performance)
|
|
79
|
+
- [Direct Response Mode](#direct-response-mode)
|
|
80
|
+
- [Quick Start](#quick-start)
|
|
81
|
+
- [Installation](#installation)
|
|
82
|
+
- [Running Locally](#running-locally)
|
|
83
|
+
- [Using Pre-built Docker Images](#using-pre-built-docker-images)
|
|
84
|
+
- [Using Docker Compose](#using-docker-compose)
|
|
85
|
+
- [Configuration Reference](#configuration-reference)
|
|
86
|
+
- [Datetime-Based Index Management](#datetime-based-index-management)
|
|
87
|
+
- [Overview](#overview)
|
|
88
|
+
- [When to Use](#when-to-use)
|
|
89
|
+
- [Configuration](#configuration)
|
|
90
|
+
- [Enabling Datetime-Based Indexing](#enabling-datetime-based-indexing)
|
|
91
|
+
- [Related Configuration Variables](#related-configuration-variables)
|
|
92
|
+
- [How Datetime-Based Indexing Works](#how-datetime-based-indexing-works)
|
|
93
|
+
- [Index and Alias Naming Convention](#index-and-alias-naming-convention)
|
|
94
|
+
- [Index Size Management](#index-size-management)
|
|
95
|
+
- [Interacting with the API](#interacting-with-the-api)
|
|
96
|
+
- [Configure the API](#configure-the-api)
|
|
97
|
+
- [Collection Pagination](#collection-pagination)
|
|
98
|
+
- [Ingesting Sample Data CLI Tool](#ingesting-sample-data-cli-tool)
|
|
99
|
+
- [Elasticsearch Mappings](#elasticsearch-mappings)
|
|
100
|
+
- [Managing Elasticsearch Indices](#managing-elasticsearch-indices)
|
|
101
|
+
- [Snapshots](#snapshots)
|
|
102
|
+
- [Reindexing](#reindexing)
|
|
103
|
+
- [Auth](#auth)
|
|
104
|
+
- [Aggregation](#aggregation)
|
|
105
|
+
- [Rate Limiting](#rate-limiting)
|
|
89
106
|
|
|
90
107
|
## Documentation & Resources
|
|
91
108
|
|
|
@@ -228,6 +245,8 @@ You can customize additional settings in your `.env` file:
|
|
|
228
245
|
| `DATABASE_REFRESH` | Controls whether database operations refresh the index immediately after changes. If set to `true`, changes will be immediately searchable. If set to `false`, changes may not be immediately visible but can improve performance for bulk operations. If set to `wait_for`, changes will wait for the next refresh cycle to become visible. | `false` | Optional |
|
|
229
246
|
| `ENABLE_TRANSACTIONS_EXTENSIONS` | Enables or disables the Transactions and Bulk Transactions API extensions. If set to `false`, the POST `/collections` route and related transaction endpoints (including bulk transaction operations) will be unavailable in the API. This is useful for deployments where mutating the catalog via the API should be prevented. | `true` | Optional |
|
|
230
247
|
| `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
|
|
248
|
+
| `STAC_INDEX_ASSETS` | Controls if Assets are indexed when added to Elasticsearch/Opensearch. This allows asset fields to be included in search queries. | `false` | Optional |
|
|
249
|
+
| `ENV_MAX_LIMIT` | Configures the environment variable in SFEOS to override the default `MAX_LIMIT`, which controls the limit parameter for returned items and STAC collections. | `10,000` | Optional |
|
|
231
250
|
|
|
232
251
|
> [!NOTE]
|
|
233
252
|
> The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, `ES_VERIFY_CERTS` and `ES_TIMEOUT` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
|
|
@@ -284,86 +284,63 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
284
284
|
async def item_collection(
|
|
285
285
|
self,
|
|
286
286
|
collection_id: str,
|
|
287
|
+
request: Request,
|
|
287
288
|
bbox: Optional[BBox] = None,
|
|
288
289
|
datetime: Optional[str] = None,
|
|
289
290
|
limit: Optional[int] = None,
|
|
291
|
+
sortby: Optional[str] = None,
|
|
292
|
+
filter_expr: Optional[str] = None,
|
|
293
|
+
filter_lang: Optional[str] = None,
|
|
290
294
|
token: Optional[str] = None,
|
|
295
|
+
query: Optional[str] = None,
|
|
296
|
+
fields: Optional[List[str]] = None,
|
|
291
297
|
**kwargs,
|
|
292
298
|
) -> stac_types.ItemCollection:
|
|
293
|
-
"""
|
|
299
|
+
"""List items within a specific collection.
|
|
300
|
+
|
|
301
|
+
This endpoint delegates to ``get_search`` under the hood with
|
|
302
|
+
``collections=[collection_id]`` so that filtering, sorting and pagination
|
|
303
|
+
behave identically to the Search endpoints.
|
|
294
304
|
|
|
295
305
|
Args:
|
|
296
|
-
collection_id (str):
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
306
|
+
collection_id (str): ID of the collection to list items from.
|
|
307
|
+
request (Request): FastAPI Request object.
|
|
308
|
+
bbox (Optional[BBox]): Optional bounding box filter.
|
|
309
|
+
datetime (Optional[str]): Optional datetime or interval filter.
|
|
310
|
+
limit (Optional[int]): Optional page size. Defaults to env ``STAC_ITEM_LIMIT`` when unset.
|
|
311
|
+
sortby (Optional[str]): Optional sort specification. Accepts repeated values
|
|
312
|
+
like ``sortby=-properties.datetime`` or ``sortby=+id``. Bare fields (e.g. ``sortby=id``)
|
|
313
|
+
imply ascending order.
|
|
314
|
+
token (Optional[str]): Optional pagination token.
|
|
315
|
+
query (Optional[str]): Optional query string.
|
|
316
|
+
filter_expr (Optional[str]): Optional filter expression.
|
|
317
|
+
filter_lang (Optional[str]): Optional filter language.
|
|
318
|
+
fields (Optional[List[str]]): Fields to include or exclude from the results.
|
|
302
319
|
|
|
303
320
|
Returns:
|
|
304
|
-
ItemCollection:
|
|
305
|
-
the filter criteria and links to various resources.
|
|
321
|
+
ItemCollection: Feature collection with items, paging links, and counts.
|
|
306
322
|
|
|
307
323
|
Raises:
|
|
308
|
-
HTTPException:
|
|
309
|
-
Exception: If any error occurs while reading the items from the database.
|
|
324
|
+
HTTPException: 404 if the collection does not exist.
|
|
310
325
|
"""
|
|
311
|
-
request: Request = kwargs["request"]
|
|
312
|
-
token = request.query_params.get("token")
|
|
313
|
-
|
|
314
|
-
base_url = str(request.base_url)
|
|
315
|
-
|
|
316
|
-
collection = await self.get_collection(
|
|
317
|
-
collection_id=collection_id, request=request
|
|
318
|
-
)
|
|
319
|
-
collection_id = collection.get("id")
|
|
320
|
-
if collection_id is None:
|
|
321
|
-
raise HTTPException(status_code=404, detail="Collection not found")
|
|
322
|
-
|
|
323
|
-
search = self.database.make_search()
|
|
324
|
-
search = self.database.apply_collections_filter(
|
|
325
|
-
search=search, collection_ids=[collection_id]
|
|
326
|
-
)
|
|
327
|
-
|
|
328
326
|
try:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
332
|
-
except (ValueError, TypeError) as e:
|
|
333
|
-
# Handle invalid interval formats if return_date fails
|
|
334
|
-
msg = f"Invalid interval format: {datetime}, error: {e}"
|
|
335
|
-
logger.error(msg)
|
|
336
|
-
raise HTTPException(status_code=400, detail=msg)
|
|
337
|
-
|
|
338
|
-
if bbox:
|
|
339
|
-
bbox = [float(x) for x in bbox]
|
|
340
|
-
if len(bbox) == 6:
|
|
341
|
-
bbox = [bbox[0], bbox[1], bbox[3], bbox[4]]
|
|
342
|
-
|
|
343
|
-
search = self.database.apply_bbox_filter(search=search, bbox=bbox)
|
|
327
|
+
await self.get_collection(collection_id=collection_id, request=request)
|
|
328
|
+
except Exception:
|
|
329
|
+
raise HTTPException(status_code=404, detail="Collection not found")
|
|
344
330
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
331
|
+
# Delegate directly to GET search for consistency
|
|
332
|
+
return await self.get_search(
|
|
333
|
+
request=request,
|
|
334
|
+
collections=[collection_id],
|
|
335
|
+
bbox=bbox,
|
|
336
|
+
datetime=datetime,
|
|
348
337
|
limit=limit,
|
|
349
|
-
sort=None,
|
|
350
338
|
token=token,
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
self.item_serializer.db_to_stac(item, base_url=base_url) for item in items
|
|
357
|
-
]
|
|
358
|
-
|
|
359
|
-
links = await PagingLinks(request=request, next=next_token).get_links()
|
|
360
|
-
|
|
361
|
-
return stac_types.ItemCollection(
|
|
362
|
-
type="FeatureCollection",
|
|
363
|
-
features=items,
|
|
364
|
-
links=links,
|
|
365
|
-
numReturned=len(items),
|
|
366
|
-
numMatched=maybe_count,
|
|
339
|
+
sortby=sortby,
|
|
340
|
+
query=query,
|
|
341
|
+
filter_expr=filter_expr,
|
|
342
|
+
filter_lang=filter_lang,
|
|
343
|
+
fields=fields,
|
|
367
344
|
)
|
|
368
345
|
|
|
369
346
|
async def get_item(
|
|
@@ -429,6 +406,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
429
406
|
HTTPException: If any error occurs while searching the catalog.
|
|
430
407
|
"""
|
|
431
408
|
limit = int(request.query_params.get("limit", os.getenv("STAC_ITEM_LIMIT", 10)))
|
|
409
|
+
|
|
432
410
|
base_args = {
|
|
433
411
|
"collections": collections,
|
|
434
412
|
"ids": ids,
|
|
@@ -446,10 +424,18 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
446
424
|
base_args["intersects"] = orjson.loads(unquote_plus(intersects))
|
|
447
425
|
|
|
448
426
|
if sortby:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
427
|
+
parsed_sort = []
|
|
428
|
+
for raw in sortby:
|
|
429
|
+
if not isinstance(raw, str):
|
|
430
|
+
continue
|
|
431
|
+
s = raw.strip()
|
|
432
|
+
if not s:
|
|
433
|
+
continue
|
|
434
|
+
direction = "desc" if s[0] == "-" else "asc"
|
|
435
|
+
field = s[1:] if s and s[0] in "+-" else s
|
|
436
|
+
parsed_sort.append({"field": field, "direction": direction})
|
|
437
|
+
if parsed_sort:
|
|
438
|
+
base_args["sortby"] = parsed_sort
|
|
453
439
|
|
|
454
440
|
if filter_expr:
|
|
455
441
|
base_args["filter_lang"] = "cql2-json"
|
|
@@ -526,13 +512,15 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
526
512
|
|
|
527
513
|
search = self.database.apply_bbox_filter(search=search, bbox=bbox)
|
|
528
514
|
|
|
529
|
-
if search_request
|
|
515
|
+
if hasattr(search_request, "intersects") and getattr(
|
|
516
|
+
search_request, "intersects"
|
|
517
|
+
):
|
|
530
518
|
search = self.database.apply_intersects_filter(
|
|
531
|
-
search=search, intersects=search_request
|
|
519
|
+
search=search, intersects=getattr(search_request, "intersects")
|
|
532
520
|
)
|
|
533
521
|
|
|
534
|
-
if search_request
|
|
535
|
-
for field_name, expr in search_request
|
|
522
|
+
if hasattr(search_request, "query") and getattr(search_request, "query"):
|
|
523
|
+
for field_name, expr in getattr(search_request, "query").items():
|
|
536
524
|
field = "properties__" + field_name
|
|
537
525
|
for op, value in expr.items():
|
|
538
526
|
# Convert enum to string
|
|
@@ -541,9 +529,14 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
541
529
|
search=search, op=operator, field=field, value=value
|
|
542
530
|
)
|
|
543
531
|
|
|
544
|
-
#
|
|
532
|
+
# Apply CQL2 filter (support both 'filter_expr' and canonical 'filter')
|
|
533
|
+
cql2_filter = None
|
|
545
534
|
if hasattr(search_request, "filter_expr"):
|
|
546
535
|
cql2_filter = getattr(search_request, "filter_expr", None)
|
|
536
|
+
if cql2_filter is None and hasattr(search_request, "filter"):
|
|
537
|
+
cql2_filter = getattr(search_request, "filter", None)
|
|
538
|
+
|
|
539
|
+
if cql2_filter is not None:
|
|
547
540
|
try:
|
|
548
541
|
search = await self.database.apply_cql2_filter(search, cql2_filter)
|
|
549
542
|
except Exception as e:
|
|
@@ -561,19 +554,23 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
561
554
|
)
|
|
562
555
|
|
|
563
556
|
sort = None
|
|
564
|
-
if search_request
|
|
565
|
-
sort = self.database.populate_sort(search_request
|
|
557
|
+
if hasattr(search_request, "sortby") and getattr(search_request, "sortby"):
|
|
558
|
+
sort = self.database.populate_sort(getattr(search_request, "sortby"))
|
|
566
559
|
|
|
567
560
|
limit = 10
|
|
568
561
|
if search_request.limit:
|
|
569
562
|
limit = search_request.limit
|
|
570
563
|
|
|
564
|
+
# Use token from the request if the model doesn't define it
|
|
565
|
+
token_param = getattr(
|
|
566
|
+
search_request, "token", None
|
|
567
|
+
) or request.query_params.get("token")
|
|
571
568
|
items, maybe_count, next_token = await self.database.execute_search(
|
|
572
569
|
search=search,
|
|
573
570
|
limit=limit,
|
|
574
|
-
token=
|
|
571
|
+
token=token_param,
|
|
575
572
|
sort=sort,
|
|
576
|
-
collection_ids=search_request
|
|
573
|
+
collection_ids=getattr(search_request, "collections", None),
|
|
577
574
|
datetime_search=datetime_search,
|
|
578
575
|
)
|
|
579
576
|
|
|
@@ -917,7 +914,7 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
917
914
|
|
|
918
915
|
@attr.s
|
|
919
916
|
class BulkTransactionsClient(BaseBulkTransactionsClient):
|
|
920
|
-
"""A client for posting bulk transactions
|
|
917
|
+
"""A client for posting bulk transactions.
|
|
921
918
|
|
|
922
919
|
Attributes:
|
|
923
920
|
session: An instance of `Session` to use for database connection.
|
|
@@ -965,6 +962,13 @@ class BulkTransactionsClient(BaseBulkTransactionsClient):
|
|
|
965
962
|
A string indicating the number of items successfully added.
|
|
966
963
|
"""
|
|
967
964
|
request = kwargs.get("request")
|
|
965
|
+
|
|
966
|
+
if os.getenv("ENABLE_DATETIME_INDEX_FILTERING"):
|
|
967
|
+
raise HTTPException(
|
|
968
|
+
status_code=400,
|
|
969
|
+
detail="The /collections/{collection_id}/bulk_items endpoint is invalid when ENABLE_DATETIME_INDEX_FILTERING is set to true. Try using the /collections/{collection_id}/items endpoint.",
|
|
970
|
+
)
|
|
971
|
+
|
|
968
972
|
if request:
|
|
969
973
|
base_url = str(request.base_url)
|
|
970
974
|
else:
|
|
@@ -17,17 +17,20 @@ def format_datetime_range(date_str: str) -> str:
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
def normalize(dt):
|
|
20
|
+
"""Normalize datetime string and preserve millisecond precision."""
|
|
20
21
|
dt = dt.strip()
|
|
21
22
|
if not dt or dt == "..":
|
|
22
23
|
return ".."
|
|
23
24
|
dt_obj = rfc3339_str_to_datetime(dt)
|
|
24
25
|
dt_utc = dt_obj.astimezone(timezone.utc)
|
|
25
|
-
return dt_utc.
|
|
26
|
+
return dt_utc.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
|
26
27
|
|
|
27
28
|
if not isinstance(date_str, str):
|
|
28
29
|
return "../.."
|
|
30
|
+
|
|
29
31
|
if "/" not in date_str:
|
|
30
32
|
return f"{normalize(date_str)}/{normalize(date_str)}"
|
|
33
|
+
|
|
31
34
|
try:
|
|
32
35
|
start, end = date_str.split("/", 1)
|
|
33
36
|
except Exception:
|
|
@@ -9,6 +9,7 @@ from starlette.requests import Request
|
|
|
9
9
|
|
|
10
10
|
from stac_fastapi.core.datetime_utils import now_to_rfc3339_str
|
|
11
11
|
from stac_fastapi.core.models.links import CollectionLinks
|
|
12
|
+
from stac_fastapi.core.utilities import get_bool_env
|
|
12
13
|
from stac_fastapi.types import stac as stac_types
|
|
13
14
|
from stac_fastapi.types.links import ItemLinks, resolve_links
|
|
14
15
|
|
|
@@ -66,6 +67,11 @@ class ItemSerializer(Serializer):
|
|
|
66
67
|
item_links = resolve_links(stac_data.get("links", []), base_url)
|
|
67
68
|
stac_data["links"] = item_links
|
|
68
69
|
|
|
70
|
+
if get_bool_env("STAC_INDEX_ASSETS"):
|
|
71
|
+
stac_data["assets"] = [
|
|
72
|
+
{"es_key": k, **v} for k, v in stac_data.get("assets", {}).items()
|
|
73
|
+
]
|
|
74
|
+
|
|
69
75
|
now = now_to_rfc3339_str()
|
|
70
76
|
if "created" not in stac_data["properties"]:
|
|
71
77
|
stac_data["properties"]["created"] = now
|
|
@@ -93,6 +99,12 @@ class ItemSerializer(Serializer):
|
|
|
93
99
|
if original_links:
|
|
94
100
|
item_links += resolve_links(original_links, base_url)
|
|
95
101
|
|
|
102
|
+
if get_bool_env("STAC_INDEX_ASSETS"):
|
|
103
|
+
assets = {a.pop("es_key"): a for a in item.get("assets", [])}
|
|
104
|
+
|
|
105
|
+
else:
|
|
106
|
+
assets = item.get("assets", {})
|
|
107
|
+
|
|
96
108
|
return stac_types.Item(
|
|
97
109
|
type="Feature",
|
|
98
110
|
stac_version=item.get("stac_version", ""),
|
|
@@ -103,7 +115,7 @@ class ItemSerializer(Serializer):
|
|
|
103
115
|
bbox=item.get("bbox", []),
|
|
104
116
|
properties=item.get("properties", {}),
|
|
105
117
|
links=item_links,
|
|
106
|
-
assets=
|
|
118
|
+
assets=assets,
|
|
107
119
|
)
|
|
108
120
|
|
|
109
121
|
|
|
@@ -128,6 +140,15 @@ class CollectionSerializer(Serializer):
|
|
|
128
140
|
collection["links"] = resolve_links(
|
|
129
141
|
collection.get("links", []), str(request.base_url)
|
|
130
142
|
)
|
|
143
|
+
|
|
144
|
+
if get_bool_env("STAC_INDEX_ASSETS"):
|
|
145
|
+
collection["assets"] = [
|
|
146
|
+
{"es_key": k, **v} for k, v in collection.get("assets", {}).items()
|
|
147
|
+
]
|
|
148
|
+
collection["item_assets"] = [
|
|
149
|
+
{"es_key": k, **v} for k, v in collection.get("item_assets", {}).items()
|
|
150
|
+
]
|
|
151
|
+
|
|
131
152
|
return collection
|
|
132
153
|
|
|
133
154
|
@classmethod
|
|
@@ -174,5 +195,18 @@ class CollectionSerializer(Serializer):
|
|
|
174
195
|
collection_links += resolve_links(original_links, str(request.base_url))
|
|
175
196
|
collection["links"] = collection_links
|
|
176
197
|
|
|
198
|
+
if get_bool_env("STAC_INDEX_ASSETS"):
|
|
199
|
+
collection["assets"] = {
|
|
200
|
+
a.pop("es_key"): a for a in collection.get("assets", [])
|
|
201
|
+
}
|
|
202
|
+
collection["item_assets"] = {
|
|
203
|
+
i.pop("es_key"): i for i in collection.get("item_assets", [])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
else:
|
|
207
|
+
collection["assets"] = collection.get("assets", {})
|
|
208
|
+
if item_assets := collection.get("item_assets"):
|
|
209
|
+
collection["item_assets"] = item_assets
|
|
210
|
+
|
|
177
211
|
# Return the stac_types.Collection object
|
|
178
212
|
return stac_types.Collection(**collection)
|
|
@@ -10,7 +10,15 @@ from typing import Any, Dict, List, Optional, Set, Union
|
|
|
10
10
|
|
|
11
11
|
from stac_fastapi.types.stac import Item
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
def get_max_limit():
|
|
15
|
+
"""
|
|
16
|
+
Retrieve a MAX_LIMIT value from an environment variable.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
int: The int value parsed from the environment variable.
|
|
20
|
+
"""
|
|
21
|
+
return int(os.getenv("ENV_MAX_LIMIT", 10000))
|
|
14
22
|
|
|
15
23
|
|
|
16
24
|
def get_bool_env(name: str, default: Union[bool, str] = False) -> bool:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""library version."""
|
|
2
|
-
__version__ = "6.
|
|
2
|
+
__version__ = "6.3.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name:
|
|
3
|
-
Version: 6.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: stac-fastapi-core
|
|
3
|
+
Version: 6.3.0
|
|
4
4
|
Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
|
|
5
5
|
Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
6
|
License: MIT
|
|
@@ -15,27 +15,6 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: fastapi~=0.109.0
|
|
19
|
-
Requires-Dist: attrs>=23.2.0
|
|
20
|
-
Requires-Dist: pydantic<3.0.0,>=2.4.1
|
|
21
|
-
Requires-Dist: stac_pydantic~=3.3.0
|
|
22
|
-
Requires-Dist: stac-fastapi.types==6.0.0
|
|
23
|
-
Requires-Dist: stac-fastapi.api==6.0.0
|
|
24
|
-
Requires-Dist: stac-fastapi.extensions==6.0.0
|
|
25
|
-
Requires-Dist: orjson~=3.9.0
|
|
26
|
-
Requires-Dist: overrides~=7.4.0
|
|
27
|
-
Requires-Dist: geojson-pydantic~=1.0.0
|
|
28
|
-
Requires-Dist: pygeofilter~=0.3.1
|
|
29
|
-
Requires-Dist: jsonschema~=4.0.0
|
|
30
|
-
Requires-Dist: slowapi~=0.1.9
|
|
31
|
-
Dynamic: classifier
|
|
32
|
-
Dynamic: description
|
|
33
|
-
Dynamic: description-content-type
|
|
34
|
-
Dynamic: home-page
|
|
35
|
-
Dynamic: license
|
|
36
|
-
Dynamic: requires-dist
|
|
37
|
-
Dynamic: requires-python
|
|
38
|
-
Dynamic: summary
|
|
39
18
|
|
|
40
19
|
# stac-fastapi-elasticsearch-opensearch
|
|
41
20
|
|
|
@@ -105,26 +84,43 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
105
84
|
|
|
106
85
|
## Table of Contents
|
|
107
86
|
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
112
|
-
- [
|
|
113
|
-
- [
|
|
114
|
-
- [
|
|
115
|
-
- [
|
|
116
|
-
- [
|
|
117
|
-
- [
|
|
118
|
-
- [
|
|
119
|
-
- [
|
|
120
|
-
- [
|
|
121
|
-
- [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- [
|
|
125
|
-
- [
|
|
126
|
-
- [
|
|
127
|
-
- [
|
|
87
|
+
- [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
|
|
88
|
+
- [Sponsors \& Supporters](#sponsors--supporters)
|
|
89
|
+
- [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
|
|
90
|
+
- [Common Deployment Patterns](#common-deployment-patterns)
|
|
91
|
+
- [Technologies](#technologies)
|
|
92
|
+
- [Table of Contents](#table-of-contents)
|
|
93
|
+
- [Documentation \& Resources](#documentation--resources)
|
|
94
|
+
- [Package Structure](#package-structure)
|
|
95
|
+
- [Examples](#examples)
|
|
96
|
+
- [Performance](#performance)
|
|
97
|
+
- [Direct Response Mode](#direct-response-mode)
|
|
98
|
+
- [Quick Start](#quick-start)
|
|
99
|
+
- [Installation](#installation)
|
|
100
|
+
- [Running Locally](#running-locally)
|
|
101
|
+
- [Using Pre-built Docker Images](#using-pre-built-docker-images)
|
|
102
|
+
- [Using Docker Compose](#using-docker-compose)
|
|
103
|
+
- [Configuration Reference](#configuration-reference)
|
|
104
|
+
- [Datetime-Based Index Management](#datetime-based-index-management)
|
|
105
|
+
- [Overview](#overview)
|
|
106
|
+
- [When to Use](#when-to-use)
|
|
107
|
+
- [Configuration](#configuration)
|
|
108
|
+
- [Enabling Datetime-Based Indexing](#enabling-datetime-based-indexing)
|
|
109
|
+
- [Related Configuration Variables](#related-configuration-variables)
|
|
110
|
+
- [How Datetime-Based Indexing Works](#how-datetime-based-indexing-works)
|
|
111
|
+
- [Index and Alias Naming Convention](#index-and-alias-naming-convention)
|
|
112
|
+
- [Index Size Management](#index-size-management)
|
|
113
|
+
- [Interacting with the API](#interacting-with-the-api)
|
|
114
|
+
- [Configure the API](#configure-the-api)
|
|
115
|
+
- [Collection Pagination](#collection-pagination)
|
|
116
|
+
- [Ingesting Sample Data CLI Tool](#ingesting-sample-data-cli-tool)
|
|
117
|
+
- [Elasticsearch Mappings](#elasticsearch-mappings)
|
|
118
|
+
- [Managing Elasticsearch Indices](#managing-elasticsearch-indices)
|
|
119
|
+
- [Snapshots](#snapshots)
|
|
120
|
+
- [Reindexing](#reindexing)
|
|
121
|
+
- [Auth](#auth)
|
|
122
|
+
- [Aggregation](#aggregation)
|
|
123
|
+
- [Rate Limiting](#rate-limiting)
|
|
128
124
|
|
|
129
125
|
## Documentation & Resources
|
|
130
126
|
|
|
@@ -267,6 +263,8 @@ You can customize additional settings in your `.env` file:
|
|
|
267
263
|
| `DATABASE_REFRESH` | Controls whether database operations refresh the index immediately after changes. If set to `true`, changes will be immediately searchable. If set to `false`, changes may not be immediately visible but can improve performance for bulk operations. If set to `wait_for`, changes will wait for the next refresh cycle to become visible. | `false` | Optional |
|
|
268
264
|
| `ENABLE_TRANSACTIONS_EXTENSIONS` | Enables or disables the Transactions and Bulk Transactions API extensions. If set to `false`, the POST `/collections` route and related transaction endpoints (including bulk transaction operations) will be unavailable in the API. This is useful for deployments where mutating the catalog via the API should be prevented. | `true` | Optional |
|
|
269
265
|
| `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
|
|
266
|
+
| `STAC_INDEX_ASSETS` | Controls if Assets are indexed when added to Elasticsearch/Opensearch. This allows asset fields to be included in search queries. | `false` | Optional |
|
|
267
|
+
| `ENV_MAX_LIMIT` | Configures the environment variable in SFEOS to override the default `MAX_LIMIT`, which controls the limit parameter for returned items and STAC collections. | `10,000` | Optional |
|
|
270
268
|
|
|
271
269
|
> [!NOTE]
|
|
272
270
|
> The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, `ES_VERIFY_CERTS` and `ES_TIMEOUT` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/base_database_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/__init__.py
RENAMED
|
File without changes
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi/core/extensions/aggregation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.3.0}/stac_fastapi_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|