Skip to content

Commit a432274

Browse files
committed
database package
1 parent c0d5aac commit a432274

File tree

8 files changed

+281
-223
lines changed

8 files changed

+281
-223
lines changed

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
ElasticsearchSettings as SyncElasticsearchSettings,
2222
)
2323
from stac_fastapi.sfeos_helpers import filter
24-
from stac_fastapi.sfeos_helpers.database_logic_helpers import (
24+
from stac_fastapi.sfeos_helpers.database import (
2525
apply_free_text_filter_shared,
2626
apply_intersects_filter_shared,
2727
create_index_templates_shared,

stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
)
2222
from stac_fastapi.opensearch.config import OpensearchSettings as SyncSearchSettings
2323
from stac_fastapi.sfeos_helpers import filter
24-
from stac_fastapi.sfeos_helpers.database_logic_helpers import (
24+
from stac_fastapi.sfeos_helpers.database import (
2525
apply_free_text_filter_shared,
2626
apply_intersects_filter_shared,
2727
create_index_templates_shared,
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# STAC FastAPI Database Package
2+
3+
This package contains shared database operations used by both the Elasticsearch and OpenSearch
4+
implementations of STAC FastAPI. It helps reduce code duplication and ensures consistent behavior
5+
between the two implementations.
6+
7+
## Package Structure
8+
9+
The database package is organized into three main modules:
10+
11+
- **index.py**: Contains functions for managing indices
12+
- [create_index_templates_shared](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py:15:0-48:33): Creates index templates for Collections and Items
13+
- [delete_item_index_shared](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py:128:0-153:30): Deletes an item index for a collection
14+
15+
- **query.py**: Contains functions for building and manipulating queries
16+
- [apply_free_text_filter_shared](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py:51:0-74:16): Applies a free text filter to a search
17+
- [apply_intersects_filter_shared](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py:77:0-104:5): Creates a geo_shape filter for intersecting geometry
18+
- [populate_sort_shared](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py:107:0-125:16): Creates a sort configuration for queries
19+
20+
- **mapping.py**: Contains functions for working with mappings
21+
- [get_queryables_mapping_shared](cci:1://file:///home/computer/Code/stac-fastapi-elasticsearch-opensearch/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py:156:0-185:27): Retrieves mapping of Queryables for search
22+
23+
## Usage
24+
25+
Import the necessary components from the database package:
26+
27+
```python
28+
from stac_fastapi.sfeos_helpers.database import (
29+
create_index_templates_shared,
30+
delete_item_index_shared,
31+
apply_free_text_filter_shared,
32+
apply_intersects_filter_shared,
33+
populate_sort_shared,
34+
get_queryables_mapping_shared,
35+
)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Shared database operations for stac-fastapi elasticsearch and opensearch backends.
2+
3+
This module provides shared database functionality used by both the Elasticsearch and OpenSearch
4+
implementations of STAC FastAPI. It includes:
5+
6+
1. Index management functions for creating and deleting indices
7+
2. Query building functions for constructing search queries
8+
3. Mapping functions for working with Elasticsearch/OpenSearch mappings
9+
10+
The database package is organized as follows:
11+
- index.py: Index management functions
12+
- query.py: Query building functions
13+
- mapping.py: Mapping functions
14+
15+
When adding new functionality to this package, consider:
16+
1. Will this code be used by both Elasticsearch and OpenSearch implementations?
17+
2. Is the functionality stable and unlikely to diverge between implementations?
18+
3. Is the function well-documented with clear input/output contracts?
19+
20+
Function Naming Conventions:
21+
- All shared functions should end with `_shared` to clearly indicate they're meant to be used by both implementations
22+
- Function names should be descriptive and indicate their purpose
23+
- Parameter names should be consistent across similar functions
24+
"""
25+
26+
# Re-export all functions for backward compatibility
27+
from .index import create_index_templates_shared, delete_item_index_shared
28+
from .mapping import get_queryables_mapping_shared
29+
from .query import (
30+
apply_free_text_filter_shared,
31+
apply_intersects_filter_shared,
32+
populate_sort_shared,
33+
)
34+
35+
__all__ = [
36+
"create_index_templates_shared",
37+
"delete_item_index_shared",
38+
"apply_free_text_filter_shared",
39+
"apply_intersects_filter_shared",
40+
"populate_sort_shared",
41+
"get_queryables_mapping_shared",
42+
]
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Index management functions for Elasticsearch/OpenSearch.
2+
3+
This module provides functions for creating and managing indices in Elasticsearch/OpenSearch.
4+
"""
5+
6+
from typing import Any
7+
8+
from stac_fastapi.sfeos_helpers.mappings import (
9+
COLLECTIONS_INDEX,
10+
ES_COLLECTIONS_MAPPINGS,
11+
ES_ITEMS_MAPPINGS,
12+
ES_ITEMS_SETTINGS,
13+
ITEMS_INDEX_PREFIX,
14+
)
15+
from stac_fastapi.sfeos_helpers.utilities import index_alias_by_collection_id
16+
17+
18+
async def create_index_templates_shared(settings: Any) -> None:
19+
"""Create index templates for Elasticsearch/OpenSearch Collection and Item indices.
20+
21+
Args:
22+
settings (Any): The settings object containing the client configuration.
23+
Must have a create_client attribute that returns an Elasticsearch/OpenSearch client.
24+
25+
Returns:
26+
None: This function doesn't return any value but creates index templates in the database.
27+
28+
Notes:
29+
This function creates two index templates:
30+
1. A template for the Collections index with the appropriate mappings
31+
2. A template for the Items indices with both settings and mappings
32+
33+
These templates ensure that any new indices created with matching patterns
34+
will automatically have the correct structure.
35+
"""
36+
client = settings.create_client
37+
await client.indices.put_index_template(
38+
name=f"template_{COLLECTIONS_INDEX}",
39+
body={
40+
"index_patterns": [f"{COLLECTIONS_INDEX}*"],
41+
"template": {"mappings": ES_COLLECTIONS_MAPPINGS},
42+
},
43+
)
44+
await client.indices.put_index_template(
45+
name=f"template_{ITEMS_INDEX_PREFIX}",
46+
body={
47+
"index_patterns": [f"{ITEMS_INDEX_PREFIX}*"],
48+
"template": {"settings": ES_ITEMS_SETTINGS, "mappings": ES_ITEMS_MAPPINGS},
49+
},
50+
)
51+
await client.close()
52+
53+
54+
async def delete_item_index_shared(settings: Any, collection_id: str) -> None:
55+
"""Delete the index for items in a collection.
56+
57+
Args:
58+
settings (Any): The settings object containing the client configuration.
59+
Must have a create_client attribute that returns an Elasticsearch/OpenSearch client.
60+
collection_id (str): The ID of the collection whose items index will be deleted.
61+
62+
Returns:
63+
None: This function doesn't return any value but deletes an item index in the database.
64+
65+
Notes:
66+
This function deletes an item index and its alias. It first resolves the alias to find
67+
the actual index name, then deletes both the alias and the index.
68+
"""
69+
client = settings.create_client
70+
71+
name = index_alias_by_collection_id(collection_id)
72+
resolved = await client.indices.resolve_index(name=name)
73+
if "aliases" in resolved and resolved["aliases"]:
74+
[alias] = resolved["aliases"]
75+
await client.indices.delete_alias(index=alias["indices"], name=alias["name"])
76+
await client.indices.delete(index=alias["indices"])
77+
else:
78+
await client.indices.delete(index=name)
79+
await client.close()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Mapping functions for Elasticsearch/OpenSearch.
2+
3+
This module provides functions for working with Elasticsearch/OpenSearch mappings.
4+
"""
5+
6+
from typing import Any, Dict
7+
8+
9+
async def get_queryables_mapping_shared(
10+
mappings: Dict[str, Dict[str, Any]], collection_id: str = "*"
11+
) -> Dict[str, str]:
12+
"""Retrieve mapping of Queryables for search.
13+
14+
Args:
15+
mappings (Dict[str, Dict[str, Any]]): The mapping information returned from
16+
Elasticsearch/OpenSearch client's indices.get_mapping() method.
17+
Expected structure is {index_name: {"mappings": {...}}}.
18+
collection_id (str, optional): The id of the Collection the Queryables
19+
belongs to. Defaults to "*".
20+
21+
Returns:
22+
Dict[str, str]: A dictionary containing the Queryables mappings, where keys are
23+
field names and values are the corresponding paths in the Elasticsearch/OpenSearch
24+
document structure.
25+
"""
26+
queryables_mapping = {}
27+
28+
for mapping in mappings.values():
29+
fields = mapping["mappings"].get("properties", {})
30+
properties = fields.pop("properties", {}).get("properties", {}).keys()
31+
32+
for field_key in fields:
33+
queryables_mapping[field_key] = field_key
34+
35+
for property_key in properties:
36+
queryables_mapping[property_key] = f"properties.{property_key}"
37+
38+
return queryables_mapping
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""Query building functions for Elasticsearch/OpenSearch.
2+
3+
This module provides functions for building and manipulating Elasticsearch/OpenSearch queries.
4+
"""
5+
6+
from typing import Any, Dict, List, Optional
7+
8+
from stac_fastapi.sfeos_helpers.mappings import Geometry
9+
10+
11+
def apply_free_text_filter_shared(
12+
search: Any, free_text_queries: Optional[List[str]]
13+
) -> Any:
14+
"""Create a free text query for Elasticsearch/OpenSearch.
15+
16+
Args:
17+
search (Any): The search object to apply the query to.
18+
free_text_queries (Optional[List[str]]): A list of text strings to search for in the properties.
19+
20+
Returns:
21+
Any: The search object with the free text query applied, or the original search
22+
object if no free_text_queries were provided.
23+
24+
Notes:
25+
This function creates a query_string query that searches for the specified text strings
26+
in all properties of the documents. The query strings are joined with OR operators.
27+
"""
28+
if free_text_queries is not None:
29+
free_text_query_string = '" OR properties.\\*:"'.join(free_text_queries)
30+
search = search.query(
31+
"query_string", query=f'properties.\\*:"{free_text_query_string}"'
32+
)
33+
34+
return search
35+
36+
37+
def apply_intersects_filter_shared(
38+
intersects: Geometry,
39+
) -> Dict[str, Dict]:
40+
"""Create a geo_shape filter for intersecting geometry.
41+
42+
Args:
43+
intersects (Geometry): The intersecting geometry, represented as a GeoJSON-like object.
44+
45+
Returns:
46+
Dict[str, Dict]: A dictionary containing the geo_shape filter configuration
47+
that can be used with Elasticsearch/OpenSearch Q objects.
48+
49+
Notes:
50+
This function creates a geo_shape filter configuration to find documents that intersect
51+
with the specified geometry. The returned dictionary should be wrapped in a Q object
52+
when applied to a search.
53+
"""
54+
return {
55+
"geo_shape": {
56+
"geometry": {
57+
"shape": {
58+
"type": intersects.type.lower(),
59+
"coordinates": intersects.coordinates,
60+
},
61+
"relation": "intersects",
62+
}
63+
}
64+
}
65+
66+
67+
def populate_sort_shared(sortby: List) -> Optional[Dict[str, Dict[str, str]]]:
68+
"""Create a sort configuration for Elasticsearch/OpenSearch queries.
69+
70+
Args:
71+
sortby (List): A list of sort specifications, each containing a field and direction.
72+
73+
Returns:
74+
Optional[Dict[str, Dict[str, str]]]: A dictionary mapping field names to sort direction
75+
configurations, or None if no sort was specified.
76+
77+
Notes:
78+
This function transforms a list of sort specifications into the format required by
79+
Elasticsearch/OpenSearch for sorting query results. The returned dictionary can be
80+
directly used in search requests.
81+
"""
82+
if sortby:
83+
return {s.field: {"order": s.direction} for s in sortby}
84+
else:
85+
return None

0 commit comments

Comments
 (0)