Indicizzazione e Aggiornamento dei Documenti con l'API REST di Elasticsearch

Padroneggia le operazioni CRUD (Create, Read, Update, Delete) di base in Elasticsearch utilizzando l'API REST. Questa guida descrive in dettaglio le richieste HTTP, gli endpoint e i payload JSON necessari per indicizzare nuovi documenti (con o senza ID specificati) e per eseguire aggiornamenti parziali granulari su record esistenti. Impara esempi pratici con `curl` per aggiornamenti atomici, modifiche tramite script e inserimento efficiente di dati in blocco.

Indicizzazione e Aggiornamento dei Documenti con l'API REST di Elasticsearch

Indicizzare e aggiornare documenti con l'API REST di Elasticsearch sembra facile all'inizio: invia JSON a un endpoint e ricevi JSON indietro. Le parti che contano in produzione sono più specifiche. Stai creando un nuovo documento o sostituendone uno vecchio? Vuoi che Elasticsearch generi l'ID, o hai bisogno di un ID dal tuo sistema sorgente? Stai eseguendo un aggiornamento parziale, o stai accidentalmente sovrascrivendo campi che intendevi mantenere?

Gli esempi seguenti usano localhost:9200, un indice chiamato products e curl semplice. In un cluster reale potresti aver bisogno anche di HTTPS, autenticazione e un'opzione per il certificato.

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

Creare un documento con un ID automatico

Usa POST /{index}/_doc quando Elasticsearch può assegnare l'ID del 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"
  }'

Una risposta di successo include un _id e un result di created.

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

Gli ID automatici vanno bene per dati di eventi, log, record di click e contenuti append-only. Non sono ideali quando lo stesso elemento del mondo reale potrebbe arrivare di nuovo in seguito. Se lo stesso aggiornamento di prodotto viene indicizzato due volte con ID automatici, ottieni due documenti.

Indicizzare con il proprio ID

Usa PUT /{index}/_doc/{id} quando il tuo sistema sorgente ha già un identificatore stabile.

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 l'ID non esiste, Elasticsearch lo crea. Se esiste già, Elasticsearch sostituisce l'intero documento. Questo comportamento di sostituzione è utile per semplici lavori di sincronizzazione, ma può anche eliminare campi che hai dimenticato di inviare.

Ad esempio, se il documento esistente ha category, brand e description, e la tua prossima PUT invia solo price, il _source memorizzato diventa solo i campi in quella nuova richiesta. Usa PUT quando stai inviando il documento completo desiderato.

Creare solo se mancante

Se la creazione duplicata sarebbe un bug, usa l'operazione di creazione:

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 l'ID esiste già, Elasticsearch restituisce un conflitto di versione invece di sovrascrivere il documento. Questo è utile per lavori di inserimento dove riprovare un messaggio non dovrebbe modificare un record esistente.

Aggiornare parzialmente i campi

Usa POST /{index}/_update/{id} con un oggetto doc quando vuoi cambiare solo alcuni campi.

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"
    }
  }'

Elasticsearch recupera internamente il documento esistente, unisce i campi forniti e indicizza nuovamente la sorgente modificata. È un aggiornamento parziale dal punto di vista dell'utente dell'API, non una mutazione in-place su disco.

Se il documento potrebbe non esistere ancora, usa 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
  }'

Questo crea il documento con il contenuto di doc se manca. Usalo quando il tuo feed upstream invia record di "stato corrente" e non ti interessa se questa è la prima volta che Elasticsearch vede l'ID.

Aggiornamenti tramite script

Gli script sono utili quando il nuovo valore dipende dal vecchio valore. Un esempio comune è incrementare un contatore:

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
      }
    }
  }'

Mantieni gli script piccoli e prevedibili. Sono potenti, ma sono anche più facili da usare male rispetto a un semplice aggiornamento doc. Per contatori ad alto volume, pensa attentamente al tasso di scrittura, alle esigenze di refresh e se Elasticsearch dovrebbe essere il sistema di registrazione.

Indicizzazione e aggiornamenti in blocco

Per più di una manciata di documenti, usa _bulk. Il corpo della richiesta è JSON delimitato da nuove righe, e la nuova riga finale è 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"}}

Invia in questo modo:

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

Usa --data-binary, non il semplice -d, in modo che curl preservi le nuove righe. Poi ispeziona la risposta. Una richiesta bulk può restituire HTTP 200 mentre singoli elementi sono falliti.

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 visibilità

Un documento indicizzato non è sempre immediatamente ricercabile. Elasticsearch aggiorna gli indici periodicamente per impostazione predefinita. Se hai bisogno di leggere il documento per ID, GET /products/_doc/sku-mouse-x1 può trovarlo subito. Se hai bisogno che appaia nella ricerca prima di continuare un test, usa 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}'

Non aggiungere refresh forzati a ogni scrittura in produzione senza misurarne il costo. Possono ridurre la produttività dell'indicizzazione.

Una regola pratica

Usa POST /_doc per dati append-only dove eventi duplicati sono accettabili o gestiti altrove. Usa PUT /_doc/{id} quando la richiesta contiene il documento corrente completo. Usa _update quando vuoi solo cambiare certi campi. Usa _create quando sovrascrivere nasconderebbe un problema nei dati. Usa _bulk quando il lavoro è più di pochi documenti.

La maggior parte dei bug di indicizzazione di Elasticsearch deriva dalla scelta della forma di scrittura sbagliata. L'endpoint dovrebbe corrispondere ai tuoi dati sorgente, al tuo comportamento di riprova e al fatto che Elasticsearch stia memorizzando un record completo o una copia ricercabile del record di un altro sistema.

Controllare i mapping prima di incolpare la richiesta di scrittura

A volte la scrittura riesce ma il risultato della ricerca sembra ancora sbagliato. Questo è spesso un problema di mapping, non un problema di endpoint di indicizzazione.

Controlla il mapping:

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

Se price è stato indicizzato per la prima volta come testo perché un documento di test iniziale ha inviato "25.99" come stringa, le query di intervallo potrebbero comportarsi male. Se sku necessita di corrispondenza esatta e aggregazioni, dovrebbe di solito essere un campo keyword o avere un sottocampo keyword. Se i timestamp arrivano in formati diversi, alcuni documenti potrebbero essere rifiutati durante l'indicizzazione mentre altri riescono.

Per sistemi prevedibili, crea il mapping dell'indice prima della prima scrittura:

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" }
      }
    }
  }'

Il mapping dinamico è comodo per l'esplorazione. Per l'inserimento in produzione, i mapping espliciti impediscono a un primo documento errato di modellare l'indice.

Gestire conflitti e riprove deliberatamente

Aggiornamenti concorrenti possono entrare in conflitto. Se due worker aggiornano lo stesso documento quasi contemporaneamente, uno potrebbe basarsi su una versione più vecchia. Per semplici riprove di aggiornamento, Elasticsearch supporta 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
    }
  }'

Questo è utile per aggiornamenti a basso rischio, ma non è un sostituto per una chiara proprietà. Se un sistema possiede i nomi dei prodotti e un altro possiede l'inventario, considera di aggiornare campi diversi attraverso pipeline ben definite. Se l'ordine degli eventi è importante, includi timestamp sorgente e rifiuta aggiornamenti più vecchi nel tuo livello di inserimento o con uno script attento.

Leggere dopo aver scritto durante il debug

Quando testi un flusso di indicizzazione, recupera il documento per ID immediatamente:

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

Poi cercalo:

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

Se GET per ID funziona ma la ricerca no, pensa a refresh, mapping, analyzer o tipo di query. Se entrambi falliscono, pensa a errore di indicizzazione, indice sbagliato, ID sbagliato, autenticazione o routing.

Per le applicazioni, registra l'indice di destinazione, l'ID del documento, lo stato della risposta e gli errori a livello di elemento bulk. Non hai bisogno di registrare ogni documento completo per sempre, ma durante il rollout quei campi risparmiano ore.