ELASTICSEARCH - Search and Analytics Engine
O que é este Node?
O ELASTICSEARCH é o node responsável por integrar com Elasticsearch para indexar documentos, buscar dados, atualizar/deletar documentos e gerenciar índices.
Por que este Node existe?
Busca e análise de grandes volumes de dados são desafios comuns. O ELASTICSEARCH existe para:
- Busca full-text: Pesquisar texto em milhões de documentos em milissegundos
- Analytics em tempo real: Analisar logs e métricas instantaneamente
- Busca fuzzy: Encontrar resultados mesmo com typos e variações
- Agregações: Calcular estatísticas e métricas sobre dados
- Escalabilidade: Distribuir dados em múltiplos nodes automaticamente
Como funciona internamente?
Quando o ELASTICSEARCH é executado, o sistema:
- Identifica operação: index_document, search, get_document, update_document, delete_document, create_index, delete_index
- Valida parâmetros: URL do ES, índice, documento conforme operação
- Configura autenticação: API Key se fornecido
- Executa operação HTTP: POST/GET/DELETE no Elasticsearch REST API
- Retorna resultado: Dados encontrados ou confirmação da operação
- Se erro: Lança exceção com detalhes
Código interno (infrastructure-executor.service.ts:139-169):
private async executeElasticsearch(parameters: any, context: any): Promise<any> {
const { operation, index, document, query, url, apiKey } = parameters;
this.logger.log(`🔍 ELASTICSEARCH - Operation: ${operation}, Index: ${index}`);
switch (operation) {
case 'index_document':
return this.indexDocument(index, document, url, apiKey, context);
case 'search':
return this.searchDocuments(index, query, url, apiKey, context);
case 'get_document':
return this.getDocument(index, parameters.id, url, apiKey, context);
case 'update_document':
return this.updateDocument(index, parameters.id, document, url, apiKey, context);
case 'delete_document':
return this.deleteDocument(index, parameters.id, url, apiKey, context);
case 'create_index':
return this.createIndex(index, parameters.mapping, url, apiKey, context);
case 'delete_index':
return this.deleteIndex(index, url, apiKey, context);
default:
throw new Error(`Unsupported Elasticsearch operation: ${operation}`);
}
}
Quando você DEVE usar este Node?
Use ELASTICSEARCH quando precisar busca e analytics avançados:
Casos de uso
- Busca em produtos: E-commerce com filtros, categorias e busca fuzzy
- Log aggregation: Centralizar e buscar logs (stack ELK)
- Monitoramento: Buscar e analisar métricas de aplicação
- Busca em documentos: Pesquisar em PDFs, artigos, documentação
- Autocomplete: Sugestões de busca em tempo real
Quando NÃO usar ELASTICSEARCH
- Database primário: Use PostgreSQL/MySQL para dados transacionais
- Cache: Use Redis para cache simples
- Filas: Use RabbitMQ/Kafka para message queues
- Dados pequenos: Overhead desnecessário para <10k documentos
Parâmetros Detalhados
operation (string, obrigatório)
O que é: Tipo de operação a ser executada no Elasticsearch.
Valores válidos:
- index_document: Indexar documento
- search: Buscar documentos
- get_document: Obter documento por ID
- update_document: Atualizar documento
- delete_document: Deletar documento
- create_index: Criar índice
- delete_index: Deletar índice
Flow completo para testar:
{
"name": "Teste ES - Index Document",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Indexar Produto",
"parameters": {
"operation": "index_document",
"index": "products",
"document": {
"name": "Notebook Dell",
"price": 3500,
"category": "electronics"
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "Produto indexado! ID: {{es_1.documentId}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 700, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "es_1" },
{ "source": "es_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Documento é indexado no Elasticsearch.
index (string, obrigatório)
O que é: Nome do índice Elasticsearch (equivalente a tabela em SQL).
Flow completo para testar:
{
"name": "Teste ES - Index Name",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Criar Índice",
"parameters": {
"operation": "create_index",
"index": "users",
"mapping": {
"properties": {
"name": { "type": "text" },
"email": { "type": "keyword" },
"age": { "type": "integer" }
}
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "Índice 'users' criado!"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 700, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "es_1" },
{ "source": "es_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Índice é criado com mapping especificado.
document (object, obrigatório para index/update)
O que é: Objeto com dados do documento a ser indexado/atualizado.
Flow completo para testar:
{
"name": "Teste ES - Document",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Pedir Nome",
"parameters": {
"message": "Nome do produto:",
"variable": "produto"
}
}
},
{
"id": "number_1",
"type": "number",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Pedir Preço",
"parameters": {
"message": "Preço:",
"variable": "preco",
"decimals": 2
}
}
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Indexar",
"parameters": {
"operation": "index_document",
"index": "catalog",
"document": {
"name": "{{produto}}",
"price": "{{preco}}",
"timestamp": "{{now}}",
"status": "active"
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "{{produto}} indexado! Preço: R$ {{preco}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "number_1" },
{ "source": "number_1", "target": "es_1" },
{ "source": "es_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Documento com variáveis é indexado.
query (object, obrigatório para search)
O que é: Query DSL do Elasticsearch para buscar documentos.
Flow completo para testar:
{
"name": "Teste ES - Search Query",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Termo de Busca",
"parameters": {
"message": "O que você procura?",
"variable": "termo"
}
}
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Buscar",
"parameters": {
"operation": "search",
"index": "products",
"query": {
"query": {
"match": {
"name": "{{termo}}"
}
}
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Resultado",
"parameters": {
"message": "Encontrados: {{es_1.totalHits}} resultados"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "es_1" },
{ "source": "es_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Busca por termo retorna resultados.
url (string, opcional)
O que é: URL do cluster Elasticsearch (padrão: http://localhost:9200).
apiKey (string, opcional)
O que é: API Key para autenticação no Elasticsearch.
id (string, obrigatório para get/update/delete)
O que é: ID do documento para operações específicas.
mapping (object, opcional)
O que é: Mapping do índice ao criar (define tipos de campos).
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| operation | string | Sim | index_document, search, get_document, update_document, delete_document, create_index, delete_index |
| index | string | Sim | Nome do índice |
| document | object | Sim* | Documento a indexar/atualizar (*para index/update) |
| query | object | Sim* | Query DSL (*para search) |
| id | string | Sim* | ID do documento (*para get/update/delete) |
| url | string | Não | URL do ES (padrão: http://localhost:9200) |
| apiKey | string | Não | API Key para autenticação |
| mapping | object | Não | Mapping ao criar índice |
Exemplo 1: Busca de Produtos
Objetivo: Indexar e buscar produtos em catálogo
JSON para Importar
{
"name": "ES - Busca de Produtos",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Indexar Notebooks",
"parameters": {
"operation": "index_document",
"index": "products",
"document": {
"name": "Notebook Dell Inspiron",
"price": 3200,
"category": "electronics",
"brand": "Dell",
"inStock": true
},
"url": "http://localhost:9200"
}
}
},
{
"id": "delay_1",
"type": "delay",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Aguardar Indexação",
"parameters": {
"duration": 1,
"unit": "seconds"
}
}
},
{
"id": "input_1",
"type": "input",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Buscar",
"parameters": {
"message": "O que você procura?",
"variable": "busca"
}
}
},
{
"id": "es_2",
"type": "elasticsearch",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Buscar Produtos",
"parameters": {
"operation": "search",
"index": "products",
"query": {
"query": {
"multi_match": {
"query": "{{busca}}",
"fields": ["name", "category", "brand"]
}
}
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Resultados",
"parameters": {
"message": "Encontrados {{es_2.totalHits}} produtos para '{{busca}}'"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1300, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "es_1" },
{ "source": "es_1", "target": "delay_1" },
{ "source": "delay_1", "target": "input_1" },
{ "source": "input_1", "target": "es_2" },
{ "source": "es_2", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Saída esperada:
Sistema: O que você procura?
Usuário: notebook
Sistema: Encontrados 1 produtos para 'notebook'
Exemplo 2: Log Aggregation
Objetivo: Indexar logs e buscar erros
JSON para Importar
{
"name": "ES - Log Aggregation",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Indexar Log",
"parameters": {
"operation": "index_document",
"index": "application-logs",
"document": {
"level": "error",
"service": "api-gateway",
"message": "Connection timeout to database",
"timestamp": "{{now}}",
"userId": 456
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Log Indexado",
"parameters": {
"message": "Log de erro indexado!"
}
}
},
{
"id": "es_2",
"type": "elasticsearch",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Buscar Erros",
"parameters": {
"operation": "search",
"index": "application-logs",
"query": {
"query": {
"bool": {
"must": [
{ "match": { "level": "error" } },
{ "match": { "service": "api-gateway" } }
]
}
},
"size": 10
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Total Erros",
"parameters": {
"message": "Total de erros no API Gateway: {{es_2.totalHits}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "es_1" },
{ "source": "es_1", "target": "message_1" },
{ "source": "message_1", "target": "es_2" },
{ "source": "es_2", "target": "message_2" },
{ "source": "message_2", "target": "end_1" }
]
}
Saída esperada:
Sistema: Log de erro indexado!
Sistema: Total de erros no API Gateway: 1
Exemplo 3: Atualizar Documento
Objetivo: Atualizar preço de produto
JSON para Importar
{
"name": "ES - Atualizar Produto",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "ID do Produto",
"parameters": {
"message": "ID do produto:",
"variable": "productId"
}
}
},
{
"id": "number_1",
"type": "number",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Novo Preço",
"parameters": {
"message": "Novo preço:",
"variable": "novoPreco",
"decimals": 2
}
}
},
{
"id": "es_1",
"type": "elasticsearch",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Atualizar",
"parameters": {
"operation": "update_document",
"index": "products",
"id": "{{productId}}",
"document": {
"price": "{{novoPreco}}",
"updatedAt": "{{now}}"
},
"url": "http://localhost:9200"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "Preço atualizado para R$ {{novoPreco}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "number_1" },
{ "source": "number_1", "target": "es_1" },
{ "source": "es_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Saída esperada:
Sistema: ID do produto:
Usuário: abc123
Sistema: Novo preço:
Usuário: 2999.90
Sistema: Preço atualizado para R$ 2999.90
Resposta do Node
Index Document
{
"success": true,
"action": "elasticsearch_document_indexed",
"index": "products",
"documentId": "abc123xyz",
"result": "created",
"timestamp": "2025-01-15T10:30:00.000Z"
}
Search
{
"success": true,
"action": "elasticsearch_search_completed",
"index": "products",
"totalHits": 42,
"documents": [ /* array de documentos */ ],
"took": 5,
"timestamp": "2025-01-15T10:30:00.000Z"
}
Query DSL Exemplos
Match simples
{
"query": {
"match": {
"name": "notebook"
}
}
}
Bool com filtros
{
"query": {
"bool": {
"must": [
{ "match": { "category": "electronics" } }
],
"filter": [
{ "range": { "price": { "gte": 1000, "lte": 5000 } } }
]
}
}
}
Multi-match
{
"query": {
"multi_match": {
"query": "dell laptop",
"fields": ["name^2", "description", "brand"]
}
}
}
Boas Práticas
✅ SIM:
- Defina mappings antes de indexar para controle de tipos
- Use query bool para combinar múltiplos critérios
- Implemente paginação (from/size ou search_after)
- Use analyzers apropriados para texto em português
- Monitore uso de memória e disk space
❌ NÃO:
- Não indexe dados sensíveis sem encryption
- Não faça queries sem limites (sempre defina size)
- Não use wildcard queries no início (ex: *termo)
- Não ignore índice patterns e lifecycle policies
- Não mantenha índices antigos desnecessariamente
Dicas
💡 Dev Tools: Use Kibana Dev Tools para testar queries
💡 Análise: Configure analyzers para melhor busca (stemming, stopwords)
💡 Performance: Use filtros (filter) ao invés de queries quando possível
💡 Relevância: Ajuste scoring com boost e function_score
Próximo Node
→ MONGODB - NoSQL database → POSTGRES - SQL database → REDIS - Cache e busca rápida