Indexação e Atualização de Documentos com a API REST do Elasticsearch

Domine as operações básicas de Criar, Ler, Atualizar e Excluir (CRUD) no Elasticsearch usando a API REST. Este guia detalha as requisições HTTP precisas, endpoints e payloads JSON necessários para indexar novos documentos (com ou sem IDs especificados) e realizar atualizações parciais granulares em registros existentes. Aprenda exemplos práticos com `curl` para atualizações atômicas, modificações via script e ingestão eficiente de dados em lote.

Indexação e Atualização de Documentos com a API REST do Elasticsearch

Indexar e atualizar documentos com a API REST do Elasticsearch parece fácil à primeira vista: envie JSON para um endpoint e receba JSON de volta. As partes que importam em produção são mais específicas. Você está criando um novo documento ou substituindo um antigo? Você quer que o Elasticsearch gere o ID, ou precisa de um ID do seu sistema de origem? Você está fazendo uma atualização parcial ou está sobrescrevendo acidentalmente campos que pretendia manter?

Os exemplos abaixo usam localhost:9200, um índice chamado products e curl simples. Em um cluster real, você pode precisar também de HTTPS, autenticação e uma opção de certificado.

curl -u elastic:password --cacert http_ca.crt https://es.example.com:9200/

Criar um documento com ID automático

Use POST /{index}/_doc quando o Elasticsearch puder atribuir o ID do documento.

curl -X POST "localhost:9200/products/_doc" \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "mouse-x1",
    "name": "Wireless Mouse X1",
    "price": 25.99,
    "in_stock": true,
    "updated_at": "2026-05-24T09:30:00Z"
  }'

Uma resposta bem-sucedida inclui um _id e um result de created.

{
  "_index": "products",
  "_id": "wI4aQZkB8xExample",
  "_version": 1,
  "result": "created"
}

IDs automáticos são adequados para dados de eventos, logs, registros de cliques e conteúdo somente de acréscimo. Eles não são ideais quando o mesmo item do mundo real pode chegar novamente mais tarde. Se a mesma atualização de produto for indexada duas vezes com IDs automáticos, você obtém dois documentos.

Indexar com seu próprio ID

Use PUT /{index}/_doc/{id} quando seu sistema de origem já possui um identificador estável.

curl -X PUT "localhost:9200/products/_doc/sku-mouse-x1" \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "mouse-x1",
    "name": "Wireless Mouse X1",
    "price": 25.99,
    "in_stock": true,
    "updated_at": "2026-05-24T09:30:00Z"
  }'

Se o ID não existir, o Elasticsearch o cria. Se já existir, o Elasticsearch substitui o documento inteiro. Esse comportamento de substituição é útil para trabalhos de sincronização simples, mas também pode excluir campos que você esqueceu de enviar.

Por exemplo, se o documento existente tem category, brand e description, e seu próximo PUT envia apenas price, o _source armazenado se torna apenas os campos dessa nova requisição. Use PUT quando você estiver enviando o documento completo desejado.

Criar apenas se ausente

Se a criação duplicada seria um bug, use a operação de criação:

curl -X PUT "localhost:9200/products/_create/sku-mouse-x1" \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "mouse-x1",
    "name": "Wireless Mouse X1",
    "price": 25.99
  }'

Se o ID já existir, o Elasticsearch retorna um conflito de versão em vez de sobrescrever o documento. Isso é útil para trabalhos de ingestão onde repetir uma mensagem não deve alterar um registro existente.

Atualizar campos parcialmente

Use POST /{index}/_update/{id} com um objeto doc quando você quiser alterar apenas alguns campos.

curl -X POST "localhost:9200/products/_update/sku-mouse-x1" \
  -H "Content-Type: application/json" \
  -d '{
    "doc": {
      "price": 22.49,
      "in_stock": false,
      "updated_at": "2026-05-24T10:15:00Z"
    }
  }'

O Elasticsearch busca o documento existente internamente, mescla os campos fornecidos e indexa a fonte alterada novamente. É uma atualização parcial do ponto de vista do usuário da API, não uma mutação no local no disco.

Se o documento pode ainda não existir, use doc_as_upsert:

curl -X POST "localhost:9200/products/_update/sku-keyboard-k90" \
  -H "Content-Type: application/json" \
  -d '{
    "doc": {
      "sku": "keyboard-k90",
      "name": "Mechanical Keyboard K90",
      "price": 129.99,
      "in_stock": true
    },
    "doc_as_upsert": true
  }'

Isso cria o documento com o conteúdo de doc se ele estiver ausente. Use isso quando seu feed upstream enviar registros de "estado atual" e você não se importar se esta é a primeira vez que o Elasticsearch vê o ID.

Atualizações com script

Scripts são úteis quando o novo valor depende do valor antigo. Um exemplo comum é incrementar um contador:

curl -X POST "localhost:9200/products/_update/sku-mouse-x1" \
  -H "Content-Type: application/json" \
  -d '{
    "script": {
      "source": "ctx._source.views = (ctx._source.views ?: 0) + params.count",
      "params": {
        "count": 1
      }
    }
  }'

Mantenha os scripts pequenos e previsíveis. Eles são poderosos, mas também são mais fáceis de usar incorretamente do que uma atualização simples com doc. Para contadores de alto volume, pense cuidadosamente sobre a taxa de gravação, necessidades de refresh e se o Elasticsearch deve ser o sistema de registro.

Indexação e atualizações em lote

Para mais do que alguns documentos, use _bulk. O corpo da requisição é JSON delimitado por nova linha, e a nova linha final é importante.

cat bulk-products.ndjson
{"index":{"_index":"products","_id":"sku-mouse-x1"}}
{"sku":"mouse-x1","name":"Wireless Mouse X1","price":25.99,"in_stock":true}
{"update":{"_index":"products","_id":"sku-keyboard-k90"}}
{"doc":{"price":119.99,"in_stock":true},"doc_as_upsert":true}
{"delete":{"_index":"products","_id":"sku-old-cable"}}

Envie assim:

curl -X POST "localhost:9200/_bulk" \
  -H "Content-Type: application/x-ndjson" \
  --data-binary @bulk-products.ndjson

Use --data-binary, não -d simples, para que o curl preserve as novas linhas. Em seguida, inspecione a resposta. Uma requisição em lote pode retornar HTTP 200 enquanto itens individuais falharam.

curl -s -X POST "localhost:9200/_bulk" \
  -H "Content-Type: application/x-ndjson" \
  --data-binary @bulk-products.ndjson | jq '.errors, .items[] | select(.index.error or .update.error or .delete.error)'

Refresh e visibilidade

Um documento indexado nem sempre é pesquisável imediatamente. O Elasticsearch atualiza os índices periodicamente por padrão. Se você precisar ler o documento pelo ID, GET /products/_doc/sku-mouse-x1 pode encontrá-lo imediatamente. Se você precisar que ele apareça na pesquisa antes de continuar um teste, use refresh=wait_for:

curl -X PUT "localhost:9200/products/_doc/sku-mouse-x1?refresh=wait_for" \
  -H "Content-Type: application/json" \
  -d '{"sku":"mouse-x1","name":"Wireless Mouse X1","price":25.99}'

Não adicione refreshes forçados a cada gravação de produção sem medir o custo. Eles podem reduzir a taxa de transferência de indexação.

Uma regra prática

Use POST /_doc para dados somente de acréscimo, onde eventos duplicados são aceitáveis ou tratados em outro lugar. Use PUT /_doc/{id} quando a requisição contém o documento completo atual. Use _update quando você quiser alterar apenas certos campos. Use _create quando sobrescrever esconderia um problema de dados. Use _bulk quando o trabalho for mais do que alguns documentos.

A maioria dos bugs de indexação do Elasticsearch vem da escolha da forma de gravação errada. O endpoint deve corresponder aos seus dados de origem, ao seu comportamento de repetição e se o Elasticsearch está armazenando um registro completo ou uma cópia pesquisável do registro de outro sistema.

Verifique os mapeamentos antes de culpar a requisição de gravação

Às vezes, a gravação é bem-sucedida e o resultado da pesquisa ainda parece errado. Isso geralmente é um problema de mapeamento, não um problema de endpoint de indexação.

Verifique o mapeamento:

curl -s "localhost:9200/products/_mapping?pretty"

Se price foi indexado primeiro como texto porque um documento de teste inicial enviou "25.99" como string, as consultas de intervalo podem se comportar mal. Se sku precisa de correspondência exata e agregações, geralmente deve ser um campo keyword ou ter um subcampo keyword. Se os timestamps chegarem em formatos diferentes, alguns documentos podem ser rejeitados durante a indexação enquanto outros são bem-sucedidos.

Para sistemas previsíveis, crie o mapeamento do índice antes da primeira gravação:

curl -X PUT "localhost:9200/products" \
  -H "Content-Type: application/json" \
  -d '{
    "mappings": {
      "properties": {
        "sku": { "type": "keyword" },
        "name": { "type": "text" },
        "price": { "type": "double" },
        "in_stock": { "type": "boolean" },
        "updated_at": { "type": "date" }
      }
    }
  }'

O mapeamento dinâmico é conveniente para exploração. Para ingestão em produção, mapeamentos explícitos impedem que um primeiro documento ruim molde o índice.

Lide com conflitos e repetições deliberadamente

Atualizações concorrentes podem entrar em conflito. Se dois workers atualizarem o mesmo documento quase ao mesmo tempo, um pode ser baseado em uma versão mais antiga. Para repetições simples de atualização, o Elasticsearch suporta retry_on_conflict:

curl -X POST "localhost:9200/products/_update/sku-mouse-x1?retry_on_conflict=3" \
  -H "Content-Type: application/json" \
  -d '{
    "doc": {
      "price": 21.99
    }
  }'

Isso é útil para atualizações de baixo risco, mas não substitui uma propriedade clara. Se um sistema possui nomes de produtos e outro possui inventário, considere atualizar diferentes campos por meio de pipelines bem definidos. Se a ordem dos eventos for importante, inclua timestamps de origem e rejeite atualizações mais antigas em sua camada de ingestão ou com um script cuidadoso.

Leia após gravar durante a depuração

Ao testar um fluxo de indexação, busque o documento pelo ID imediatamente:

curl -s "localhost:9200/products/_doc/sku-mouse-x1?pretty"

Em seguida, pesquise por ele:

curl -s "localhost:9200/products/_search?pretty" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "term": {
        "sku": "mouse-x1"
      }
    }
  }'

Se GET por ID funciona, mas a pesquisa não, pense em refresh, mapeamento, analisador ou tipo de consulta. Se ambos falharem, pense em erro de indexação, índice errado, ID errado, autenticação ou roteamento.

Para aplicações, registre o índice alvo, o ID do documento, o status da resposta e os erros em nível de item do bulk. Você não precisa registrar todos os documentos completos para sempre, mas durante a implantação esses campos economizam horas.