stac-fastapi-core 6.2.1__tar.gz → 6.4.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.4.0}/PKG-INFO +80 -45
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/README.md +78 -22
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/base_database_logic.py +18 -4
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/core.py +133 -82
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/datetime_utils.py +4 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/filter.py +0 -18
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/models/links.py +1 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/serializers.py +35 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/utilities.py +9 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/version.py +1 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/PKG-INFO +81 -46
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/top_level.txt +0 -1
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/setup.cfg +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/setup.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/__init__.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/base_settings.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/basic_auth.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/__init__.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/aggregation.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/fields.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/query.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/models/__init__.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/models/search.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/rate_limit.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/route_dependencies.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/session.py +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/not-zip-safe +0 -0
- {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.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.4.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
|
|
|
@@ -75,11 +54,10 @@ SFEOS (stac-fastapi-elasticsearch-opensearch) is a high-performance, scalable AP
|
|
|
75
54
|
- **Scale to millions of geospatial assets** with fast search performance through optimized spatial indexing and query capabilities
|
|
76
55
|
- **Support OGC-compliant filtering** including spatial operations (intersects, contains, etc.) and temporal queries
|
|
77
56
|
- **Perform geospatial aggregations** to analyze data distribution across space and time
|
|
57
|
+
- **Enhanced collection search capabilities** with support for sorting and field selection
|
|
78
58
|
|
|
79
59
|
This implementation builds on the STAC-FastAPI framework, providing a production-ready solution specifically optimized for Elasticsearch and OpenSearch databases. It's ideal for organizations managing large geospatial data catalogs who need efficient discovery and access capabilities through standardized APIs.
|
|
80
60
|
|
|
81
|
-
|
|
82
|
-
|
|
83
61
|
## Common Deployment Patterns
|
|
84
62
|
|
|
85
63
|
stac-fastapi-elasticsearch-opensearch can be deployed in several ways depending on your needs:
|
|
@@ -105,26 +83,44 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
105
83
|
|
|
106
84
|
## Table of Contents
|
|
107
85
|
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
112
|
-
- [
|
|
113
|
-
- [
|
|
114
|
-
- [
|
|
115
|
-
- [
|
|
116
|
-
- [
|
|
117
|
-
- [
|
|
118
|
-
- [
|
|
119
|
-
- [
|
|
120
|
-
- [
|
|
121
|
-
- [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- [
|
|
125
|
-
- [
|
|
126
|
-
- [
|
|
127
|
-
- [
|
|
86
|
+
- [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
|
|
87
|
+
- [Sponsors \& Supporters](#sponsors--supporters)
|
|
88
|
+
- [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
|
|
89
|
+
- [Common Deployment Patterns](#common-deployment-patterns)
|
|
90
|
+
- [Technologies](#technologies)
|
|
91
|
+
- [Table of Contents](#table-of-contents)
|
|
92
|
+
- [Collection Search Extensions](#collection-search-extensions)
|
|
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
|
|
|
@@ -135,6 +131,37 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
135
131
|
- [Gitter Chat](https://app.gitter.im/#/room/#stac-fastapi-elasticsearch_community:gitter.im) - For real-time discussions
|
|
136
132
|
- [GitHub Discussions](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/discussions) - For longer-form questions and answers
|
|
137
133
|
|
|
134
|
+
## Collection Search Extensions
|
|
135
|
+
|
|
136
|
+
SFEOS implements extended capabilities for the `/collections` endpoint, allowing for more powerful collection discovery:
|
|
137
|
+
|
|
138
|
+
- **Sorting**: Sort collections by sortable fields using the `sortby` parameter
|
|
139
|
+
- Example: `/collections?sortby=+id` (ascending sort by ID)
|
|
140
|
+
- Example: `/collections?sortby=-id` (descending sort by ID)
|
|
141
|
+
- Example: `/collections?sortby=-temporal` (descending sort by temporal extent)
|
|
142
|
+
|
|
143
|
+
- **Field Selection**: Request only specific fields to be returned using the `fields` parameter
|
|
144
|
+
- Example: `/collections?fields=id,title,description`
|
|
145
|
+
- This helps reduce payload size when only certain fields are needed
|
|
146
|
+
|
|
147
|
+
- **Free Text Search**: Search across collection text fields using the `q` parameter
|
|
148
|
+
- Example: `/collections?q=landsat`
|
|
149
|
+
- Searches across multiple text fields including title, description, and keywords
|
|
150
|
+
- Supports partial word matching and relevance-based sorting
|
|
151
|
+
|
|
152
|
+
These extensions make it easier to build user interfaces that display and navigate through collections efficiently.
|
|
153
|
+
|
|
154
|
+
> **Configuration**: Collection search extensions can be disabled by setting the `ENABLE_COLLECTIONS_SEARCH` environment variable to `false`. By default, these extensions are enabled.
|
|
155
|
+
|
|
156
|
+
> **Note**: Sorting is only available on fields that are indexed for sorting in Elasticsearch/OpenSearch. With the default mappings, you can sort on:
|
|
157
|
+
> - `id` (keyword field)
|
|
158
|
+
> - `extent.temporal.interval` (date field)
|
|
159
|
+
> - `temporal` (alias to extent.temporal.interval)
|
|
160
|
+
>
|
|
161
|
+
> Text fields like `title` and `description` are not sortable by default as they use text analysis for better search capabilities. Attempting to sort on these fields will result in a user-friendly error message explaining which fields are sortable and how to make additional fields sortable by updating the mappings.
|
|
162
|
+
>
|
|
163
|
+
> **Important**: Adding keyword fields to make text fields sortable can significantly increase the index size, especially for large text fields. Consider the storage implications when deciding which fields to make sortable.
|
|
164
|
+
|
|
138
165
|
## Package Structure
|
|
139
166
|
|
|
140
167
|
This project is organized into several packages, each with a specific purpose:
|
|
@@ -265,8 +292,12 @@ You can customize additional settings in your `.env` file:
|
|
|
265
292
|
| `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
|
|
266
293
|
| `RAISE_ON_BULK_ERROR` | Controls whether bulk insert operations raise exceptions on errors. If set to `true`, the operation will stop and raise an exception when an error occurs. If set to `false`, errors will be logged, and the operation will continue. **Note:** STAC Item and ItemCollection validation errors will always raise, regardless of this flag. | `false` | Optional |
|
|
267
294
|
| `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 |
|
|
295
|
+
| `ENABLE_COLLECTIONS_SEARCH` | Enable collection search extensions (sort, fields). | `true` | Optional |
|
|
268
296
|
| `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
297
|
| `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
|
|
298
|
+
| `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 |
|
|
299
|
+
| `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 |
|
|
300
|
+
| `USE_DATETIME` | Configures the datetime search behavior in SFEOS. When enabled, searches both datetime field and falls back to start_datetime/end_datetime range for items with null datetime. When disabled, searches only by start_datetime/end_datetime range. | True | Optional |
|
|
270
301
|
|
|
271
302
|
> [!NOTE]
|
|
272
303
|
> 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.
|
|
@@ -408,6 +439,10 @@ The system uses a precise naming convention:
|
|
|
408
439
|
- **Root Path Configuration**: The application root path is the base URL by default.
|
|
409
440
|
- For AWS Lambda with Gateway API: Set `STAC_FASTAPI_ROOT_PATH` to match the Gateway API stage name (e.g., `/v1`)
|
|
410
441
|
|
|
442
|
+
- **Feature Configuration**: Control which features are enabled:
|
|
443
|
+
- `ENABLE_COLLECTIONS_SEARCH`: Set to `true` (default) to enable collection search extensions (sort, fields). Set to `false` to disable.
|
|
444
|
+
- `ENABLE_TRANSACTIONS_EXTENSIONS`: Set to `true` (default) to enable transaction extensions. Set to `false` to disable.
|
|
445
|
+
|
|
411
446
|
|
|
412
447
|
## Collection Pagination
|
|
413
448
|
|
|
@@ -36,11 +36,10 @@ SFEOS (stac-fastapi-elasticsearch-opensearch) is a high-performance, scalable AP
|
|
|
36
36
|
- **Scale to millions of geospatial assets** with fast search performance through optimized spatial indexing and query capabilities
|
|
37
37
|
- **Support OGC-compliant filtering** including spatial operations (intersects, contains, etc.) and temporal queries
|
|
38
38
|
- **Perform geospatial aggregations** to analyze data distribution across space and time
|
|
39
|
+
- **Enhanced collection search capabilities** with support for sorting and field selection
|
|
39
40
|
|
|
40
41
|
This implementation builds on the STAC-FastAPI framework, providing a production-ready solution specifically optimized for Elasticsearch and OpenSearch databases. It's ideal for organizations managing large geospatial data catalogs who need efficient discovery and access capabilities through standardized APIs.
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
43
|
## Common Deployment Patterns
|
|
45
44
|
|
|
46
45
|
stac-fastapi-elasticsearch-opensearch can be deployed in several ways depending on your needs:
|
|
@@ -66,26 +65,44 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
66
65
|
|
|
67
66
|
## Table of Contents
|
|
68
67
|
|
|
69
|
-
- [
|
|
70
|
-
- [
|
|
71
|
-
- [
|
|
72
|
-
- [
|
|
73
|
-
- [
|
|
74
|
-
- [
|
|
75
|
-
- [
|
|
76
|
-
- [
|
|
77
|
-
- [
|
|
78
|
-
- [
|
|
79
|
-
- [
|
|
80
|
-
- [
|
|
81
|
-
- [
|
|
82
|
-
- [
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- [
|
|
86
|
-
- [
|
|
87
|
-
- [
|
|
88
|
-
- [
|
|
68
|
+
- [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
|
|
69
|
+
- [Sponsors \& Supporters](#sponsors--supporters)
|
|
70
|
+
- [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
|
|
71
|
+
- [Common Deployment Patterns](#common-deployment-patterns)
|
|
72
|
+
- [Technologies](#technologies)
|
|
73
|
+
- [Table of Contents](#table-of-contents)
|
|
74
|
+
- [Collection Search Extensions](#collection-search-extensions)
|
|
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
|
|
|
@@ -96,6 +113,37 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
96
113
|
- [Gitter Chat](https://app.gitter.im/#/room/#stac-fastapi-elasticsearch_community:gitter.im) - For real-time discussions
|
|
97
114
|
- [GitHub Discussions](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/discussions) - For longer-form questions and answers
|
|
98
115
|
|
|
116
|
+
## Collection Search Extensions
|
|
117
|
+
|
|
118
|
+
SFEOS implements extended capabilities for the `/collections` endpoint, allowing for more powerful collection discovery:
|
|
119
|
+
|
|
120
|
+
- **Sorting**: Sort collections by sortable fields using the `sortby` parameter
|
|
121
|
+
- Example: `/collections?sortby=+id` (ascending sort by ID)
|
|
122
|
+
- Example: `/collections?sortby=-id` (descending sort by ID)
|
|
123
|
+
- Example: `/collections?sortby=-temporal` (descending sort by temporal extent)
|
|
124
|
+
|
|
125
|
+
- **Field Selection**: Request only specific fields to be returned using the `fields` parameter
|
|
126
|
+
- Example: `/collections?fields=id,title,description`
|
|
127
|
+
- This helps reduce payload size when only certain fields are needed
|
|
128
|
+
|
|
129
|
+
- **Free Text Search**: Search across collection text fields using the `q` parameter
|
|
130
|
+
- Example: `/collections?q=landsat`
|
|
131
|
+
- Searches across multiple text fields including title, description, and keywords
|
|
132
|
+
- Supports partial word matching and relevance-based sorting
|
|
133
|
+
|
|
134
|
+
These extensions make it easier to build user interfaces that display and navigate through collections efficiently.
|
|
135
|
+
|
|
136
|
+
> **Configuration**: Collection search extensions can be disabled by setting the `ENABLE_COLLECTIONS_SEARCH` environment variable to `false`. By default, these extensions are enabled.
|
|
137
|
+
|
|
138
|
+
> **Note**: Sorting is only available on fields that are indexed for sorting in Elasticsearch/OpenSearch. With the default mappings, you can sort on:
|
|
139
|
+
> - `id` (keyword field)
|
|
140
|
+
> - `extent.temporal.interval` (date field)
|
|
141
|
+
> - `temporal` (alias to extent.temporal.interval)
|
|
142
|
+
>
|
|
143
|
+
> Text fields like `title` and `description` are not sortable by default as they use text analysis for better search capabilities. Attempting to sort on these fields will result in a user-friendly error message explaining which fields are sortable and how to make additional fields sortable by updating the mappings.
|
|
144
|
+
>
|
|
145
|
+
> **Important**: Adding keyword fields to make text fields sortable can significantly increase the index size, especially for large text fields. Consider the storage implications when deciding which fields to make sortable.
|
|
146
|
+
|
|
99
147
|
## Package Structure
|
|
100
148
|
|
|
101
149
|
This project is organized into several packages, each with a specific purpose:
|
|
@@ -226,8 +274,12 @@ You can customize additional settings in your `.env` file:
|
|
|
226
274
|
| `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
|
|
227
275
|
| `RAISE_ON_BULK_ERROR` | Controls whether bulk insert operations raise exceptions on errors. If set to `true`, the operation will stop and raise an exception when an error occurs. If set to `false`, errors will be logged, and the operation will continue. **Note:** STAC Item and ItemCollection validation errors will always raise, regardless of this flag. | `false` | Optional |
|
|
228
276
|
| `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 |
|
|
277
|
+
| `ENABLE_COLLECTIONS_SEARCH` | Enable collection search extensions (sort, fields). | `true` | Optional |
|
|
229
278
|
| `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
279
|
| `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
|
|
280
|
+
| `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 |
|
|
281
|
+
| `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 |
|
|
282
|
+
| `USE_DATETIME` | Configures the datetime search behavior in SFEOS. When enabled, searches both datetime field and falls back to start_datetime/end_datetime range for items with null datetime. When disabled, searches only by start_datetime/end_datetime range. | True | Optional |
|
|
231
283
|
|
|
232
284
|
> [!NOTE]
|
|
233
285
|
> 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.
|
|
@@ -369,6 +421,10 @@ The system uses a precise naming convention:
|
|
|
369
421
|
- **Root Path Configuration**: The application root path is the base URL by default.
|
|
370
422
|
- For AWS Lambda with Gateway API: Set `STAC_FASTAPI_ROOT_PATH` to match the Gateway API stage name (e.g., `/v1`)
|
|
371
423
|
|
|
424
|
+
- **Feature Configuration**: Control which features are enabled:
|
|
425
|
+
- `ENABLE_COLLECTIONS_SEARCH`: Set to `true` (default) to enable collection search extensions (sort, fields). Set to `false` to disable.
|
|
426
|
+
- `ENABLE_TRANSACTIONS_EXTENSIONS`: Set to `true` (default) to enable transaction extensions. Set to `false` to disable.
|
|
427
|
+
|
|
372
428
|
|
|
373
429
|
## Collection Pagination
|
|
374
430
|
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/base_database_logic.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Base database logic."""
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
|
-
from typing import Any, Dict, Iterable, List, Optional
|
|
4
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class BaseDatabaseLogic(abc.ABC):
|
|
@@ -14,9 +14,23 @@ class BaseDatabaseLogic(abc.ABC):
|
|
|
14
14
|
|
|
15
15
|
@abc.abstractmethod
|
|
16
16
|
async def get_all_collections(
|
|
17
|
-
self,
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
self,
|
|
18
|
+
token: Optional[str],
|
|
19
|
+
limit: int,
|
|
20
|
+
request: Any = None,
|
|
21
|
+
sort: Optional[List[Dict[str, Any]]] = None,
|
|
22
|
+
) -> Tuple[List[Dict[str, Any]], Optional[str]]:
|
|
23
|
+
"""Retrieve a list of collections from the database, supporting pagination.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
token (Optional[str]): The pagination token.
|
|
27
|
+
limit (int): The number of results to return.
|
|
28
|
+
request (Any, optional): The FastAPI request object. Defaults to None.
|
|
29
|
+
sort (Optional[List[Dict[str, Any]]], optional): Optional sort parameter. Defaults to None.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
A tuple of (collections, next pagination token if any).
|
|
33
|
+
"""
|
|
20
34
|
pass
|
|
21
35
|
|
|
22
36
|
@abc.abstractmethod
|
|
@@ -224,10 +224,19 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
224
224
|
|
|
225
225
|
return landing_page
|
|
226
226
|
|
|
227
|
-
async def all_collections(
|
|
227
|
+
async def all_collections(
|
|
228
|
+
self,
|
|
229
|
+
fields: Optional[List[str]] = None,
|
|
230
|
+
sortby: Optional[str] = None,
|
|
231
|
+
q: Optional[Union[str, List[str]]] = None,
|
|
232
|
+
**kwargs,
|
|
233
|
+
) -> stac_types.Collections:
|
|
228
234
|
"""Read all collections from the database.
|
|
229
235
|
|
|
230
236
|
Args:
|
|
237
|
+
fields (Optional[List[str]]): Fields to include or exclude from the results.
|
|
238
|
+
sortby (Optional[str]): Sorting options for the results.
|
|
239
|
+
q (Optional[List[str]]): Free text search terms.
|
|
231
240
|
**kwargs: Keyword arguments from the request.
|
|
232
241
|
|
|
233
242
|
Returns:
|
|
@@ -238,10 +247,48 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
238
247
|
limit = int(request.query_params.get("limit", os.getenv("STAC_ITEM_LIMIT", 10)))
|
|
239
248
|
token = request.query_params.get("token")
|
|
240
249
|
|
|
250
|
+
# Process fields parameter for filtering collection properties
|
|
251
|
+
includes, excludes = set(), set()
|
|
252
|
+
if fields and self.extension_is_enabled("FieldsExtension"):
|
|
253
|
+
for field in fields:
|
|
254
|
+
if field[0] == "-":
|
|
255
|
+
excludes.add(field[1:])
|
|
256
|
+
else:
|
|
257
|
+
includes.add(field[1:] if field[0] in "+ " else field)
|
|
258
|
+
|
|
259
|
+
sort = None
|
|
260
|
+
if sortby:
|
|
261
|
+
parsed_sort = []
|
|
262
|
+
for raw in sortby:
|
|
263
|
+
if not isinstance(raw, str):
|
|
264
|
+
continue
|
|
265
|
+
s = raw.strip()
|
|
266
|
+
if not s:
|
|
267
|
+
continue
|
|
268
|
+
direction = "desc" if s[0] == "-" else "asc"
|
|
269
|
+
field = s[1:] if s and s[0] in "+-" else s
|
|
270
|
+
parsed_sort.append({"field": field, "direction": direction})
|
|
271
|
+
if parsed_sort:
|
|
272
|
+
sort = parsed_sort
|
|
273
|
+
|
|
274
|
+
# Convert q to a list if it's a string
|
|
275
|
+
q_list = None
|
|
276
|
+
if q is not None:
|
|
277
|
+
q_list = [q] if isinstance(q, str) else q
|
|
278
|
+
|
|
241
279
|
collections, next_token = await self.database.get_all_collections(
|
|
242
|
-
token=token, limit=limit, request=request
|
|
280
|
+
token=token, limit=limit, request=request, sort=sort, q=q_list
|
|
243
281
|
)
|
|
244
282
|
|
|
283
|
+
# Apply field filtering if fields parameter was provided
|
|
284
|
+
if fields and self.extension_is_enabled("FieldsExtension"):
|
|
285
|
+
filtered_collections = [
|
|
286
|
+
filter_fields(collection, includes, excludes)
|
|
287
|
+
for collection in collections
|
|
288
|
+
]
|
|
289
|
+
else:
|
|
290
|
+
filtered_collections = collections
|
|
291
|
+
|
|
245
292
|
links = [
|
|
246
293
|
{"rel": Relations.root.value, "type": MimeTypes.json, "href": base_url},
|
|
247
294
|
{"rel": Relations.parent.value, "type": MimeTypes.json, "href": base_url},
|
|
@@ -256,7 +303,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
256
303
|
next_link = PagingLinks(next=next_token, request=request).link_next()
|
|
257
304
|
links.append(next_link)
|
|
258
305
|
|
|
259
|
-
return stac_types.Collections(collections=
|
|
306
|
+
return stac_types.Collections(collections=filtered_collections, links=links)
|
|
260
307
|
|
|
261
308
|
async def get_collection(
|
|
262
309
|
self, collection_id: str, **kwargs
|
|
@@ -284,86 +331,63 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
284
331
|
async def item_collection(
|
|
285
332
|
self,
|
|
286
333
|
collection_id: str,
|
|
334
|
+
request: Request,
|
|
287
335
|
bbox: Optional[BBox] = None,
|
|
288
336
|
datetime: Optional[str] = None,
|
|
289
337
|
limit: Optional[int] = None,
|
|
338
|
+
sortby: Optional[str] = None,
|
|
339
|
+
filter_expr: Optional[str] = None,
|
|
340
|
+
filter_lang: Optional[str] = None,
|
|
290
341
|
token: Optional[str] = None,
|
|
342
|
+
query: Optional[str] = None,
|
|
343
|
+
fields: Optional[List[str]] = None,
|
|
291
344
|
**kwargs,
|
|
292
345
|
) -> stac_types.ItemCollection:
|
|
293
|
-
"""
|
|
346
|
+
"""List items within a specific collection.
|
|
347
|
+
|
|
348
|
+
This endpoint delegates to ``get_search`` under the hood with
|
|
349
|
+
``collections=[collection_id]`` so that filtering, sorting and pagination
|
|
350
|
+
behave identically to the Search endpoints.
|
|
294
351
|
|
|
295
352
|
Args:
|
|
296
|
-
collection_id (str):
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
353
|
+
collection_id (str): ID of the collection to list items from.
|
|
354
|
+
request (Request): FastAPI Request object.
|
|
355
|
+
bbox (Optional[BBox]): Optional bounding box filter.
|
|
356
|
+
datetime (Optional[str]): Optional datetime or interval filter.
|
|
357
|
+
limit (Optional[int]): Optional page size. Defaults to env ``STAC_ITEM_LIMIT`` when unset.
|
|
358
|
+
sortby (Optional[str]): Optional sort specification. Accepts repeated values
|
|
359
|
+
like ``sortby=-properties.datetime`` or ``sortby=+id``. Bare fields (e.g. ``sortby=id``)
|
|
360
|
+
imply ascending order.
|
|
361
|
+
token (Optional[str]): Optional pagination token.
|
|
362
|
+
query (Optional[str]): Optional query string.
|
|
363
|
+
filter_expr (Optional[str]): Optional filter expression.
|
|
364
|
+
filter_lang (Optional[str]): Optional filter language.
|
|
365
|
+
fields (Optional[List[str]]): Fields to include or exclude from the results.
|
|
302
366
|
|
|
303
367
|
Returns:
|
|
304
|
-
ItemCollection:
|
|
305
|
-
the filter criteria and links to various resources.
|
|
368
|
+
ItemCollection: Feature collection with items, paging links, and counts.
|
|
306
369
|
|
|
307
370
|
Raises:
|
|
308
|
-
HTTPException:
|
|
309
|
-
Exception: If any error occurs while reading the items from the database.
|
|
371
|
+
HTTPException: 404 if the collection does not exist.
|
|
310
372
|
"""
|
|
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
373
|
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)
|
|
374
|
+
await self.get_collection(collection_id=collection_id, request=request)
|
|
375
|
+
except Exception:
|
|
376
|
+
raise HTTPException(status_code=404, detail="Collection not found")
|
|
344
377
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
378
|
+
# Delegate directly to GET search for consistency
|
|
379
|
+
return await self.get_search(
|
|
380
|
+
request=request,
|
|
381
|
+
collections=[collection_id],
|
|
382
|
+
bbox=bbox,
|
|
383
|
+
datetime=datetime,
|
|
348
384
|
limit=limit,
|
|
349
|
-
sort=None,
|
|
350
385
|
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,
|
|
386
|
+
sortby=sortby,
|
|
387
|
+
query=query,
|
|
388
|
+
filter_expr=filter_expr,
|
|
389
|
+
filter_lang=filter_lang,
|
|
390
|
+
fields=fields,
|
|
367
391
|
)
|
|
368
392
|
|
|
369
393
|
async def get_item(
|
|
@@ -429,6 +453,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
429
453
|
HTTPException: If any error occurs while searching the catalog.
|
|
430
454
|
"""
|
|
431
455
|
limit = int(request.query_params.get("limit", os.getenv("STAC_ITEM_LIMIT", 10)))
|
|
456
|
+
|
|
432
457
|
base_args = {
|
|
433
458
|
"collections": collections,
|
|
434
459
|
"ids": ids,
|
|
@@ -446,10 +471,18 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
446
471
|
base_args["intersects"] = orjson.loads(unquote_plus(intersects))
|
|
447
472
|
|
|
448
473
|
if sortby:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
474
|
+
parsed_sort = []
|
|
475
|
+
for raw in sortby:
|
|
476
|
+
if not isinstance(raw, str):
|
|
477
|
+
continue
|
|
478
|
+
s = raw.strip()
|
|
479
|
+
if not s:
|
|
480
|
+
continue
|
|
481
|
+
direction = "desc" if s[0] == "-" else "asc"
|
|
482
|
+
field = s[1:] if s and s[0] in "+-" else s
|
|
483
|
+
parsed_sort.append({"field": field, "direction": direction})
|
|
484
|
+
if parsed_sort:
|
|
485
|
+
base_args["sortby"] = parsed_sort
|
|
453
486
|
|
|
454
487
|
if filter_expr:
|
|
455
488
|
base_args["filter_lang"] = "cql2-json"
|
|
@@ -526,13 +559,15 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
526
559
|
|
|
527
560
|
search = self.database.apply_bbox_filter(search=search, bbox=bbox)
|
|
528
561
|
|
|
529
|
-
if search_request
|
|
562
|
+
if hasattr(search_request, "intersects") and getattr(
|
|
563
|
+
search_request, "intersects"
|
|
564
|
+
):
|
|
530
565
|
search = self.database.apply_intersects_filter(
|
|
531
|
-
search=search, intersects=search_request
|
|
566
|
+
search=search, intersects=getattr(search_request, "intersects")
|
|
532
567
|
)
|
|
533
568
|
|
|
534
|
-
if search_request
|
|
535
|
-
for field_name, expr in search_request
|
|
569
|
+
if hasattr(search_request, "query") and getattr(search_request, "query"):
|
|
570
|
+
for field_name, expr in getattr(search_request, "query").items():
|
|
536
571
|
field = "properties__" + field_name
|
|
537
572
|
for op, value in expr.items():
|
|
538
573
|
# Convert enum to string
|
|
@@ -541,9 +576,14 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
541
576
|
search=search, op=operator, field=field, value=value
|
|
542
577
|
)
|
|
543
578
|
|
|
544
|
-
#
|
|
579
|
+
# Apply CQL2 filter (support both 'filter_expr' and canonical 'filter')
|
|
580
|
+
cql2_filter = None
|
|
545
581
|
if hasattr(search_request, "filter_expr"):
|
|
546
582
|
cql2_filter = getattr(search_request, "filter_expr", None)
|
|
583
|
+
if cql2_filter is None and hasattr(search_request, "filter"):
|
|
584
|
+
cql2_filter = getattr(search_request, "filter", None)
|
|
585
|
+
|
|
586
|
+
if cql2_filter is not None:
|
|
547
587
|
try:
|
|
548
588
|
search = await self.database.apply_cql2_filter(search, cql2_filter)
|
|
549
589
|
except Exception as e:
|
|
@@ -561,19 +601,23 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
561
601
|
)
|
|
562
602
|
|
|
563
603
|
sort = None
|
|
564
|
-
if search_request
|
|
565
|
-
sort = self.database.populate_sort(search_request
|
|
604
|
+
if hasattr(search_request, "sortby") and getattr(search_request, "sortby"):
|
|
605
|
+
sort = self.database.populate_sort(getattr(search_request, "sortby"))
|
|
566
606
|
|
|
567
607
|
limit = 10
|
|
568
608
|
if search_request.limit:
|
|
569
609
|
limit = search_request.limit
|
|
570
610
|
|
|
611
|
+
# Use token from the request if the model doesn't define it
|
|
612
|
+
token_param = getattr(
|
|
613
|
+
search_request, "token", None
|
|
614
|
+
) or request.query_params.get("token")
|
|
571
615
|
items, maybe_count, next_token = await self.database.execute_search(
|
|
572
616
|
search=search,
|
|
573
617
|
limit=limit,
|
|
574
|
-
token=
|
|
618
|
+
token=token_param,
|
|
575
619
|
sort=sort,
|
|
576
|
-
collection_ids=search_request
|
|
620
|
+
collection_ids=getattr(search_request, "collections", None),
|
|
577
621
|
datetime_search=datetime_search,
|
|
578
622
|
)
|
|
579
623
|
|
|
@@ -599,8 +643,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
599
643
|
type="FeatureCollection",
|
|
600
644
|
features=items,
|
|
601
645
|
links=links,
|
|
602
|
-
|
|
603
|
-
|
|
646
|
+
numberReturned=len(items),
|
|
647
|
+
numberMatched=maybe_count,
|
|
604
648
|
)
|
|
605
649
|
|
|
606
650
|
|
|
@@ -917,7 +961,7 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
917
961
|
|
|
918
962
|
@attr.s
|
|
919
963
|
class BulkTransactionsClient(BaseBulkTransactionsClient):
|
|
920
|
-
"""A client for posting bulk transactions
|
|
964
|
+
"""A client for posting bulk transactions.
|
|
921
965
|
|
|
922
966
|
Attributes:
|
|
923
967
|
session: An instance of `Session` to use for database connection.
|
|
@@ -965,6 +1009,13 @@ class BulkTransactionsClient(BaseBulkTransactionsClient):
|
|
|
965
1009
|
A string indicating the number of items successfully added.
|
|
966
1010
|
"""
|
|
967
1011
|
request = kwargs.get("request")
|
|
1012
|
+
|
|
1013
|
+
if os.getenv("ENABLE_DATETIME_INDEX_FILTERING"):
|
|
1014
|
+
raise HTTPException(
|
|
1015
|
+
status_code=400,
|
|
1016
|
+
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.",
|
|
1017
|
+
)
|
|
1018
|
+
|
|
968
1019
|
if request:
|
|
969
1020
|
base_url = str(request.base_url)
|
|
970
1021
|
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:
|
|
@@ -41,24 +41,6 @@ DEFAULT_QUERYABLES: Dict[str, Dict[str, Any]] = {
|
|
|
41
41
|
"description": "Creation Timestamp",
|
|
42
42
|
"$ref": "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/updated",
|
|
43
43
|
},
|
|
44
|
-
"cloud_cover": {
|
|
45
|
-
"description": "Cloud Cover",
|
|
46
|
-
"$ref": "https://stac-extensions.github.io/eo/v1.0.0/schema.json#/definitions/fields/properties/eo:cloud_cover",
|
|
47
|
-
},
|
|
48
|
-
"cloud_shadow_percentage": {
|
|
49
|
-
"title": "Cloud Shadow Percentage",
|
|
50
|
-
"description": "Cloud Shadow Percentage",
|
|
51
|
-
"type": "number",
|
|
52
|
-
"minimum": 0,
|
|
53
|
-
"maximum": 100,
|
|
54
|
-
},
|
|
55
|
-
"nodata_pixel_percentage": {
|
|
56
|
-
"title": "No Data Pixel Percentage",
|
|
57
|
-
"description": "No Data Pixel Percentage",
|
|
58
|
-
"type": "number",
|
|
59
|
-
"minimum": 0,
|
|
60
|
-
"maximum": 100,
|
|
61
|
-
},
|
|
62
44
|
}
|
|
63
45
|
"""Queryables that are present in all collections."""
|
|
64
46
|
|
|
@@ -139,7 +139,7 @@ class CollectionLinks(BaseLinks):
|
|
|
139
139
|
if "FilterExtension" in self.extensions:
|
|
140
140
|
return dict(
|
|
141
141
|
rel="queryables",
|
|
142
|
-
type=MimeTypes.
|
|
142
|
+
type=MimeTypes.jsonschema.value,
|
|
143
143
|
href=urljoin(
|
|
144
144
|
self.base_url, f"collections/{self.collection_id}/queryables"
|
|
145
145
|
),
|
|
@@ -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.4.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.4.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
|
|
|
@@ -75,11 +54,10 @@ SFEOS (stac-fastapi-elasticsearch-opensearch) is a high-performance, scalable AP
|
|
|
75
54
|
- **Scale to millions of geospatial assets** with fast search performance through optimized spatial indexing and query capabilities
|
|
76
55
|
- **Support OGC-compliant filtering** including spatial operations (intersects, contains, etc.) and temporal queries
|
|
77
56
|
- **Perform geospatial aggregations** to analyze data distribution across space and time
|
|
57
|
+
- **Enhanced collection search capabilities** with support for sorting and field selection
|
|
78
58
|
|
|
79
59
|
This implementation builds on the STAC-FastAPI framework, providing a production-ready solution specifically optimized for Elasticsearch and OpenSearch databases. It's ideal for organizations managing large geospatial data catalogs who need efficient discovery and access capabilities through standardized APIs.
|
|
80
60
|
|
|
81
|
-
|
|
82
|
-
|
|
83
61
|
## Common Deployment Patterns
|
|
84
62
|
|
|
85
63
|
stac-fastapi-elasticsearch-opensearch can be deployed in several ways depending on your needs:
|
|
@@ -105,26 +83,44 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
105
83
|
|
|
106
84
|
## Table of Contents
|
|
107
85
|
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
112
|
-
- [
|
|
113
|
-
- [
|
|
114
|
-
- [
|
|
115
|
-
- [
|
|
116
|
-
- [
|
|
117
|
-
- [
|
|
118
|
-
- [
|
|
119
|
-
- [
|
|
120
|
-
- [
|
|
121
|
-
- [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- [
|
|
125
|
-
- [
|
|
126
|
-
- [
|
|
127
|
-
- [
|
|
86
|
+
- [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
|
|
87
|
+
- [Sponsors \& Supporters](#sponsors--supporters)
|
|
88
|
+
- [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
|
|
89
|
+
- [Common Deployment Patterns](#common-deployment-patterns)
|
|
90
|
+
- [Technologies](#technologies)
|
|
91
|
+
- [Table of Contents](#table-of-contents)
|
|
92
|
+
- [Collection Search Extensions](#collection-search-extensions)
|
|
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
|
|
|
@@ -135,6 +131,37 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
|
|
|
135
131
|
- [Gitter Chat](https://app.gitter.im/#/room/#stac-fastapi-elasticsearch_community:gitter.im) - For real-time discussions
|
|
136
132
|
- [GitHub Discussions](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/discussions) - For longer-form questions and answers
|
|
137
133
|
|
|
134
|
+
## Collection Search Extensions
|
|
135
|
+
|
|
136
|
+
SFEOS implements extended capabilities for the `/collections` endpoint, allowing for more powerful collection discovery:
|
|
137
|
+
|
|
138
|
+
- **Sorting**: Sort collections by sortable fields using the `sortby` parameter
|
|
139
|
+
- Example: `/collections?sortby=+id` (ascending sort by ID)
|
|
140
|
+
- Example: `/collections?sortby=-id` (descending sort by ID)
|
|
141
|
+
- Example: `/collections?sortby=-temporal` (descending sort by temporal extent)
|
|
142
|
+
|
|
143
|
+
- **Field Selection**: Request only specific fields to be returned using the `fields` parameter
|
|
144
|
+
- Example: `/collections?fields=id,title,description`
|
|
145
|
+
- This helps reduce payload size when only certain fields are needed
|
|
146
|
+
|
|
147
|
+
- **Free Text Search**: Search across collection text fields using the `q` parameter
|
|
148
|
+
- Example: `/collections?q=landsat`
|
|
149
|
+
- Searches across multiple text fields including title, description, and keywords
|
|
150
|
+
- Supports partial word matching and relevance-based sorting
|
|
151
|
+
|
|
152
|
+
These extensions make it easier to build user interfaces that display and navigate through collections efficiently.
|
|
153
|
+
|
|
154
|
+
> **Configuration**: Collection search extensions can be disabled by setting the `ENABLE_COLLECTIONS_SEARCH` environment variable to `false`. By default, these extensions are enabled.
|
|
155
|
+
|
|
156
|
+
> **Note**: Sorting is only available on fields that are indexed for sorting in Elasticsearch/OpenSearch. With the default mappings, you can sort on:
|
|
157
|
+
> - `id` (keyword field)
|
|
158
|
+
> - `extent.temporal.interval` (date field)
|
|
159
|
+
> - `temporal` (alias to extent.temporal.interval)
|
|
160
|
+
>
|
|
161
|
+
> Text fields like `title` and `description` are not sortable by default as they use text analysis for better search capabilities. Attempting to sort on these fields will result in a user-friendly error message explaining which fields are sortable and how to make additional fields sortable by updating the mappings.
|
|
162
|
+
>
|
|
163
|
+
> **Important**: Adding keyword fields to make text fields sortable can significantly increase the index size, especially for large text fields. Consider the storage implications when deciding which fields to make sortable.
|
|
164
|
+
|
|
138
165
|
## Package Structure
|
|
139
166
|
|
|
140
167
|
This project is organized into several packages, each with a specific purpose:
|
|
@@ -265,8 +292,12 @@ You can customize additional settings in your `.env` file:
|
|
|
265
292
|
| `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
|
|
266
293
|
| `RAISE_ON_BULK_ERROR` | Controls whether bulk insert operations raise exceptions on errors. If set to `true`, the operation will stop and raise an exception when an error occurs. If set to `false`, errors will be logged, and the operation will continue. **Note:** STAC Item and ItemCollection validation errors will always raise, regardless of this flag. | `false` | Optional |
|
|
267
294
|
| `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 |
|
|
295
|
+
| `ENABLE_COLLECTIONS_SEARCH` | Enable collection search extensions (sort, fields). | `true` | Optional |
|
|
268
296
|
| `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
297
|
| `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
|
|
298
|
+
| `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 |
|
|
299
|
+
| `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 |
|
|
300
|
+
| `USE_DATETIME` | Configures the datetime search behavior in SFEOS. When enabled, searches both datetime field and falls back to start_datetime/end_datetime range for items with null datetime. When disabled, searches only by start_datetime/end_datetime range. | True | Optional |
|
|
270
301
|
|
|
271
302
|
> [!NOTE]
|
|
272
303
|
> 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.
|
|
@@ -408,6 +439,10 @@ The system uses a precise naming convention:
|
|
|
408
439
|
- **Root Path Configuration**: The application root path is the base URL by default.
|
|
409
440
|
- For AWS Lambda with Gateway API: Set `STAC_FASTAPI_ROOT_PATH` to match the Gateway API stage name (e.g., `/v1`)
|
|
410
441
|
|
|
442
|
+
- **Feature Configuration**: Control which features are enabled:
|
|
443
|
+
- `ENABLE_COLLECTIONS_SEARCH`: Set to `true` (default) to enable collection search extensions (sort, fields). Set to `false` to disable.
|
|
444
|
+
- `ENABLE_TRANSACTIONS_EXTENSIONS`: Set to `true` (default) to enable transaction extensions. Set to `false` to disable.
|
|
445
|
+
|
|
411
446
|
|
|
412
447
|
## Collection Pagination
|
|
413
448
|
|
|
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.4.0}/stac_fastapi/core/extensions/__init__.py
RENAMED
|
File without changes
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.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
|
{stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|