Skip to content

Commit 9baebae

Browse files
committed
working demo updated by precommit
1 parent 9feecfe commit 9baebae

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
- CloudFerro logo to sponsors and supporters list [#485](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/485)
1313
- Latest news section to README [#485](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/485)
14+
- Environment variable `EXCLUDED_FROM_QUERYABLES` to exclude specific fields from queryables endpoint and filtering. Supports comma-separated list of fully qualified field names (e.g., `properties.auth:schemes,properties.storage:schemes`)
1415

1516
### Changed
1617

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ You can customize additional settings in your `.env` file:
313313
| `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 |
314314
| `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 |
315315
| `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 |
316+
| `EXCLUDED_FROM_QUERYABLES` | Comma-separated list of fully qualified field names to exclude from the queryables endpoint and filtering. Use full paths like `properties.auth:schemes,properties.storage:schemes`. Excluded fields and their nested children will not be exposed in queryables. | None | Optional |
316317

317318
> [!NOTE]
318319
> 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.

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter/README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,48 @@ between the two implementations.
99
The filter package is organized into three main modules:
1010

1111
- **cql2.py**: Contains functions for converting CQL2 patterns to Elasticsearch/OpenSearch compatible formats
12+
1213
- [cql2_like_to_es](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter.py:59:0-75:5): Converts CQL2 "LIKE" characters to Elasticsearch "wildcard" characters
13-
- [_replace_like_patterns](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter.py:51:0-56:71): Helper function for pattern replacement
14+
- [\_replace_like_patterns](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter.py:51:0-56:71): Helper function for pattern replacement
1415

1516
- **transform.py**: Contains functions for transforming CQL2 queries to Elasticsearch/OpenSearch query DSL
17+
1618
- [to_es_field](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter.py:83:0-93:47): Maps field names using queryables mapping
1719
- [to_es](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter.py:96:0-201:13): Transforms CQL2 query structures to Elasticsearch/OpenSearch query DSL
1820

1921
- **client.py**: Contains the base filter client implementation
2022
- [EsAsyncBaseFiltersClient](cci:2://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter.py:209:0-293:25): Base class for implementing the STAC filter extension
2123

24+
## Configuration
25+
26+
### Excluding Fields from Queryables
27+
28+
You can exclude specific fields from being exposed in the queryables endpoint and from filtering by setting the `EXCLUDED_FROM_QUERYABLES` environment variable. This is useful for hiding sensitive or internal fields that should not be queryable by API users.
29+
30+
**Environment Variable:**
31+
32+
```bash
33+
EXCLUDED_FROM_QUERYABLES="properties.auth:schemes,properties.storage:schemes,properties.internal:metadata"
34+
```
35+
36+
**Format:**
37+
38+
- Comma-separated list of fully qualified field names
39+
- Use the full path including the `properties.` prefix for item properties
40+
- Example field names:
41+
- `properties.auth:schemes`
42+
- `properties.storage:schemes`
43+
44+
**Behavior:**
45+
46+
- Excluded fields will not appear in the queryables response
47+
- Excluded fields and their nested children will be skipped during field traversal
48+
- Both the field itself and any nested properties will be excluded
49+
2250
## Usage
2351

2452
Import the necessary components from the filter package:
2553

2654
```python
27-
from stac_fastapi.sfeos_helpers.filter import cql2_like_to_es, to_es, EsAsyncBaseFiltersClient
55+
from stac_fastapi.sfeos_helpers.filter import cql2_like_to_es, to_es, EsAsyncBaseFiltersClient
56+
```

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/filter/client.py

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Filter client implementation for Elasticsearch/OpenSearch."""
22

3+
import os
34
from collections import deque
4-
from typing import Any, Dict, Optional, Tuple
5+
from typing import Any
56

67
import attr
78
from fastapi import Request
@@ -18,9 +19,27 @@ class EsAsyncBaseFiltersClient(AsyncBaseFiltersClient):
1819

1920
database: BaseDatabaseLogic = attr.ib()
2021

22+
@staticmethod
23+
def _get_excluded_from_queryables() -> set[str]:
24+
"""Get fields to exclude from queryables endpoint and filtering.
25+
26+
Reads from EXCLUDED_FROM_QUERYABLES environment variable.
27+
Supports comma-separated list of field names.
28+
29+
Example:
30+
EXCLUDED_FROM_QUERYABLES="auth:schemes,storage:schemes"
31+
32+
Returns:
33+
Set[str]: Set of field names to exclude from queryables
34+
"""
35+
excluded = os.getenv("EXCLUDED_FROM_QUERYABLES", "")
36+
if not excluded:
37+
return set()
38+
return {field.strip() for field in excluded.split(",") if field.strip()}
39+
2140
async def get_queryables(
22-
self, collection_id: Optional[str] = None, **kwargs
23-
) -> Dict[str, Any]:
41+
self, collection_id: str | None = None, **kwargs
42+
) -> dict[str, Any]:
2443
"""Get the queryables available for the given collection_id.
2544
2645
If collection_id is None, returns the intersection of all
@@ -38,21 +57,23 @@ async def get_queryables(
3857
Returns:
3958
Dict[str, Any]: A dictionary containing the queryables for the given collection.
4059
"""
41-
request: Optional[Request] = kwargs.get("request")
42-
url_str: str = str(request.url) if request else ""
43-
queryables: Dict[str, Any] = {
60+
request: Request | None = kwargs.get("request")
61+
url_str = str(request.url) if request else ""
62+
63+
queryables: dict[str, Any] = {
4464
"$schema": "https://json-schema.org/draft-07/schema",
45-
"$id": f"{url_str}",
65+
"$id": url_str,
4666
"type": "object",
4767
"title": "Queryables for STAC API",
4868
"description": "Queryable names for the STAC API Item Search filter.",
4969
"properties": DEFAULT_QUERYABLES,
5070
"additionalProperties": True,
5171
}
72+
5273
if not collection_id:
5374
return queryables
5475

55-
properties: Dict[str, Any] = queryables["properties"].copy()
76+
properties = queryables["properties"].copy()
5677
queryables.update(
5778
{
5879
"properties": properties,
@@ -62,8 +83,9 @@ async def get_queryables(
6283

6384
mapping_data = await self.database.get_items_mapping(collection_id)
6485
mapping_properties = next(iter(mapping_data.values()))["mappings"]["properties"]
65-
stack: deque[Tuple[str, Dict[str, Any]]] = deque(mapping_properties.items())
66-
enum_fields: Dict[str, Dict[str, Any]] = {}
86+
stack: deque[tuple[str, dict[str, Any]]] = deque(mapping_properties.items())
87+
enum_fields: dict[str, dict[str, Any]] = {}
88+
excluded_fields = self._get_excluded_from_queryables()
6789

6890
while stack:
6991
field_fqn, field_def = stack.popleft()
@@ -75,11 +97,16 @@ async def get_queryables(
7597
(f"{field_fqn}.{k}", v)
7698
for k, v in field_properties.items()
7799
if v.get("enabled", True)
100+
and f"{field_fqn}.{k}" not in excluded_fields
78101
)
79102

80103
# Skip non-indexed or disabled fields
81104
field_type = field_def.get("type")
82-
if not field_type or not field_def.get("enabled", True):
105+
if (
106+
not field_type
107+
or not field_def.get("enabled", True)
108+
or field_fqn in excluded_fields
109+
):
83110
continue
84111

85112
# Fields in Item Properties should be exposed with their un-prefixed names,
@@ -88,7 +115,7 @@ async def get_queryables(
88115
field_name = field_fqn.removeprefix("properties.")
89116

90117
# Generate field properties
91-
field_result = ALL_QUERYABLES.get(field_name, {})
118+
field_result = ALL_QUERYABLES.get(field_name, {}).copy()
92119
properties[field_name] = field_result
93120

94121
field_name_human = field_name.replace("_", " ").title()
@@ -104,9 +131,10 @@ async def get_queryables(
104131
enum_fields[field_fqn] = field_result
105132

106133
if enum_fields:
107-
for field_fqn, unique_values in (
108-
await self.database.get_items_unique_values(collection_id, enum_fields)
109-
).items():
110-
enum_fields[field_fqn]["enum"] = unique_values
134+
unique_values = await self.database.get_items_unique_values(
135+
collection_id, enum_fields
136+
)
137+
for field_fqn, values in unique_values.items():
138+
enum_fields[field_fqn]["enum"] = values
111139

112140
return queryables

0 commit comments

Comments
 (0)