Elasticsearch CRUD Operations
CRUD stands for Create, Read, Update, and Delete. These four operations cover everything you do with individual documents in Elasticsearch. Every web application relies on these patterns daily.
The CRUD Whiteboard
+--------+--------+---------------------------+ | Letter | Action | HTTP Method | +--------+--------+---------------------------+ | C | Create | PUT or POST | | R | Read | GET | | U | Update | POST (_update) or PUT | | D | Delete | DELETE | +--------+--------+---------------------------+
Create — Adding a Document
Use PUT with a specific ID to create a document at a known location:
PUT /employees/_doc/1
{
"name": "Priya Sharma",
"department": "Engineering",
"salary": 95000,
"join_date": "2023-03-15"
}
Use POST without an ID to let Elasticsearch generate one automatically:
POST /employees/_doc
{
"name": "Raj Patel",
"department": "Marketing",
"salary": 72000,
"join_date": "2024-01-10"
}
Elasticsearch returns the auto-generated ID in the response under the _id key.
Create Only — Avoid Overwriting
If you use PUT and the document already exists, Elasticsearch overwrites it. Add _create to the URL to prevent that:
PUT /employees/_create/1
{
"name": "Priya Sharma",
...
}
If document ID 1 already exists, Elasticsearch returns a 409 Conflict error instead of overwriting.
Read — Fetching a Document
GET /employees/_doc/1
To fetch only specific fields instead of the entire document, use _source_includes:
GET /employees/_doc/1?_source_includes=name,salary
This returns only the name and salary fields — useful when documents have many fields and you need just a few.
Update — Modifying Specific Fields
The update API changes only the fields you specify. Other fields in the document stay untouched.
POST /employees/_update/1
{
"doc": {
"salary": 102000
}
}
Only Priya's salary changes. Her department, join_date, and name remain exactly as they were.
Update vs Full Replace
Update API (POST _update):
Before: { name: "Priya", dept: "Engineering", salary: 95000 }
Change: salary to 102000
After: { name: "Priya", dept: "Engineering", salary: 102000 }
✔ Other fields preserved
PUT (full replace):
Before: { name: "Priya", dept: "Engineering", salary: 95000 }
New doc: { salary: 102000 }
After: { salary: 102000 }
✘ name and dept are GONE
Always use the update API when you only want to change a few fields.
Update with Script
Use a Painless script to perform calculations during an update — for example, giving everyone a 10% raise:
POST /employees/_update/1
{
"script": {
"source": "ctx._source.salary = ctx._source.salary * 1.10"
}
}
Delete — Removing a Document
DELETE /employees/_doc/1
Elasticsearch marks the document as deleted and removes it from search results immediately. The disk space is reclaimed later during a background merge process.
Delete by Query
Delete multiple documents matching a condition without deleting the whole index:
POST /employees/_delete_by_query
{
"query": {
"term": {
"department": "Marketing"
}
}
}
All Marketing employees are removed. Engineering employees remain.
Bulk API — Multiple Operations at Once
The Bulk API sends many CRUD operations in a single HTTP request. This is far faster than one request per document when loading large datasets.
POST /_bulk
{ "index": { "_index": "employees", "_id": "10" } }
{ "name": "Anita Roy", "department": "Finance", "salary": 68000 }
{ "index": { "_index": "employees", "_id": "11" } }
{ "name": "Sanjay Gupta", "department": "Sales", "salary": 55000 }
{ "delete": { "_index": "employees", "_id": "5" } }
Each action line is followed by the data line for that action. Delete has no data line. Elasticsearch processes every operation and reports success or failure for each one independently — a single failure does not stop the rest.
Check If a Document Exists
HEAD /employees/_doc/1
A 200 response means the document exists. A 404 means it does not. HEAD returns no body — only the status code — which makes it faster than GET for existence checks.
