Elasticsearch examples
← All examplesCopy-paste recipes for the Elasticsearch API, using the official @elastic client. Each one reads from or writes to /v1/tenants/:t/es/. New here? Set the client up in Connect an Elasticsearch client, or see the raw endpoints in the HTTP API reference.
These use the Node client; the same requests work from any Elasticsearch 7.x client or straight over HTTP. Writes commit with the row, so a document is searchable immediately, and row-level security and column masking apply to every query.
1Create an index with a mapping
Declare the fields up front. Types are the Elasticsearch types your client already sends.
await es.indices.create({
index: 'shop.products',
mappings: { properties: {
name: { type: 'text' },
brand: { type: 'keyword' },
price: { type: 'integer' }
} }
})2Index a document
Create or replace a document by _id. It is searchable the instant the call returns — there is no refresh interval to wait on.
await es.index({
index: 'shop.products',
id: 'sku-8842',
document: { name: 'Carbon Marathon', brand: 'Aero', price: 149 }
})3Bulk-load many documents
The fast path for ingest and backfills. One NDJSON action line per document (the client builds it for you from the operations array).
await es.bulk({ operations: [
{ index: { _index: 'shop.products', _id: 'sku-1207' } },
{ name: 'Trail 24', brand: 'Aero', price: 89 },
{ index: { _index: 'shop.products', _id: 'sku-3355' } },
{ name: 'City Runner', brand: 'Metro', price: 72 }
]})4Partial update (merge)
Send only the fields that change. Everything else on the document is preserved.
await es.update({
index: 'shop.products',
id: 'sku-8842',
doc: { price: 139 } // name, brand, ... untouched
})5Full-text match
Run the Query DSL you already write. match analyzes the query the same way the field was indexed.
await es.search({
index: 'shop.products',
query: { match: { name: 'marathon' } }
})6Bool query with a filter
Combine a scoring must with non-scoring filter clauses — a term and a numeric range.
await es.search({
index: 'shop.products',
query: { bool: {
must: [{ match: { name: 'runner' } }],
filter: [{ term: { brand: 'Metro' } },
{ range: { price: { lte: 100 } } }]
} }
})7Aggregate (terms)
Set size: 0 to skip the hits and get just the buckets — the shape Kibana and Grafana panels use.
await es.search({
index: 'shop.products',
size: 0,
aggs: { by_brand: { terms: { field: 'brand' } } }
})8Count matches
A count without the documents.
await es.count({
index: 'shop.products',
query: { term: { brand: 'Aero' } }
})9Paginate with search_after
Deep paging within the 10,000-hit window. Sort by a field plus _id, then pass the previous page’s last sort tuple.
const page1 = await es.search({
index: 'shop.products', size: 20,
sort: [{ price: 'asc' }, { _id: 'asc' }],
query: { match_all: {} }
})
const last = page1.hits.hits.at(-1).sort // e.g. [72, 'sku-3355']
await es.search({
index: 'shop.products', size: 20,
sort: [{ price: 'asc' }, { _id: 'asc' }],
search_after: last,
query: { match_all: {} }
})10Delete by query (bounded)
max_docs caps how many are deleted. An unbounded whole-table delete is refused rather than run — use _reindex into a fresh index for that.
await es.deleteByQuery({
index: 'shop.products',
max_docs: 100,
query: { term: { brand: 'Aero' } }
})