Elasticsearch & OpenSearch

Elasticsearch is a distributed, JSON-document search and analytics engine built on Apache Lucene, originally released by Shay Banon in 2010. OpenSearch is the open-source fork created in 2021 by AWS after Elastic relicensed Elasticsearch under the SSPL/Elastic License; OpenSearch is Apache 2.0 and a drop-in replacement for the Elasticsearch 7.x APIs. Both engines are simultaneously a NoSQL document store, a distributed full-text search engine, and a real-time analytics platform.

OpenSearch is the Apache 2.0 fork of Elasticsearch 7.10 that AWS started in 2021 after Elastic moved Elasticsearch to non-open-source licences. (Elastic later added AGPL as an additional licence option in 2024.) The two engines share the same core design, a cluster of nodes holding Lucene indexes split into shards, and the same basic REST API, so most of this page applies to both.

Cluster architecture

Index, shards and replicas across a three-node cluster Client request REST over HTTP to any node; that node coordinates the query Cluster (3 data nodes) Node 1 P0, R1 Node 2 P1, R2 Node 3 P2, R0 Each shard = one Lucene index segments: inverted index, doc values, stored fields, vectors P = primary shard, R = replica. A replica is never placed on the same node as its primary.

An index is a logical collection of documents divided into a fixed number of primary shards, each a self-contained Lucene index. Replicas are copies of primaries on other nodes; they provide failover and extra read capacity. A search is fanned out to one copy of every shard, each shard returns its local top hits, and the coordinating node merges them into the final ranking. The number of primary shards is set at index creation, so it is usually sized for expected data volume, with indexes rolled over by time for logs.

Key Features:

Elasticsearch vs. OpenSearch:

Use Cases:

Write and search path

From indexed document to searchable segment Index request PUT /index/_doc/1 Primary shard analyze fields, write translog Replicas same operation applied in parallel Refresh (~1 s) buffer becomes searchable segment Merge small segments combined over time Search is near real-time: a document becomes visible after the next refresh, not at the moment of the write.

Text fields are analyzed (tokenized, lower-cased, optionally stemmed) at write time, and the resulting terms go into the inverted index; keyword, numeric, and date fields go into column-oriented doc values used for sorting and aggregations. The translog makes acknowledged writes durable before segments are flushed to disk. Because segments are immutable, updates are implemented as delete plus reinsert, and deleted documents are only physically removed when segments merge.

Worked example: mapping, indexing, querying

# Create an index with an explicit mapping
curl -X PUT localhost:9200/articles -H 'Content-Type: application/json' -d '{
  "settings": { "number_of_shards": 1, "number_of_replicas": 1 },
  "mappings": { "properties": {
      "title":     { "type": "text" },
      "body":      { "type": "text", "analyzer": "english" },
      "tags":      { "type": "keyword" },
      "published": { "type": "date" }
  } }
}'

# Index a document
curl -X PUT localhost:9200/articles/_doc/1 -H 'Content-Type: application/json' -d '{
  "title": "Hybrid search for RAG", "body": "Combining BM25 with dense vectors...",
  "tags": ["rag", "search"], "published": "2025-06-01"
}'

# Full-text query with a filter and an aggregation
curl -X POST localhost:9200/articles/_search -H 'Content-Type: application/json' -d '{
  "query": { "bool": {
      "must":   { "multi_match": { "query": "bm25 vectors", "fields": ["title^2", "body"] } },
      "filter": { "range": { "published": { "gte": "2025-01-01" } } }
  } },
  "aggs": { "by_tag": { "terms": { "field": "tags" } } }
}'

The bool query separates scoring clauses (must) from non-scoring filters (filter), which are cached and cheaper. title^2 doubles the weight of title matches. The terms aggregation runs on the keyword field's doc values; running it on a text field fails by default.

When to use it vs alternatives

NeedElasticsearch / OpenSearchAlternative
Relevance-ranked full-text search at scaleCore strengthPostgreSQL full-text for small data; Whoosh for local tools
Log and event analytics with dashboardsCore strengthLoki, ClickHouse for cheaper storage
System of record with transactionsNot suitableRelational database or MongoDB
Pure vector searchSupported (HNSW)Dedicated vector databases, pgvector
Metrics time seriesWorkableInfluxDB, Prometheus

Pitfalls