n8n Elasticsearch: Full Text Search Index Automation

I use Elasticsearch with n8n because building search functionality from scratch is expensive and error-prone. Elasticsearch handles full-text search, relevance ranking, faceted navigation, and autocomplete out of the box. n8n connects to Elasticsearch through its HTTP Request node and the dedicated Elasticsearch node, letting me automate index management, data ingestion, and search queries.

Elasticsearch stores documents in indexed collections called indices. Each document contains fields with analyzers that tokenize text for fast searching. I’ve built search pipelines that ingest product catalogs, index blog posts, and power autocomplete suggestions — all orchestrated through n8n workflows.

Index Management and Data Ingestion

Creating and managing indices is the first step. I use the Elasticsearch node’s index operations to define mappings that control how fields get indexed:

`javascript
// Elasticsearch node: create index with custom mapping
Operation: Index Manage
Action: Create Index
Index Name: products
Body: {
“mappings”: {
“properties”: {
“name”: { “type”: “text”, “analyzer”: “english” },
“description”: { “type”: “text”, “analyzer”: “english” },
“price”: { “type”: “float” },
“category”: { “type”: “keyword” },
“sku”: { “type”: “keyword” },
“created_at”: { “type”: “date” },
“in_stock”: { “type”: “boolean” }
}
},
“settings”: {
“number_of_shards”: 1,
“number_of_replicas”: 0
}
}
`

The mapping defines field types. text fields get analyzed — split into tokens for full-text search. keyword fields are exact-match, ideal for categories and SKUs. float, date, and boolean types enable range queries and sorting.

For bulk data ingestion, I use the Bulk API through the HTTP Request node. This is far faster than inserting documents one at a time:

`javascript
// HTTP Request node: bulk index documents
Method: POST
URL: http://localhost:9200/products/_bulk
Headers: { Content-Type: application/x-ndjson }
Body: {{ndjson_bulk_payload}}
`

The bulk payload uses newline-delimited JSON format:

`javascript
// Code node: prepare bulk payload
const docs = items.map(item => ({
index: { _index: ‘products’, _id: item.json.sku }
}));

const inserts = items.map(item => JSON.stringify({
name: item.json.name,
description: item.json.description,
price: item.json.price,
category: item.json.category,
sku: item.json.sku,
created_at: new Date().toISOString(),
in_stock: item.json.in_stock
}));

const bulkData = […docs, …inserts].join(‘\n’) + ‘\n’;
return [{ json: { bulk_payload: bulkData } }];
`

Each line is either an action document (index/create/update/delete) or the data document. Elasticsearch processes these in batches, making bulk operations 10-100x faster than individual requests.

Search Queries and Relevance Tuning

Elasticsearch’s query DSL supports various search types. The Match query handles full-text search, Term queries do exact matching, and Range queries filter by numeric or date bounds.

`javascript
// Elasticsearch node: search with combined queries
Operation: Search
Index: products
Body: {
“query”: {
“bool”: {
“must”: [
{
“multi_match”: {
“query”: “{{search_term}}”,
“fields”: [“name^3”, “description”, “category”]
}
}
],
“filter”: [
{ “term”: { “in_stock”: true } },
{ “range”: { “price”: { “lte”: 100 } } }
]
}
},
“highlight”: {
“fields”: {
“name”: {},
“description”: {}
}
},
“size”: 20
}
`

The multi_match query searches across multiple fields. The ^3 boost factor gives the name field three times the weight of description. Results rank higher when the search term appears in the product name versus the description.

The bool query combines must clauses (required matches) with filter clauses (performance-optimized conditions). Filters don’t affect scoring — they just include or exclude documents. This distinction matters for caching and performance.

Highlighting wraps matching terms in tags in the response, so I can display search results with the matched terms highlighted:

`javascript
// Code node: extract highlighted snippets
const hits = item.json.hits.hits;
const results = hits.map(hit => ({
id: hit._id,
name: hit._source.name,
snippet: hit.highlight?.description?.[0] || hit._source.description,
price: hit._source.price,
score: hit._score
}));
return results.map(r => ({ json: r }));
`

Autocomplete and Suggestion Features

Autocomplete suggestions come from the Completion Suggester. I index a special field with a completion suggester type, then query it for real-time suggestions as users type:

`javascript
// Elasticsearch node: create index with completion suggester
Operation: Index Manage
Action: Create Index
Index Name: search_suggestions
Body: {
“mappings”: {
“properties”: {
“name”: { “type”: “text” },
“suggest_field”: {
“type”: “completion”,
“analyzer”: “simple”,
“search_analyzer”: “simple”
}
}
}
}
`

Index suggestion documents with weighted prefixes:

`javascript
// Bulk index suggestions
[
{ “index”: { “_index”: “search_suggestions”, “_id”: “1” } },
{ “name”: “n8n automation”, “suggest_field”: { “input”: [“n8n”, “n8n automation”, “n8n workflow”], “weight”: 10 } }
]
`

Query for suggestions:

`javascript
// Elasticsearch node: get suggestions
Operation: Search
Index: search_suggestions
Body: {
“suggest”: {
“autocomplete”: {
“prefix”: “{{user_input}}”,
“completion”: {
“field”: “suggest_field”,
“size”: 10
}
}
}
}
`

The weighted input array ensures “n8n automation” ranks higher than just “n8n” when both match. This gives better suggestion quality for branded or specific terms.

Connecting to Your Existing Setup

The n8n Docker Deployment guide covers running Elasticsearch alongside n8n in Docker Compose. A shared network lets the n8n container reach Elasticsearch on port 9200.

For connection configuration, the n8n Docker Environment Variables reference lists the variables for proxy settings and timeout configuration when Elasticsearch sits behind a load balancer.

The n8n Nodes Techniques guide covers the expressions and data mapping techniques useful for constructing Elasticsearch query bodies dynamically from workflow data.

Action Card: Quick Search Index

Test your Elasticsearch connection with this minimal workflow:

`javascript
// 1. Elasticsearch node: Create Index
// Index Name: test_search
// Mapping: {“properties”:{“title”:{“type”:”text”},”body”:{“type”:”text”}}}

// 2. Elasticsearch node: Index Document
// Index: test_search
// Document: {“title”:”n8n Elasticsearch Guide”,”body”:”Learn to automate search with n8n and Elasticsearch”}

// 3. Elasticsearch node: Search
// Index: test_search
// Query: {“query”:{“match”:{“body”:”Elasticsearch”}}}

// 4. Response node: verify search returned your document
`

This confirms index creation, document indexing, and search retrieval all work end-to-end.

References

  • Elasticsearch Documentation
  • Elasticsearch Query DSL
  • n8n Elasticsearch Node
  • Elasticsearch Bulk API
  • Elastic Cloud (Managed)
  • Leave a Reply

    Your email address will not be published. Required fields are marked *