Integrates Elasticsearch using elasticsearch-py 8.x with patterns for
Scanned 9/4/2026
Install to Claude Code
npx -y skills add paulpas/agent-skill-router --skill elasticsearch-sdk --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Elasticsearch Sdk?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/paulpas-elasticsearch-sdk)More formats (shields.io, HTML) on the badges page.
---
name: elasticsearch-sdk
description: Integrates Elasticsearch using elasticsearch-py 8.x with patterns for
indexing, search queries, aggregations, vector search, bulk operations, and index
lifecycle management.
license: MIT
compatibility: opencode
metadata:
version: "1.0.0"
domain: coding
triggers: elasticsearch, elasticsearch-py, opensearch, full-text search, elastic
aggregations, how do i search data from python, elk stack, vector search
archetypes:
- tactical
- generation
anti_triggers:
- brainstorming
- vague ideation
- code golf
- over-engineering
response_profile:
verbosity: low
directive_strength: high
abstraction_level: operational
role: implementation
scope: implementation
output-format: code
content-types:
- code
- guidance
- do-dont
- examples
related-skills: coding-mongodb-driver, coding-postgresql-sdk, coding-rag-architecture
---
# Elasticsearch Python SDK Integration
Integrates Elasticsearch using `elasticsearch-py` 8.x (also compatible with OpenSearch) with patterns for index management, search queries (term, match, bool), aggregations, bulk indexing, vector/kNN search, index lifecycle management (ILM), and async operations.
## TL;DR Checklist
- [ ] Use `Elasticsearch()` client with `basic_auth` or `api_key` for authentication
- [ ] Use `helpers.bulk()` for high-throughput indexing — never index documents one-by-one
- [ ] Use `bool` query with `must`/`filter`/`should` clauses for complex search
- [ ] Use `aggs` for bucket and metric aggregations, not client-side grouping
- [ ] Use `knn` query for vector similarity search (Elasticsearch 8.x+)
- [ ] Use `index.create()` with explicit mappings — never rely on dynamic mapping for production
- [ ] Use `point_in_time` (PIT) + `search_after` for deep pagination instead of `from`/`size`
---
## When to Use
Use this skill when:
- Building full-text search functionality for applications
- Implementing log aggregation and analysis with Elasticsearch
- Performing real-time analytics with bucket and metric aggregations
- Implementing vector/kNN search for semantic or similarity-based retrieval
- Building RAG (Retrieval-Augmented Generation) pipelines with Elasticsearch as a vector store
- Managing time-series indices with ILM policies and rollover
- Migrating from or integrating with OpenSearch (API-compatible)
---
## When NOT to Use
- For transaction-heavy OLTP workloads (use PostgreSQL or MongoDB)
- When you need a primary data store with strong consistency guarantees
- For simple key-value lookups (use Redis or DynamoDB)
- For complex joins across multiple entities (use a relational database)
- When your data fits in memory and doesn't need search capabilities (keep it simple)
---
## Core Workflow
### 1. Connect to Elasticsearch
```python
from elasticsearch import Elasticsearch, helpers
from elasticsearch.exceptions import (
ConnectionError as ESConnectionError,
NotFoundError,
AuthorizationException,
)
# Elastic Cloud or self-managed cluster
es = Elasticsearch(
"https://localhost:9200",
basic_auth=("elastic", os.environ["ES_PASSWORD"]),
ca_certs="/path/to/ca.crt",
request_timeout=30,
max_retries=3,
retry_on_timeout=True,
)
# Verify connectivity
if not es.ping():
raise RuntimeError("Cannot connect to Elasticsearch")
```
**Checkpoint:** Always verify with `es.ping()` or `es.info()` at startup. Catch `AuthorizationException` immediately on auth failures — don't proceed with invalid credentials.
### 2. Create Index with Explicit Mappings
```python
def create_articles_index(es: Elasticsearch, index_name: str) -> dict:
"""Create an index with explicit mappings and settings."""
mappings = {
"properties": {
"title": {
"type": "text",
"fields": {"keyword": {"type": "keyword"}},
"analyzer": "english",
},
"content": {"type": "text", "analyzer": "english"},
"author": {"type": "keyword"},
"published_at": {"type": "date"},
"tags": {"type": "keyword"},
"view_count": {"type": "integer"},
"embedding": {
"type": "dense_vector",
"dims": 768,
"index": True,
"similarity": "cosine",
},
}
}
settings = {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "30s",
}
if not es.indices.exists(index=index_name):
return es.indices.create(index=index_name, mappings=mappings, settings=settings)
return {"acknowledged": True, "existing": True}
```
**Checkpoint:** Always define explicit mappings before indexing production data. Use `keyword` type for exact-match fields and `text` with analyzers for full-text search. Add `dense_vector` fields for vector search.
### 3. Index Documents with Bulk API
```python
def bulk_index_articles(es: Elasticsearch, index_name: str, articles: list[dict]) -> dict:
"""Bulk index articles with error handling."""
def generate_actions():
for article in articles:
yield {
"_index": index_name,
"_id": article.get("id"),
"_source": {
"title": article["title"],
"content": article["content"],
"author": article["author"],
"published_at": article["published_at"],
"tags": article.get("tags", []),
"view_count": article.get("view_count", 0),
"embedding": article.get("embedding"),
},
}
success, errors = helpers.bulk(
es,
generate_actions(),
chunk_size=500,
request_timeout=60,
raise_on_error=False,
)
return {"indexed": success, "errors": len(errors)}
```
**Checkpoint:** Use `raise_on_error=False` and inspect errors for failures. Use `chunk_size` to balance throughput and memory. Always check `errors` count after bulk operations.
### 4. Search with Bool Query
```python
def search_articles(
es: Elasticsearch,
index_name: str,
query_text: str,
author: str | None = None,
tags: list[str] | None = None,
min_date: str | None = None,
from_: int = 0,
size: int = 20,
) -> dict:
"""Full-text search with optional filters."""
must_clauses = [{"match": {"content": query_text}}]
filter_clauses = []
if author:
filter_clauses.append({"term": {"author": author}})
if tags:
filter_clauses.append({"terms": {"tags": tags}})
if min_date:
filter_clauses.append({"range": {"published_at": {"gte": min_date}}})
body = {
"query": {
"bool": {
"must": must_clauses,
"filter": filter_clauses,
}
},
"sort": [{"_score": "desc"}, {"published_at": "desc"}],
"from_": from_,
"size": size,
"aggs": {
"by_author": {"terms": {"field": "author", "size": 10}},
"date_histogram": {"date_histogram": {"field": "published_at", "calendar_interval": "month"}},
},
}
return es.search(index=index_name, body=body)
```
**Checkpoint:** Use `filter` context for structured data (dates, keywords) — filter clauses are cached and don't affect scoring. Use `must` for full-text relevance scoring.
---
## Implementation Patterns
### Pattern 1: Vector/kNN Search
```python
def vector_search(
es: Elasticsearch,
index_name: str,
query_vector: list[float],
k: int = 10,
num_candidates: int = 100,
) -> list[dict]:
"""Perform kNN vector search for semantic similarity."""
body = {
"knn": {
"field": "embedding",
"query_vector": query_vector,
"k": k,
"num_candidates": num_candidates,
},
"_source": {"excludes": ["embedding"]},
}
response = es.search(index=index_name, body=body)
return [hit["_source"] for hit in response["hits"]["hits"]]
```
### Pattern 2: Point-in-Time Deep Pagination
```python
def scroll_all_results(es: Elasticsearch, index_name: str, query: dict, page_size: int = 1000):
"""Paginate through all matching results using PIT + search_after."""
pit = es.open_point_in_time(index=index_name, keep_alive="5m")
sort = [{"published_at": "desc"}, {"_shard_doc": "desc"}]
last_sort = None
try:
while True:
body = {
"size": page_size,
"query": query,
"sort": sort,
"pit": {"id": pit["id"], "keep_alive": "5m"},
}
if last_sort:
body["search_after"] = last_sort
response = es.search(body=body)
hits = response["hits"]["hits"]
if not hits:
break
for hit in hits:
yield hit["_source"]
last_sort = hits[-1]["sort"]
finally:
es.close_point_in_time(id=pit["id"])
```
### BAD vs GOOD: Indexing Pattern
```python
# ❌ BAD — Indexing one document at a time (slow)
def index_articles_bad(es, index, articles):
for article in articles:
es.index(index=index, document=article)
# ✅ GOOD — Bulk indexing (10-50x faster)
def index_articles_good(es, index, articles):
success, errors = helpers.bulk(es, (
{"_index": index, "_source": a} for a in articles
), raise_on_error=False)
if errors:
log.error(f"Bulk indexing failed for {len(errors)} documents")
```
### BAD vs GOOD: Query Construction
```python
# ❌ BAD — Inefficient wildcard query
def search_bad(es, index, term):
return es.search(index=index, query={"wildcard": {"title": f"*{term}*"}})
# ✅ GOOD — Analyzed match query with proper field
def search_good(es, index, term):
return es.search(index=index, query={"match": {"title": {"query": term, "fuzziness": "AUTO"}}})
```
---
## Constraints
### MUST DO
- Define explicit index mappings before indexing — never rely solely on dynamic mapping
- Use `helpers.bulk()` for all batch indexing operations
- Use bool queries with `filter` for structured data and `must` for full-text relevance
- Set `number_of_shards` based on your data volume and node count (20-40GB per shard)
- Use `ilm` (Index Lifecycle Management) for time-series indices
- Set `refresh_interval` to `-1` during bulk indexing, reset after
### MUST NOT DO
- Never use `scroll` for deep pagination in user-facing queries — use `search_after` or PIT
- Avoid `wildcard` and `regexp` queries on `text` fields — they skip the analysis index
- Do not index documents one-by-one in loops — always use bulk helpers
- Never use `match_all` queries without `size` limits — can crash the cluster
- Avoid large `terms` queries with thousands of values — use `terms_set` or filters instead
---
## Output Template
When writing Elasticsearch integration code, structure your output as:
1. **Client Initialization** — Elasticsearch client with authentication and connection settings
2. **Index Setup** — Create index with explicit mappings (fields, analyzers, vector config)
3. **Bulk Indexing** — helpers.bulk() with chunked document generation
4. **Search Query** — Bool query with must/filter/should clauses, aggregations, sorting
5. **Result Processing** — Extract hits, parse aggregations, handle pagination
---
## Related Skills
| Skill | Purpose |
|---|---|
| `coding-mongodb-driver` | Document database patterns — alternative to Elasticsearch for non-search workloads |
| `coding-rag-architecture` | RAG pipeline patterns using Elasticsearch as vector store |
| `coding-postgresql-sdk` | PostgreSQL full-text search as an alternative |
---
## Live References
- [elasticsearch-py Documentation](https://elasticsearch-py.readthedocs.io/en/v8.x/) — Official Python client 8.x docs
- [Elasticsearch Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html) — Complete query DSL reference
- [Elasticsearch Aggregations Guide](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html) — Bucket and metric aggregations
- [Elasticsearch Vector Search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html) — kNN and vector similarity search
- [Elasticsearch Bulk API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html) — Bulk indexing and update API
- [Elasticsearch ILM](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html) — Index lifecycle management policies
- [OpenSearch Python Client](https://opensearch.org/docs/latest/clients/python/) — OpenSearch Python client (API-compatible)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!