Full-text search and relevance optimization with Elasticsearch
Scope: Full-text search, Query DSL, aggregations, relevance tuning, performance optimization Lines: ~450 Last Updated: 2025-10-27 Format Version: 1.0 (Atomic)
Activate this skill when:
Elasticsearch uses inverted indexes for fast full-text search.
How it works:
Documents:
Doc 1: "Elasticsearch is fast"
Doc 2: "Elasticsearch is scalable"
Inverted Index:
Term → Document IDs
elasticsearch → [1, 2]
fast → [1]
scalable → [2]
Search Process:
Mapping defines field types and how they're indexed.
Text vs Keyword:
{
"mappings": {
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": {"type": "keyword"}
}
}
}
}
}
text: Full-text search, analyzed, tokenizedkeyword: Exact matching, aggregations, sortingAnalyzer processes text during indexing and searching:
Input: "The QUICK Brown Fox"
↓ Standard Tokenizer
["The", "QUICK", "Brown", "Fox"]
↓ Lowercase Filter
["the", "quick", "brown", "fox"]
↓ Stop Words Filter
["quick", "brown", "fox"]
Match Query (full-text):
{
"query": {
"match": {
"description": {
"query": "gaming laptop",
"operator": "and"
}
}
}
}
Term Query (exact match):
{
"query": {
"term": {
"status.keyword": "published"
}
}
}
Bool Query (combine queries):
{
"query": {
"bool": {
"must": [{"match": {"title": "laptop"}}],
"filter": [
{"term": {"category": "electronics"}},
{"range": {"price": {"gte": 500}}}
],
"should": [{"term": {"brand": "Apple"}}],
"must_not": [{"term": {"status": "discontinued"}}]
}
}
}
must: Must match, affects scorefilter: Must match, no scoring (cached, faster)should: Optional, boosts scoremust_not: Must not match (filter)When to use:
{
"query": {
"multi_match": {
"query": "gaming laptop",
"fields": ["title^3", "description^1.5", "tags"],
"type": "best_fields",
"fuzziness": "AUTO"
}
}
}
Benefits:
^3) increases relevance for title matchesfuzziness handles typosbest_fields uses highest-scoring fieldUse case: Exact filters + full-text search
// ❌ Bad: Everything in must (slower, scoring overhead)
{
"query": {
"bool": {
"must": [
{"match": {"description": "laptop"}},
{"term": {"status": "published"}},
{"range": {"price": {"gte": 500}}}
]
}
}
}
// ✅ Good: Use filter for exact matches (faster, cached)
{
"query": {
"bool": {
"must": [{"match": {"description": "laptop"}}],
"filter": [
{"term": {"status": "published"}},
{"range": {"price": {"gte": 500}}}
]
}
}
}
Benefits:
Use case: Fast typeahead suggestions
Mapping:
{
"mappings": {
"properties": {
"suggest": {
"type": "completion",
"contexts": [
{"name": "category", "type": "category"}
]
}
}
}
}
Index:
{
"name": "Gaming Laptop",
"suggest": {
"input": ["gaming laptop", "laptop gaming", "gaming"],
"weight": 10,
"contexts": {"category": "electronics"}
}
}
Query:
{
"suggest": {
"product_suggest": {
"prefix": "gam",
"completion": {
"field": "suggest",
"size": 10,
"contexts": {"category": "electronics"},
"fuzzy": {"fuzziness": "AUTO"}
}
}
}
}
Benefits:
Use case: E-commerce filters (category, price, brand)
{
"query": {
"match": {"title": "laptop"}
},
"aggs": {
"categories": {
"terms": {"field": "category.keyword", "size": 10}
},
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{"to": 500},
{"from": 500, "to": 1500},
{"from": 1500}
]
}
},
"brands": {
"terms": {"field": "brand.keyword", "size": 20}
}
}
}
Benefits:
Use case: Deep pagination (page 100+)
// ❌ Bad: from/size for deep pagination (expensive)
GET /products/_search?from=10000&size=10
// ✅ Good: search_after (efficient, stateless)
{
"query": {"match_all": {}},
"size": 10,
"sort": [
{"created_at": "desc"},
{"_id": "desc"}
],
"search_after": ["2025-10-27T00:00:00Z", "prod_123"]
}
Why search_after?
Trade-off: Can't jump to arbitrary pages (sequential only)
Use case: Boost results by multiple factors
{
"query": {
"function_score": {
"query": {"match": {"title": "laptop"}},
"functions": [
{
"filter": {"term": {"is_premium": true}},
"weight": 2
},
{
"field_value_factor": {
"field": "rating",
"factor": 1.5,
"modifier": "sqrt"
}
},
{
"gauss": {
"created_at": {
"origin": "now",
"scale": "30d",
"decay": 0.5
}
}
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
Factors:
Use case: Multi-level analytics
{
"size": 0,
"aggs": {
"categories": {
"terms": {"field": "category.keyword"},
"aggs": {
"brands": {
"terms": {"field": "brand.keyword"},
"aggs": {
"avg_price": {"avg": {"field": "price"}},
"avg_rating": {"avg": {"field": "rating"}}
}
}
}
}
}
}
Result Structure:
Electronics
├── Apple: avg_price: $1200, avg_rating: 4.5
├── Dell: avg_price: $800, avg_rating: 4.2
Books
├── Penguin: avg_price: $15, avg_rating: 4.7
Use case: Show matching text snippets
{
"query": {"match": {"description": "gaming"}},
"highlight": {
"fields": {
"description": {
"fragment_size": 150,
"number_of_fragments": 3
}
},
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"]
}
}
Result:
{
"highlight": {
"description": [
"High performance <mark>gaming</mark> laptop...",
"Ideal for <mark>gaming</mark> and content creation..."
]
}
}
Type | Use Case | Analyzed
------------------|-----------------------------|---------
match | Full-text search | Yes
term | Exact match | No
range | Numeric/date ranges | No
bool | Combine queries | N/A
multi_match | Multi-field search | Yes
prefix | Prefix matching | No
wildcard | Pattern matching | No
fuzzy | Typo tolerance | Yes
match_phrase | Exact phrase | Yes
Type | Purpose
------------------|----------------------------------
terms | Group by field value
range | Group by ranges
date_histogram | Group by time intervals
avg/min/max/sum | Calculate metrics
cardinality | Unique count (approximate)
percentiles | Distribution percentiles
top_hits | Top documents per bucket
Type | Use Case
------------------|----------------------------------
text | Full-text search
keyword | Exact match, aggregations
integer/long | Whole numbers
float/double | Decimals
date | Dates/timestamps
boolean | true/false
nested | Array of objects
geo_point | Lat/lon coordinates
completion | Autocomplete
❌ Using term query on text fields
{"term": {"title": "Quick"}} // Won't match (analyzed as "quick")
✅ Use match or query title.keyword
❌ Deep pagination with from/size
GET /products/_search?from=10000&size=10 // Expensive
✅ Use search_after for deep pagination
❌ Large terms aggregations
{"aggs": {"all_users": {"terms": {"size": 100000}}}}
✅ Use composite aggregation or limit size
❌ Not using filters for exact matches
{"bool": {"must": [{"term": {"status": "active"}}]}}
✅ Move to filter for caching and performance
❌ Leading wildcards
{"wildcard": {"name": "*smith"}} // Extremely slow
✅ Use reverse field + prefix or full-text search
❌ Over-sharding
{"settings": {"number_of_shards": 100}} // For 10GB index
✅ Target 10-50 GB per shard (2-3 shards for 10GB)
This skill includes Level 3 Resources (executable tools, reference materials, examples):
resources/REFERENCE.md (3,500+ lines)
Deep dive covering:
resources/scripts/analyze_queries.py
Analyzes Elasticsearch Query DSL queries for performance issues:
# Analyze query file
./analyze_queries.py --query-file queries.json
# Analyze single query
./analyze_queries.py --query '{"query": {"match": {"field": "value"}}}'
# JSON output
./analyze_queries.py --query-file queries.json --json
# With Elasticsearch endpoint
./analyze_queries.py --query-file queries.json --endpoint http://localhost:9200
Features:
resources/scripts/optimize_indexes.py
Analyzes indices and provides optimization recommendations:
# Analyze single index
./optimize_indexes.py --index products --endpoint http://localhost:9200
# Analyze all indices
./optimize_indexes.py --all-indices
# JSON output
./optimize_indexes.py --index products --json
Features:
resources/scripts/benchmark_search.py
Benchmarks query performance with statistical analysis:
# Benchmark query file
./benchmark_search.py --query-file queries.json --index products
# Custom iterations and concurrency
./benchmark_search.py --query-file queries.json --iterations 500 --concurrent-requests 10
# JSON output
./benchmark_search.py --query-file queries.json --json
Features:
Mappings:
examples/mappings/product-index.json - Complete product index with custom analyzers, multi-fields, nested objects, completion suggesterQueries:
examples/queries/full-text-search.json - 10+ full-text search patterns (match, multi_match, bool, fuzzy, boosting, nested, highlighting, pagination)examples/queries/aggregations.json - 12+ aggregation examples (terms, range, date_histogram, nested, pipeline, bucket_script)examples/queries/autocomplete.json - 10+ autocomplete patterns (completion suggester, match_bool_prefix, edge n-grams, phrase suggester)Python:
examples/python/elasticsearch-client.py - Comprehensive client examples (CRUD, search, aggregations, updates, highlighting)examples/python/bulk-indexer.py - Production-ready bulk indexer (streaming, parallel, error handling, progress tracking)Node.js:
examples/nodejs/search-service.js - Search service class (full-text search, autocomplete, faceted search, custom scoring, pagination)Docker:
examples/docker/docker-compose.yml - Single-node and multi-node Elasticsearch + Kibana setup# Start Elasticsearch + Kibana
cd resources/examples/docker && docker-compose up -d
# Analyze query performance
./resources/scripts/analyze_queries.py --query-file my-queries.json
# Get index optimization recommendations
./resources/scripts/optimize_indexes.py --index products
# Benchmark queries
./resources/scripts/benchmark_search.py --query-file queries.json --index products
# Run Python examples
cd resources/examples/python
pip install elasticsearch
python elasticsearch-client.py
# Bulk index test data
python bulk-indexer.py --generate-test-data 10000 --index products
postgres-query-optimization.md - Query optimization concepts applicable to Elasticsearchredis-data-structures.md - Caching Elasticsearch resultsapi-rate-limiting.md - Rate limiting search APIsdatabase-selection.md - When to use Elasticsearch vs other databasesLast Updated: 2025-10-27 Format Version: 1.0 (Atomic)