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.
Files changed (30) hide show
  1. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/PKG-INFO +80 -45
  2. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/README.md +78 -22
  3. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/base_database_logic.py +18 -4
  4. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/core.py +133 -82
  5. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/datetime_utils.py +4 -1
  6. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/filter.py +0 -18
  7. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/models/links.py +1 -1
  8. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/serializers.py +35 -1
  9. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/utilities.py +9 -1
  10. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/version.py +1 -1
  11. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/PKG-INFO +81 -46
  12. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/top_level.txt +0 -1
  13. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/setup.cfg +0 -0
  14. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/setup.py +0 -0
  15. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/__init__.py +0 -0
  16. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/base_settings.py +0 -0
  17. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/basic_auth.py +0 -0
  18. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/__init__.py +0 -0
  19. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/aggregation.py +0 -0
  20. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/fields.py +0 -0
  21. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/extensions/query.py +0 -0
  22. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/models/__init__.py +0 -0
  23. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/models/search.py +0 -0
  24. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/rate_limit.py +0 -0
  25. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/route_dependencies.py +0 -0
  26. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi/core/session.py +0 -0
  27. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/SOURCES.txt +0 -0
  28. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/dependency_links.txt +0 -0
  29. {stac_fastapi_core-6.2.1 → stac_fastapi_core-6.4.0}/stac_fastapi_core.egg-info/not-zip-safe +0 -0
  30. {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.4
1
+ Metadata-Version: 2.1
2
2
  Name: stac_fastapi_core
3
- Version: 6.2.1
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
- - [Documentation & Resources](#documentation--resources)
109
- - [Package Structure](#package-structure)
110
- - [Examples](#examples)
111
- - [Performance](#performance)
112
- - [Quick Start](#quick-start)
113
- - [Installation](#installation)
114
- - [Running Locally](#running-locally)
115
- - [Configuration reference](#configuration-reference)
116
- - [Interacting with the API](#interacting-with-the-api)
117
- - [Configure the API](#configure-the-api)
118
- - [Collection pagination](#collection-pagination)
119
- - [Ingesting Sample Data CLI Tool](#ingesting-sample-data-cli-tool)
120
- - [Elasticsearch Mappings](#elasticsearch-mappings)
121
- - [Managing Elasticsearch Indices](#managing-elasticsearch-indices)
122
- - [Snapshots](#snapshots)
123
- - [Reindexing](#reindexing)
124
- - [Auth](#auth)
125
- - [Aggregation](#aggregation)
126
- - [Rate Limiting](#rate-limiting)
127
- - [Datetime-Based Index Management](#datetime-based-index-management)
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
- - [Documentation & Resources](#documentation--resources)
70
- - [Package Structure](#package-structure)
71
- - [Examples](#examples)
72
- - [Performance](#performance)
73
- - [Quick Start](#quick-start)
74
- - [Installation](#installation)
75
- - [Running Locally](#running-locally)
76
- - [Configuration reference](#configuration-reference)
77
- - [Interacting with the API](#interacting-with-the-api)
78
- - [Configure the API](#configure-the-api)
79
- - [Collection pagination](#collection-pagination)
80
- - [Ingesting Sample Data CLI Tool](#ingesting-sample-data-cli-tool)
81
- - [Elasticsearch Mappings](#elasticsearch-mappings)
82
- - [Managing Elasticsearch Indices](#managing-elasticsearch-indices)
83
- - [Snapshots](#snapshots)
84
- - [Reindexing](#reindexing)
85
- - [Auth](#auth)
86
- - [Aggregation](#aggregation)
87
- - [Rate Limiting](#rate-limiting)
88
- - [Datetime-Based Index Management](#datetime-based-index-management)
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
 
@@ -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, token: Optional[str], limit: int
18
- ) -> Iterable[Dict[str, Any]]:
19
- """Retrieve a list of all collections from the database."""
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(self, **kwargs) -> stac_types.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=collections, links=links)
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
- """Read items from a specific collection in the database.
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): The identifier of the collection to read items from.
297
- bbox (Optional[BBox]): The bounding box to filter items by.
298
- datetime (Optional[str]): The datetime range to filter items by.
299
- limit (int): The maximum number of items to return.
300
- token (str): A token used for pagination.
301
- request (Request): The incoming request.
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: An `ItemCollection` object containing the items from the specified collection that meet
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: If the specified collection is not found.
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
- search, datetime_search = self.database.apply_datetime_filter(
330
- search=search, datetime=datetime
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
- limit = int(request.query_params.get("limit", os.getenv("STAC_ITEM_LIMIT", 10)))
346
- items, maybe_count, next_token = await self.database.execute_search(
347
- search=search,
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
- collection_ids=[collection_id],
352
- datetime_search=datetime_search,
353
- )
354
-
355
- items = [
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
- base_args["sortby"] = [
450
- {"field": sort[1:], "direction": "desc" if sort[0] == "-" else "asc"}
451
- for sort in sortby
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.intersects:
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.intersects
566
+ search=search, intersects=getattr(search_request, "intersects")
532
567
  )
533
568
 
534
- if search_request.query:
535
- for field_name, expr in search_request.query.items():
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
- # only cql2_json is supported here
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.sortby:
565
- sort = self.database.populate_sort(search_request.sortby)
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=search_request.token,
618
+ token=token_param,
575
619
  sort=sort,
576
- collection_ids=search_request.collections,
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
- numReturned=len(items),
603
- numMatched=maybe_count,
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 to a Postgres database.
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.strftime("%Y-%m-%dT%H:%M:%SZ")
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.json.value,
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=item.get("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
- MAX_LIMIT = 10000
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.1"
2
+ __version__ = "6.4.0"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
2
- Name: stac_fastapi_core
3
- Version: 6.2.1
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
- - [Documentation & Resources](#documentation--resources)
109
- - [Package Structure](#package-structure)
110
- - [Examples](#examples)
111
- - [Performance](#performance)
112
- - [Quick Start](#quick-start)
113
- - [Installation](#installation)
114
- - [Running Locally](#running-locally)
115
- - [Configuration reference](#configuration-reference)
116
- - [Interacting with the API](#interacting-with-the-api)
117
- - [Configure the API](#configure-the-api)
118
- - [Collection pagination](#collection-pagination)
119
- - [Ingesting Sample Data CLI Tool](#ingesting-sample-data-cli-tool)
120
- - [Elasticsearch Mappings](#elasticsearch-mappings)
121
- - [Managing Elasticsearch Indices](#managing-elasticsearch-indices)
122
- - [Snapshots](#snapshots)
123
- - [Reindexing](#reindexing)
124
- - [Auth](#auth)
125
- - [Aggregation](#aggregation)
126
- - [Rate Limiting](#rate-limiting)
127
- - [Datetime-Based Index Management](#datetime-based-index-management)
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