Skip to content

Elasticsearch and OpenSearch

ElasticsearchStore and OpenSearchStore implement the same async vector-store contract as the core stores. They keep protoprompt's replace-on-add behavior, metadata filters, cosine score threshold, and source doc_id metadata.

This adapter is dense-vector retrieval only. Sparse+dense hybrid retrieval stays in the research backlog until it has a stable cross-backend contract.

Install and create the index

pip install "protoprompt[elasticsearch]"  # Elasticsearch 9.x client
# or
pip install "protoprompt[opensearch]"     # OpenSearch 3.1 async client

Constructors do not mutate server schema. Call setup() explicitly during a deployment or migration:

from protoprompt.integrations import ElasticsearchStore

store = ElasticsearchStore(
    "https://search.example.com:9200",
    index_name="protoprompt-memory-v1",
    dimensions=1536,
    api_key="...",  # forwarded to the official client
)
await store.setup()

OpenSearch accepts its official client options in the same way. For AWS request signing, build an AsyncOpenSearch client with the approved signer and inject it with client=. The host owns injected clients; close() only closes clients the adapter created.

String metadata is mapped to keyword, so equality and $in keep exact values. OpenSearch uses Lucene HNSW because inline k-NN filtering is supported by that engine. Both adapters recalculate cosine similarity from the stored vector so score_threshold means the same thing on both servers.

Local live test

The compose file disables authentication and is test-only:

docker compose -f docker-compose.search.yml up -d --wait
PROTOPROMPT_ELASTICSEARCH_URL=http://localhost:9200 \
PROTOPROMPT_OPENSEARCH_URL=http://localhost:9201 \
pytest -m integration tests/integration/test_search_live.py
docker compose -f docker-compose.search.yml down

Run the example with python examples/search_vector_store.py. Set SEARCH_BACKEND=opensearch to use port 9201.

Migration and rollback

Create a versioned index, populate it through DocumentIndexer, compare counts and sampled queries, then switch the application configuration. Do not point the adapter at an existing index with an incompatible vector dimension.

Rollback is a configuration switch to the previous index. Keep the old index read-only until the observation window ends; deleting it is a separate operator decision. setup() never modifies an existing mapping.

Supported client lines are declared by the extras. Dependency updates require the contract suite plus both opt-in live tests. An incompatible server line gets a new major extra constraint or adapter dialect instead of silent behavior changes.