stac-fastapi-elasticsearch 6.4.0__py3-none-any.whl → 6.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,9 @@ from stac_fastapi.core.extensions.aggregation import (
23
23
  EsAggregationExtensionGetRequest,
24
24
  EsAggregationExtensionPostRequest,
25
25
  )
26
+ from stac_fastapi.core.extensions.collections_search import (
27
+ CollectionsSearchEndpointExtension,
28
+ )
26
29
  from stac_fastapi.core.extensions.fields import FieldsExtension
27
30
  from stac_fastapi.core.rate_limit import setup_rate_limit
28
31
  from stac_fastapi.core.route_dependencies import get_route_dependencies
@@ -34,9 +37,11 @@ from stac_fastapi.elasticsearch.database_logic import (
34
37
  create_collection_index,
35
38
  create_index_templates,
36
39
  )
37
- from stac_fastapi.extensions.core import ( # CollectionSearchFilterExtension,
40
+ from stac_fastapi.extensions.core import (
38
41
  AggregationExtension,
39
42
  CollectionSearchExtension,
43
+ CollectionSearchFilterExtension,
44
+ CollectionSearchPostExtension,
40
45
  FilterExtension,
41
46
  FreeTextExtension,
42
47
  SortExtension,
@@ -57,8 +62,14 @@ logger = logging.getLogger(__name__)
57
62
 
58
63
  TRANSACTIONS_EXTENSIONS = get_bool_env("ENABLE_TRANSACTIONS_EXTENSIONS", default=True)
59
64
  ENABLE_COLLECTIONS_SEARCH = get_bool_env("ENABLE_COLLECTIONS_SEARCH", default=True)
65
+ ENABLE_COLLECTIONS_SEARCH_ROUTE = get_bool_env(
66
+ "ENABLE_COLLECTIONS_SEARCH_ROUTE", default=False
67
+ )
60
68
  logger.info("TRANSACTIONS_EXTENSIONS is set to %s", TRANSACTIONS_EXTENSIONS)
61
69
  logger.info("ENABLE_COLLECTIONS_SEARCH is set to %s", ENABLE_COLLECTIONS_SEARCH)
70
+ logger.info(
71
+ "ENABLE_COLLECTIONS_SEARCH_ROUTE is set to %s", ENABLE_COLLECTIONS_SEARCH_ROUTE
72
+ )
62
73
 
63
74
  settings = ElasticsearchSettings()
64
75
  session = Session.create_from_settings(settings)
@@ -116,16 +127,18 @@ if TRANSACTIONS_EXTENSIONS:
116
127
 
117
128
  extensions = [aggregation_extension] + search_extensions
118
129
 
119
- # Create collection search extensions if enabled
120
- if ENABLE_COLLECTIONS_SEARCH:
130
+ # Collection search related variables
131
+ collections_get_request_model = None
132
+
133
+ if ENABLE_COLLECTIONS_SEARCH or ENABLE_COLLECTIONS_SEARCH_ROUTE:
121
134
  # Create collection search extensions
122
135
  collection_search_extensions = [
123
- # QueryExtension(conformance_classes=[QueryConformanceClasses.COLLECTIONS]),
136
+ QueryExtension(conformance_classes=[QueryConformanceClasses.COLLECTIONS]),
124
137
  SortExtension(conformance_classes=[SortConformanceClasses.COLLECTIONS]),
125
138
  FieldsExtension(conformance_classes=[FieldsConformanceClasses.COLLECTIONS]),
126
- # CollectionSearchFilterExtension(
127
- # conformance_classes=[FilterConformanceClasses.COLLECTIONS]
128
- # ),
139
+ CollectionSearchFilterExtension(
140
+ conformance_classes=[FilterConformanceClasses.COLLECTIONS]
141
+ ),
129
142
  FreeTextExtension(conformance_classes=[FreeTextConformanceClasses.COLLECTIONS]),
130
143
  ]
131
144
 
@@ -135,7 +148,58 @@ if ENABLE_COLLECTIONS_SEARCH:
135
148
  )
136
149
  collections_get_request_model = collection_search_ext.GET
137
150
 
151
+ # Create a post request model for collection search
152
+ collection_search_post_request_model = create_post_request_model(
153
+ collection_search_extensions
154
+ )
155
+
156
+ # Create collection search extensions if enabled
157
+ if ENABLE_COLLECTIONS_SEARCH:
158
+ # Initialize collection search POST extension
159
+ collection_search_post_ext = CollectionSearchPostExtension(
160
+ client=CoreClient(
161
+ database=database_logic,
162
+ session=session,
163
+ post_request_model=collection_search_post_request_model,
164
+ landing_page_id=os.getenv("STAC_FASTAPI_LANDING_PAGE_ID", "stac-fastapi"),
165
+ ),
166
+ settings=settings,
167
+ POST=collection_search_post_request_model,
168
+ conformance_classes=[
169
+ "https://api.stacspec.org/v1.0.0-rc.1/collection-search",
170
+ QueryConformanceClasses.COLLECTIONS,
171
+ FilterConformanceClasses.COLLECTIONS,
172
+ FreeTextConformanceClasses.COLLECTIONS,
173
+ SortConformanceClasses.COLLECTIONS,
174
+ FieldsConformanceClasses.COLLECTIONS,
175
+ ],
176
+ )
138
177
  extensions.append(collection_search_ext)
178
+ extensions.append(collection_search_post_ext)
179
+
180
+ if ENABLE_COLLECTIONS_SEARCH_ROUTE:
181
+ # Initialize collections-search endpoint extension
182
+ collections_search_endpoint_ext = CollectionsSearchEndpointExtension(
183
+ client=CoreClient(
184
+ database=database_logic,
185
+ session=session,
186
+ post_request_model=collection_search_post_request_model,
187
+ landing_page_id=os.getenv("STAC_FASTAPI_LANDING_PAGE_ID", "stac-fastapi"),
188
+ ),
189
+ settings=settings,
190
+ GET=collections_get_request_model,
191
+ POST=collection_search_post_request_model,
192
+ conformance_classes=[
193
+ "https://api.stacspec.org/v1.0.0-rc.1/collection-search",
194
+ QueryConformanceClasses.COLLECTIONS,
195
+ FilterConformanceClasses.COLLECTIONS,
196
+ FreeTextConformanceClasses.COLLECTIONS,
197
+ SortConformanceClasses.COLLECTIONS,
198
+ FieldsConformanceClasses.COLLECTIONS,
199
+ ],
200
+ )
201
+ extensions.append(collections_search_endpoint_ext)
202
+
139
203
 
140
204
  database_logic.extensions = [type(ext).__name__ for ext in extensions]
141
205
 
@@ -175,8 +239,8 @@ app_config = {
175
239
  "route_dependencies": get_route_dependencies(),
176
240
  }
177
241
 
178
- # Add collections_get_request_model if collection search is enabled
179
- if ENABLE_COLLECTIONS_SEARCH:
242
+ # Add collections_get_request_model if it was created
243
+ if collections_get_request_model:
180
244
  app_config["collections_get_request_model"] = collections_get_request_model
181
245
 
182
246
  api = StacApi(**app_config)
@@ -193,6 +257,7 @@ async def lifespan(app: FastAPI):
193
257
  app = api.app
194
258
  app.router.lifespan_context = lifespan
195
259
  app.root_path = os.getenv("STAC_FASTAPI_ROOT_PATH", "")
260
+
196
261
  # Add rate limit
197
262
  setup_rate_limit(app, rate_limit=os.getenv("STAC_FASTAPI_RATE_LIMIT"))
198
263
 
@@ -176,7 +176,10 @@ class DatabaseLogic(BaseDatabaseLogic):
176
176
  request: Request,
177
177
  sort: Optional[List[Dict[str, Any]]] = None,
178
178
  q: Optional[List[str]] = None,
179
- ) -> Tuple[List[Dict[str, Any]], Optional[str]]:
179
+ filter: Optional[Dict[str, Any]] = None,
180
+ query: Optional[Dict[str, Dict[str, Any]]] = None,
181
+ datetime: Optional[str] = None,
182
+ ) -> Tuple[List[Dict[str, Any]], Optional[str], Optional[int]]:
180
183
  """Retrieve a list of collections from Elasticsearch, supporting pagination.
181
184
 
182
185
  Args:
@@ -185,6 +188,9 @@ class DatabaseLogic(BaseDatabaseLogic):
185
188
  request (Request): The FastAPI request object.
186
189
  sort (Optional[List[Dict[str, Any]]]): Optional sort parameter from the request.
187
190
  q (Optional[List[str]]): Free text search terms.
191
+ query (Optional[Dict[str, Dict[str, Any]]]): Query extension parameters.
192
+ filter (Optional[Dict[str, Any]]): Structured query in CQL2 format.
193
+ datetime (Optional[str]): Temporal filter.
188
194
 
189
195
  Returns:
190
196
  A tuple of (collections, next pagination token if any).
@@ -222,8 +228,24 @@ class DatabaseLogic(BaseDatabaseLogic):
222
228
  "size": limit,
223
229
  }
224
230
 
231
+ # Handle search_after token - split by '|' to get all sort values
232
+ search_after = None
225
233
  if token:
226
- body["search_after"] = [token]
234
+ try:
235
+ # The token should be a pipe-separated string of sort values
236
+ # e.g., "2023-01-01T00:00:00Z|collection-1"
237
+ search_after = token.split("|")
238
+ # If the number of sort fields doesn't match token parts, ignore the token
239
+ if len(search_after) != len(formatted_sort):
240
+ search_after = None
241
+ except Exception:
242
+ search_after = None
243
+
244
+ if search_after is not None:
245
+ body["search_after"] = search_after
246
+
247
+ # Build the query part of the body
248
+ query_parts = []
227
249
 
228
250
  # Apply free text query if provided
229
251
  if q:
@@ -251,17 +273,86 @@ class DatabaseLogic(BaseDatabaseLogic):
251
273
  }
252
274
  )
253
275
 
254
- # Add the query to the body using bool query with should clauses
255
- body["query"] = {
256
- "bool": {"should": should_clauses, "minimum_should_match": 1}
257
- }
276
+ # Add the free text query to the query parts
277
+ query_parts.append(
278
+ {"bool": {"should": should_clauses, "minimum_should_match": 1}}
279
+ )
258
280
 
259
- # Execute the search
260
- response = await self.client.search(
261
- index=COLLECTIONS_INDEX,
262
- body=body,
281
+ # Apply structured filter if provided
282
+ if filter:
283
+ # Convert string filter to dict if needed
284
+ if isinstance(filter, str):
285
+ filter = orjson.loads(filter)
286
+ # Convert the filter to an Elasticsearch query using the filter module
287
+ es_query = filter_module.to_es(await self.get_queryables_mapping(), filter)
288
+ query_parts.append(es_query)
289
+
290
+ # Apply query extension if provided
291
+ if query:
292
+ try:
293
+ # First create a search object to apply filters
294
+ search = Search(index=COLLECTIONS_INDEX)
295
+
296
+ # Process each field and operator in the query
297
+ for field_name, expr in query.items():
298
+ for op, value in expr.items():
299
+ # For collections, we don't need to prefix with 'properties__'
300
+ field = field_name
301
+ # Apply the filter using apply_stacql_filter
302
+ search = self.apply_stacql_filter(
303
+ search=search, op=op, field=field, value=value
304
+ )
305
+
306
+ # Convert the search object to a query dict and add it to query_parts
307
+ search_dict = search.to_dict()
308
+ if "query" in search_dict:
309
+ query_parts.append(search_dict["query"])
310
+
311
+ except Exception as e:
312
+ logger.error(f"Error converting query to Elasticsearch: {e}")
313
+ # If there's an error, add a query that matches nothing
314
+ query_parts.append({"bool": {"must_not": {"match_all": {}}}})
315
+ raise
316
+
317
+ # Combine all query parts with AND logic if there are multiple
318
+ datetime_filter = None
319
+ if datetime:
320
+ datetime_filter = self._apply_collection_datetime_filter(datetime)
321
+ if datetime_filter:
322
+ query_parts.append(datetime_filter)
323
+
324
+ # Combine all query parts with AND logic
325
+ if query_parts:
326
+ body["query"] = (
327
+ query_parts[0]
328
+ if len(query_parts) == 1
329
+ else {"bool": {"must": query_parts}}
330
+ )
331
+
332
+ # Create a copy of the body for count query (without pagination and sorting)
333
+ count_body = body.copy()
334
+ if "search_after" in count_body:
335
+ del count_body["search_after"]
336
+ count_body["size"] = 0
337
+
338
+ # Create async tasks for both search and count
339
+ search_task = asyncio.create_task(
340
+ self.client.search(
341
+ index=COLLECTIONS_INDEX,
342
+ body=body,
343
+ )
263
344
  )
264
345
 
346
+ count_task = asyncio.create_task(
347
+ self.client.count(
348
+ index=COLLECTIONS_INDEX,
349
+ body={"query": body.get("query", {"match_all": {}})},
350
+ )
351
+ )
352
+
353
+ # Wait for search task to complete
354
+ response = await search_task
355
+
265
356
  hits = response["hits"]["hits"]
266
357
  collections = [
267
358
  self.collection_serializer.db_to_stac(
@@ -274,9 +365,59 @@ class DatabaseLogic(BaseDatabaseLogic):
274
365
  if len(hits) == limit:
275
366
  next_token_values = hits[-1].get("sort")
276
367
  if next_token_values:
277
- next_token = next_token_values[0]
368
+ # Join all sort values with '|' to create the token
369
+ next_token = "|".join(str(val) for val in next_token_values)
278
370
 
279
- return collections, next_token
371
+ # Get the total count of collections
372
+ matched = (
373
+ response["hits"]["total"]["value"]
374
+ if response["hits"]["total"]["relation"] == "eq"
375
+ else None
376
+ )
377
+
378
+ # If count task is done, use its result
379
+ if count_task.done():
380
+ try:
381
+ matched = count_task.result().get("count")
382
+ except Exception as e:
383
+ logger.error(f"Count task failed: {e}")
384
+
385
+ return collections, next_token, matched
386
+
387
+ @staticmethod
388
+ def _apply_collection_datetime_filter(
389
+ datetime_str: Optional[str],
390
+ ) -> Optional[Dict[str, Any]]:
391
+ """Create a temporal filter for collections based on their extent."""
392
+ if not datetime_str:
393
+ return None
394
+
395
+ # Parse the datetime string into start and end
396
+ if "/" in datetime_str:
397
+ start, end = datetime_str.split("/")
398
+ # Replace open-ended ranges with concrete dates
399
+ if start == "..":
400
+ # For open-ended start, use a very early date
401
+ start = "1800-01-01T00:00:00Z"
402
+ if end == "..":
403
+ # For open-ended end, use a far future date
404
+ end = "2999-12-31T23:59:59Z"
405
+ else:
406
+ # If it's just a single date, use it for both start and end
407
+ start = end = datetime_str
408
+
409
+ return {
410
+ "bool": {
411
+ "must": [
412
+ # Check if any date in the array is less than or equal to the query end date
413
+ # This will match if the collection's start date is before or equal to the query end date
414
+ {"range": {"extent.temporal.interval": {"lte": end}}},
415
+ # Check if any date in the array is greater than or equal to the query start date
416
+ # This will match if the collection's end date is after or equal to the query start date
417
+ {"range": {"extent.temporal.interval": {"gte": start}}},
418
+ ]
419
+ }
420
+ }
280
421
 
281
422
  async def get_one_item(self, collection_id: str, item_id: str) -> Dict:
282
423
  """Retrieve a single item from the database.
@@ -540,18 +681,31 @@ class DatabaseLogic(BaseDatabaseLogic):
540
681
 
541
682
  Args:
542
683
  search (Search): The search object to apply the filter to.
543
- op (str): The comparison operator to use. Can be 'eq' (equal), 'gt' (greater than), 'gte' (greater than or equal),
544
- 'lt' (less than), or 'lte' (less than or equal).
684
+ op (str): The comparison operator to use. Can be 'eq' (equal), 'ne'/'neq' (not equal), 'gt' (greater than),
685
+ 'gte' (greater than or equal), 'lt' (less than), or 'lte' (less than or equal).
545
686
  field (str): The field to perform the comparison on.
546
687
  value (float): The value to compare the field against.
547
688
 
548
689
  Returns:
549
690
  search (Search): The search object with the specified filter applied.
550
691
  """
551
- if op != "eq":
692
+ if op == "eq":
693
+ search = search.filter("term", **{field: value})
694
+ elif op == "ne" or op == "neq":
695
+ # For not equal, use a bool query with must_not
696
+ search = search.exclude("term", **{field: value})
697
+ elif op in ["gt", "gte", "lt", "lte"]:
698
+ # For range operators
552
699
  key_filter = {field: {op: value}}
553
700
  search = search.filter(Q("range", **key_filter))
554
- else:
701
+ elif op == "in":
702
+ # For in operator (value should be a list)
703
+ if isinstance(value, list):
704
+ search = search.filter("terms", **{field: value})
705
+ else:
706
+ search = search.filter("term", **{field: value})
707
+ elif op == "contains":
708
+ # For contains operator (for arrays)
555
709
  search = search.filter("term", **{field: value})
556
710
 
557
711
  return search
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "6.4.0"
2
+ __version__ = "6.5.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi-elasticsearch
3
- Version: 6.4.0
3
+ Version: 6.5.1
4
4
  Summary: An implementation of STAC API based on the FastAPI framework with both Elasticsearch and Opensearch.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -15,8 +15,8 @@ Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Requires-Python: >=3.9
17
17
  Description-Content-Type: text/markdown
18
- Requires-Dist: stac-fastapi-core==6.4.0
19
- Requires-Dist: sfeos-helpers==6.4.0
18
+ Requires-Dist: stac-fastapi-core==6.5.1
19
+ Requires-Dist: sfeos-helpers==6.5.1
20
20
  Requires-Dist: elasticsearch[async]~=8.18.0
21
21
  Requires-Dist: uvicorn~=0.23.0
22
22
  Requires-Dist: starlette<0.36.0,>=0.35.0
@@ -102,13 +102,13 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
102
102
  ## Table of Contents
103
103
 
104
104
  - [stac-fastapi-elasticsearch-opensearch](#stac-fastapi-elasticsearch-opensearch)
105
- - [Sponsors \& Supporters](#sponsors--supporters)
105
+ - [Sponsors & Supporters](#sponsors--supporters)
106
106
  - [Project Introduction - What is SFEOS?](#project-introduction---what-is-sfeos)
107
107
  - [Common Deployment Patterns](#common-deployment-patterns)
108
108
  - [Technologies](#technologies)
109
109
  - [Table of Contents](#table-of-contents)
110
110
  - [Collection Search Extensions](#collection-search-extensions)
111
- - [Documentation \& Resources](#documentation--resources)
111
+ - [Documentation & Resources](#documentation--resources)
112
112
  - [Package Structure](#package-structure)
113
113
  - [Examples](#examples)
114
114
  - [Performance](#performance)
@@ -151,7 +151,11 @@ This project is built on the following technologies: STAC, stac-fastapi, FastAPI
151
151
 
152
152
  ## Collection Search Extensions
153
153
 
154
- SFEOS implements extended capabilities for the `/collections` endpoint, allowing for more powerful collection discovery:
154
+ SFEOS provides enhanced collection search capabilities through two primary routes:
155
+ - **GET/POST `/collections`**: The standard STAC endpoint with extended query parameters
156
+ - **GET/POST `/collections-search`**: A custom endpoint that supports the same parameters, created to avoid conflicts with the STAC Transactions extension if enabled (which uses POST `/collections` for collection creation)
157
+
158
+ These endpoints support advanced collection discovery features including:
155
159
 
156
160
  - **Sorting**: Sort collections by sortable fields using the `sortby` parameter
157
161
  - Example: `/collections?sortby=+id` (ascending sort by ID)
@@ -167,9 +171,26 @@ SFEOS implements extended capabilities for the `/collections` endpoint, allowing
167
171
  - Searches across multiple text fields including title, description, and keywords
168
172
  - Supports partial word matching and relevance-based sorting
169
173
 
174
+ - **Structured Filtering**: Filter collections using CQL2 expressions
175
+ - JSON format: `/collections?filter={"op":"=","args":[{"property":"id"},"sentinel-2"]}&filter-lang=cql2-json`
176
+ - Text format: `/collections?filter=id='sentinel-2'&filter-lang=cql2-text` (note: string values must be quoted)
177
+ - Advanced text format: `/collections?filter=id LIKE '%sentinel%'&filter-lang=cql2-text` (supports LIKE, BETWEEN, etc.)
178
+ - Supports both CQL2 JSON and CQL2 text formats with various operators
179
+ - Enables precise filtering on any collection property
180
+
181
+ - **Datetime Filtering**: Filter collections by their temporal extent using the `datetime` parameter
182
+ - Example: `/collections?datetime=2020-01-01T00:00:00Z/2020-12-31T23:59:59Z` (finds collections with temporal extents that overlap this range)
183
+ - Example: `/collections?datetime=2020-06-15T12:00:00Z` (finds collections whose temporal extent includes this specific time)
184
+ - Example: `/collections?datetime=2020-01-01T00:00:00Z/..` (finds collections with temporal extents that extend to or beyond January 1, 2020)
185
+ - Example: `/collections?datetime=../2020-12-31T23:59:59Z` (finds collections with temporal extents that begin on or before December 31, 2020)
186
+ - Collections are matched if their temporal extent overlaps with the provided datetime parameter
187
+ - This allows for efficient discovery of collections based on time periods
188
+
170
189
  These extensions make it easier to build user interfaces that display and navigate through collections efficiently.
171
190
 
172
- > **Configuration**: Collection search extensions can be disabled by setting the `ENABLE_COLLECTIONS_SEARCH` environment variable to `false`. By default, these extensions are enabled.
191
+ > **Configuration**: Collection search extensions (sorting, field selection, free text search, structured filtering, and datetime filtering) for the `/collections` endpoint can be disabled by setting the `ENABLE_COLLECTIONS_SEARCH` environment variable to `false`. By default, these extensions are enabled.
192
+ >
193
+ > **Configuration**: The custom `/collections-search` endpoint can be enabled by setting the `ENABLE_COLLECTIONS_SEARCH_ROUTE` environment variable to `true`. By default, this endpoint is **disabled**.
173
194
 
174
195
  > **Note**: Sorting is only available on fields that are indexed for sorting in Elasticsearch/OpenSearch. With the default mappings, you can sort on:
175
196
  > - `id` (keyword field)
@@ -180,6 +201,7 @@ These extensions make it easier to build user interfaces that display and naviga
180
201
  >
181
202
  > **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.
182
203
 
204
+
183
205
  ## Package Structure
184
206
 
185
207
  This project is organized into several packages, each with a specific purpose:
@@ -192,7 +214,7 @@ This project is organized into several packages, each with a specific purpose:
192
214
  - Shared logic and utilities that improve code reuse between backends
193
215
 
194
216
  - **stac_fastapi_elasticsearch**: Complete implementation of the STAC API using Elasticsearch as the backend database. This package depends on both `stac_fastapi_core` and `sfeos_helpers`.
195
- -
217
+
196
218
  - **stac_fastapi_opensearch**: Complete implementation of the STAC API using OpenSearch as the backend database. This package depends on both `stac_fastapi_core` and `sfeos_helpers`.
197
219
 
198
220
  ## Examples
@@ -310,12 +332,13 @@ You can customize additional settings in your `.env` file:
310
332
  | `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
311
333
  | `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 |
312
334
  | `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 |
313
- | `ENABLE_COLLECTIONS_SEARCH` | Enable collection search extensions (sort, fields). | `true` | Optional |
314
- | `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 |
335
+ | `ENABLE_COLLECTIONS_SEARCH` | Enable collection search extensions (sort, fields, free text search, structured filtering, and datetime filtering) on the core `/collections` endpoint. | `true` | Optional |
336
+ | `ENABLE_COLLECTIONS_SEARCH_ROUTE` | Enable the custom `/collections-search` endpoint (both GET and POST methods). When disabled, the custom endpoint will not be available, but collection search extensions will still be available on the core `/collections` endpoint if `ENABLE_COLLECTIONS_SEARCH` is true. | `false` | Optional |
337
+ | `ENABLE_TRANSACTIONS_EXTENSIONS` | Enables or disables the Transactions and Bulk Transactions API extensions. This is useful for deployments where mutating the catalog via the API should be prevented. If set to `true`, the POST `/collections` route for search will be unavailable in the API. | `true` | Optional |
315
338
  | `STAC_ITEM_LIMIT` | Sets the environment variable for result limiting to SFEOS for the number of returned items and STAC collections. | `10` | Optional |
316
339
  | `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 |
317
340
  | `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 |
318
- | `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 |
341
+ | `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 |
319
342
 
320
343
  > [!NOTE]
321
344
  > 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.
@@ -461,7 +484,6 @@ The system uses a precise naming convention:
461
484
  - `ENABLE_COLLECTIONS_SEARCH`: Set to `true` (default) to enable collection search extensions (sort, fields). Set to `false` to disable.
462
485
  - `ENABLE_TRANSACTIONS_EXTENSIONS`: Set to `true` (default) to enable transaction extensions. Set to `false` to disable.
463
486
 
464
-
465
487
  ## Collection Pagination
466
488
 
467
489
  - **Overview**: The collections route supports pagination through optional query parameters.
@@ -0,0 +1,10 @@
1
+ stac_fastapi/elasticsearch/__init__.py,sha256=w_MZutYLreNV372sCuO46bPb0TngmPs4u8737ueS0wE,31
2
+ stac_fastapi/elasticsearch/app.py,sha256=024U5xvXmSWUJABS9SekbqqamhsNSpqPBII2NBWktz8,9979
3
+ stac_fastapi/elasticsearch/config.py,sha256=itvPYr4TiOg9pWQrycgGaQxQ_Vc2KKP3aHdtH0OUZvw,5322
4
+ stac_fastapi/elasticsearch/database_logic.py,sha256=eNPzK_dBhNJgAgqEpF3hHmqep_7_KGpLVEQA-jusEAY,70183
5
+ stac_fastapi/elasticsearch/version.py,sha256=FuGC3fKnAmD4Wk95swJ6qCVBs5mZiShrlRKuSH-voyE,45
6
+ stac_fastapi_elasticsearch-6.5.1.dist-info/METADATA,sha256=JFd5dRXZznzO6uqbUGS1wVL5YbkyNorypBCQ2T-KVA8,42084
7
+ stac_fastapi_elasticsearch-6.5.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
8
+ stac_fastapi_elasticsearch-6.5.1.dist-info/entry_points.txt,sha256=aCKixki0LpUl64UPsPMtiNvfdyq-QsTCxVjJ54VF6Jk,82
9
+ stac_fastapi_elasticsearch-6.5.1.dist-info/top_level.txt,sha256=vqn-D9-HsRPTTxy0Vk_KkDmTiMES4owwBQ3ydSZYb2s,13
10
+ stac_fastapi_elasticsearch-6.5.1.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- stac_fastapi/elasticsearch/__init__.py,sha256=w_MZutYLreNV372sCuO46bPb0TngmPs4u8737ueS0wE,31
2
- stac_fastapi/elasticsearch/app.py,sha256=BXoewlnMDHU6bwBJY9bV02Gho-ZzmIyESMOUWPMuR9k,7506
3
- stac_fastapi/elasticsearch/config.py,sha256=itvPYr4TiOg9pWQrycgGaQxQ_Vc2KKP3aHdtH0OUZvw,5322
4
- stac_fastapi/elasticsearch/database_logic.py,sha256=6dvex-sEMsl3rBnfTKYey7gyG63wq6KPzOUYRM20r4I,63574
5
- stac_fastapi/elasticsearch/version.py,sha256=1xfRb7s1Stv055oEewNJpZn9OQUUcaJvgSzCsYLHYSQ,45
6
- stac_fastapi_elasticsearch-6.4.0.dist-info/METADATA,sha256=-kQ6Zw1DMLH7WOvdtMCqsWbrC7HxnZ1Jp9xPVpJrqg0,39686
7
- stac_fastapi_elasticsearch-6.4.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
8
- stac_fastapi_elasticsearch-6.4.0.dist-info/entry_points.txt,sha256=aCKixki0LpUl64UPsPMtiNvfdyq-QsTCxVjJ54VF6Jk,82
9
- stac_fastapi_elasticsearch-6.4.0.dist-info/top_level.txt,sha256=vqn-D9-HsRPTTxy0Vk_KkDmTiMES4owwBQ3ydSZYb2s,13
10
- stac_fastapi_elasticsearch-6.4.0.dist-info/RECORD,,