Elasticsearch
The Elasticsearch provider uses elasticsearch-dsl for document store operations, making it suitable for search and analytics workloads.
Overview
Elasticsearch is a document-oriented provider designed for:
- Full-text search across domain data
- Analytics and aggregation workloads
- Read-optimized views when combined with projections
Unlike relational providers, Elasticsearch does not support real transactions or raw queries. It is best used for read-heavy workloads where eventual consistency is acceptable, or alongside a relational provider for write operations.
Installation
pip install "protean[elasticsearch]"
# Or install packages separately
pip install elasticsearch elasticsearch-dsl
Configuration
[databases.elasticsearch]
provider = "elasticsearch"
database_uri = "{'hosts': ['localhost']}"
namespace_prefix = "${PROTEAN_ENV}"
settings = "{'number_of_shards': 3}"
Configuration Options
| Option | Default | Description |
|---|---|---|
provider |
Required | Must be "elasticsearch" for Elasticsearch |
database_uri |
Required | Elasticsearch connection info (hosts dict) |
namespace_prefix |
None |
Prefix for index names (e.g. prod → prod-person) |
namespace_separator |
"-" |
Character joining prefix and index name |
settings |
None |
Index settings passed as-is to Elasticsearch |
Namespace Prefixing
Index names are derived from aggregate class names. When namespace_prefix is
set, it is prepended to every index name:
| Prefix | Separator | Aggregate | Index Name |
|---|---|---|---|
prod |
- |
Person |
prod-person |
prod |
_ |
Person |
prod_person |
| (none) | -- | Person |
person |
Using namespace_prefix = "${PROTEAN_ENV}" lets you share a single
Elasticsearch cluster across environments by giving each environment a distinct
prefix.
Capabilities
The Elasticsearch provider supports the following capabilities:
CRUD -- Create, Read, Update, Delete single records
FILTER -- Query/filter records with lookup criteria
BULK_OPERATIONS --
update_all(),delete_all()ORDERING -- Server-side ordering of results
SCHEMA_MANAGEMENT -- Create/drop indices
OPTIMISTIC_LOCKING -- Version-based concurrency control
TRANSACTIONS -- No transaction support (session has no-op commit/rollback)
SIMULATED_TRANSACTIONS -- Not applicable
RAW_QUERIES -- Not supported
CONNECTION_POOLING -- Managed by the
elasticsearchclient internallyNATIVE_JSON -- Elasticsearch stores JSON natively, but this flag refers to SQL-style JSON columns
NATIVE_ARRAY -- No SQL-style array columns
Indexes
Elasticsearch does not use relational indexes, so portable
Index declarations are not translated into
DDL here. Configure search behavior through the Elasticsearch field mapping
(below) instead. Index declarations on an aggregate remain valid (they are
honored by SQL providers); they are simply not applied by this adapter.
Field Mapping
Protean auto-generates an explicit Elasticsearch mapping for every
aggregate. Each Protean field type is mapped to an appropriate
elasticsearch_dsl field type:
| Protean Field | ES Mapping Type | Notes |
|---|---|---|
String |
keyword |
Exact match, sortable, aggregatable |
Identifier / Auto |
keyword |
Identity fields |
Integer |
integer |
|
Float |
float |
|
Boolean |
boolean |
|
DateTime |
date |
|
Date |
date |
|
Dict |
(dynamic) | Uses ES dynamic mapping |
List |
(dynamic) | Uses ES dynamic mapping |
ValueObjectList |
nested |
Nested objects |
String fields default to keyword — they support exact matching, sorting,
and aggregations out of the box. If you need full-text search with
analyzers, define a custom Elasticsearch Model (see below).
Custom Elasticsearch Model
Supply a custom @domain.model when you need ES-specific field tuning
(analyzers, multi-fields, normalizers, etc.) or custom Index settings.
User-defined fields take precedence; unmapped attributes are filled in
automatically from the aggregate.
import elasticsearch_dsl
@domain.aggregate
class Article:
title: String()
body: String()
category: String()
class Meta:
schema_name = "articles"
@domain.model(part_of=Article)
class ArticleModel:
# Full-text search with .keyword subfield for exact match
title = elasticsearch_dsl.Text(
analyzer="standard",
fields={"keyword": elasticsearch_dsl.Keyword()}
)
# Full-text search only
body = elasticsearch_dsl.Text(analyzer="english")
# category is not listed — auto-mapped as Keyword from the aggregate
class Index:
settings = {"number_of_shards": 1}
Note
When a custom model defines an Index inner class, its settings override
the global settings from the configuration file.
Note
When a custom model defines Text-type fields, lookups like exact,
in, contains, startswith, and endswith on those fields
automatically use the .keyword subfield for exact matching.
Limitations
- No Real Transactions -- Elasticsearch does not support ACID transactions.
The session object provides no-op
commit()androllback()methods. Data is indexed immediately on write. - Eventual Consistency -- Newly indexed documents may not be immediately searchable. Elasticsearch refreshes indices periodically (default: 1 second).
- No Raw Queries -- The
raw()method is not supported. Use the Elasticsearch DSL directly if you need advanced query features. - Requires Running Service -- Elasticsearch must be installed and running.
Use
make upto start Protean's Docker-based development services.
Next Steps
- Learn about database capabilities in detail
- Explore PostgreSQL for transactional workloads
- Understand the ports and adapters architecture