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.

Leave a Comment