Skip to content

Commit

Permalink
Merge pull request #209 from StijnCaerts/#208-index-template
Browse files Browse the repository at this point in the history
Index templates
  • Loading branch information
jonhealy1 authored Mar 15, 2024
2 parents f34e249 + 3b572e9 commit d3c9138
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

- use index templates for Collection and Item indices [#208](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/discussions/208)
- Added API `title`, `version`, and `description` parameters from environment variables `STAC_FASTAPI_TITLE`, `STAC_FASTAPI_VERSION` and `STAC_FASTAPI_DESCRIPTION`, respectively. [#207](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/207)

### Changed
Expand Down
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ make ingest

## Elasticsearch Mappings

Mappings apply to search index, not source.
Mappings apply to search index, not source. The mappings are stored in index templates on application startup.
These templates will be used implicitly when creating new Collection and Item indices.


## Managing Elasticsearch Indices
### Snapshots

This section covers how to create a snapshot repository and then create and restore snapshots with this.

Expand Down Expand Up @@ -219,3 +221,52 @@ curl -X "POST" "http://localhost:8080/collections" \

Voila! You have a copy of the collection now that has a resource URI (`/collections/my-collection-copy`) and can be
correctly queried by collection name.

### Reindexing
This section covers how to reindex documents stored in Elasticsearch/OpenSearch.
A reindex operation might be useful to apply changes to documents or to correct dynamically generated mappings.

The index templates will make sure that manually created indices will also have the correct mappings and settings.

In this example, we will make a copy of an existing Item index `items_my-collection-000001` but change the Item identifier to be lowercase.

```shell
curl -X "POST" "http://localhost:9200/_reindex" \
-H 'Content-Type: application/json' \
-d $'{
"source": {
"index": "items_my-collection-000001"
},
"dest": {
"index": "items_my-collection-000002"
},
"script": {
"source": "ctx._source.id = ctx._source.id.toLowerCase()",
"lang": "painless"
}
}'
```

If we are happy with the data in the newly created index, we can move the alias `items_my-collection` to the new index `items_my-collection-000002`.
```shell
curl -X "POST" "http://localhost:9200/_aliases" \
-h 'Content-Type: application/json' \
-d $'{
"actions": [
{
"remove": {
"index": "*",
"alias": "items_my-collection"
}
},
{
"add": {
"index": "items_my-collection-000002",
"alias": "items_my-collection"
}
}
]
}'
```

The modified Items with lowercase identifiers will now be visible to users accessing `my-collection` in the STAC API.
2 changes: 2 additions & 0 deletions stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from stac_fastapi.elasticsearch.database_logic import (
DatabaseLogic,
create_collection_index,
create_index_templates,
)
from stac_fastapi.extensions.core import (
ContextExtension,
Expand Down Expand Up @@ -78,6 +79,7 @@

@app.on_event("startup")
async def _startup_event() -> None:
await create_index_templates()
await create_collection_index()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,36 @@ def indices(collection_ids: Optional[List[str]]) -> str:
return ",".join([index_by_collection_id(c) for c in collection_ids])


async def create_index_templates() -> None:
"""
Create index templates for the Collection and Item indices.
Returns:
None
"""
client = AsyncElasticsearchSettings().create_client
await client.indices.put_template(
name=f"template_{COLLECTIONS_INDEX}",
body={
"index_patterns": [f"{COLLECTIONS_INDEX}*"],
"mappings": ES_COLLECTIONS_MAPPINGS,
},
)
await client.indices.put_template(
name=f"template_{ITEMS_INDEX_PREFIX}",
body={
"index_patterns": [f"{ITEMS_INDEX_PREFIX}*"],
"settings": ES_ITEMS_SETTINGS,
"mappings": ES_ITEMS_MAPPINGS,
},
)
await client.close()


async def create_collection_index() -> None:
"""
Create the index for a Collection.
Create the index for a Collection. The settings of the index template will be used implicitly.
Returns:
None
Expand All @@ -184,14 +211,13 @@ async def create_collection_index() -> None:
await client.options(ignore_status=400).indices.create(
index=f"{COLLECTIONS_INDEX}-000001",
aliases={COLLECTIONS_INDEX: {}},
mappings=ES_COLLECTIONS_MAPPINGS,
)
await client.close()


async def create_item_index(collection_id: str):
"""
Create the index for Items.
Create the index for Items. The settings of the index template will be used implicitly.
Args:
collection_id (str): Collection identifier.
Expand All @@ -206,8 +232,6 @@ async def create_item_index(collection_id: str):
await client.options(ignore_status=400).indices.create(
index=f"{index_by_collection_id(collection_id)}-000001",
aliases={index_name: {}},
mappings=ES_ITEMS_MAPPINGS,
settings=ES_ITEMS_SETTINGS,
)
await client.close()

Expand Down
2 changes: 2 additions & 0 deletions stac_fastapi/opensearch/stac_fastapi/opensearch/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from stac_fastapi.opensearch.database_logic import (
DatabaseLogic,
create_collection_index,
create_index_templates,
)

settings = OpensearchSettings()
Expand Down Expand Up @@ -78,6 +79,7 @@

@app.on_event("startup")
async def _startup_event() -> None:
await create_index_templates()
await create_collection_index()


Expand Down
38 changes: 31 additions & 7 deletions stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,18 +173,44 @@ def indices(collection_ids: Optional[List[str]]) -> str:
return ",".join([index_by_collection_id(c) for c in collection_ids])


async def create_index_templates() -> None:
"""
Create index templates for the Collection and Item indices.
Returns:
None
"""
client = AsyncSearchSettings().create_client
await client.indices.put_template(
name=f"template_{COLLECTIONS_INDEX}",
body={
"index_patterns": [f"{COLLECTIONS_INDEX}*"],
"mappings": ES_COLLECTIONS_MAPPINGS,
},
)
await client.indices.put_template(
name=f"template_{ITEMS_INDEX_PREFIX}",
body={
"index_patterns": [f"{ITEMS_INDEX_PREFIX}*"],
"settings": ES_ITEMS_SETTINGS,
"mappings": ES_ITEMS_MAPPINGS,
},
)
await client.close()


async def create_collection_index() -> None:
"""
Create the index for a Collection.
Create the index for a Collection. The settings of the index template will be used implicitly.
Returns:
None
"""
client = AsyncSearchSettings().create_client

search_body = {
"mappings": ES_COLLECTIONS_MAPPINGS,
search_body: Dict[str, Any] = {
"aliases": {COLLECTIONS_INDEX: {}},
}

Expand All @@ -203,7 +229,7 @@ async def create_collection_index() -> None:

async def create_item_index(collection_id: str):
"""
Create the index for Items.
Create the index for Items. The settings of the index template will be used implicitly.
Args:
collection_id (str): Collection identifier.
Expand All @@ -214,10 +240,8 @@ async def create_item_index(collection_id: str):
"""
client = AsyncSearchSettings().create_client
index_name = index_by_collection_id(collection_id)
search_body = {
search_body: Dict[str, Any] = {
"aliases": {index_name: {}},
"mappings": ES_ITEMS_MAPPINGS,
"settings": ES_ITEMS_SETTINGS,
}

try:
Expand Down
3 changes: 3 additions & 0 deletions stac_fastapi/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from stac_fastapi.opensearch.database_logic import (
DatabaseLogic,
create_collection_index,
create_index_templates,
)
else:
from stac_fastapi.elasticsearch.config import (
Expand All @@ -32,6 +33,7 @@
from stac_fastapi.elasticsearch.database_logic import (
DatabaseLogic,
create_collection_index,
create_index_templates,
)

from stac_fastapi.extensions.core import ( # FieldsExtension,
Expand Down Expand Up @@ -215,6 +217,7 @@ async def app():

@pytest_asyncio.fixture(scope="session")
async def app_client(app):
await create_index_templates()
await create_collection_index()

async with AsyncClient(app=app, base_url="http://test-server") as c:
Expand Down
Empty file.
51 changes: 51 additions & 0 deletions stac_fastapi/tests/database/test_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os
import uuid
from copy import deepcopy

import pytest

from ..conftest import MockRequest, database

if os.getenv("BACKEND", "elasticsearch").lower() == "opensearch":
from stac_fastapi.opensearch.database_logic import (
COLLECTIONS_INDEX,
ES_COLLECTIONS_MAPPINGS,
ES_ITEMS_MAPPINGS,
index_by_collection_id,
)
else:
from stac_fastapi.elasticsearch.database_logic import (
COLLECTIONS_INDEX,
ES_COLLECTIONS_MAPPINGS,
ES_ITEMS_MAPPINGS,
index_by_collection_id,
)


@pytest.mark.asyncio
async def test_index_mapping_collections(ctx):
response = await database.client.indices.get_mapping(index=COLLECTIONS_INDEX)
if not isinstance(response, dict):
response = response.body
actual_mappings = next(iter(response.values()))["mappings"]
assert (
actual_mappings["dynamic_templates"]
== ES_COLLECTIONS_MAPPINGS["dynamic_templates"]
)


@pytest.mark.asyncio
async def test_index_mapping_items(ctx, txn_client):
collection = deepcopy(ctx.collection)
collection["id"] = str(uuid.uuid4())
await txn_client.create_collection(collection, request=MockRequest)
response = await database.client.indices.get_mapping(
index=index_by_collection_id(collection["id"])
)
if not isinstance(response, dict):
response = response.body
actual_mappings = next(iter(response.values()))["mappings"]
assert (
actual_mappings["dynamic_templates"] == ES_ITEMS_MAPPINGS["dynamic_templates"]
)
await txn_client.delete_collection(collection["id"])

0 comments on commit d3c9138

Please sign in to comment.