ELK Stack — CRUD Operations
CRUD Operations
CRUD stands for Create, Read, Update, Delete — the four basic operations you perform on documents in Elasticsearch. Let’s master each one with practical examples.
Setup — Create a Test Index
$ curl -X PUT -u elastic:changeme123 \
"http://localhost:9200/employees" \
-H "Content-Type: application/json" \
-d '{
"mappings": {
"properties": {
"name": { "type": "text" },
"department": { "type": "keyword" },
"salary": { "type": "integer" },
"hire_date": { "type": "date" },
"active": { "type": "boolean" }
}
},
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
}
}'
Create (Index) Documents
Auto-generated ID (POST):
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_doc" \
-H "Content-Type: application/json" \
-d '{
"name": "Alice Johnson",
"department": "Engineering",
"salary": 95000,
"hire_date": "2022-03-15",
"active": true
}'
Expected output:
{
"_index": "employees",
"_id": "a1b2c3d4...",
"_version": 1,
"result": "created"
}
Custom ID (PUT):
$ curl -X PUT -u elastic:changeme123 \
"http://localhost:9200/employees/_doc/emp-001" \
-H "Content-Type: application/json" \
-d '{
"name": "Bob Smith",
"department": "Marketing",
"salary": 72000,
"hire_date": "2021-08-01",
"active": true
}'
Bulk Indexing (Multiple Documents):
The _bulk API lets you index many documents in one request — much faster than individual calls:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_bulk" \
-H "Content-Type: application/x-ndjson" \
-d '
{"index": {"_id": "emp-002"}}
{"name": "Carol Davis", "department": "Engineering", "salary": 105000, "hire_date": "2020-01-10", "active": true}
{"index": {"_id": "emp-003"}}
{"name": "David Wilson", "department": "Sales", "salary": 68000, "hire_date": "2023-06-20", "active": true}
{"index": {"_id": "emp-004"}}
{"name": "Eve Martinez", "department": "Engineering", "salary": 88000, "hire_date": "2022-11-05", "active": false}
{"index": {"_id": "emp-005"}}
{"name": "Frank Brown", "department": "Marketing", "salary": 75000, "hire_date": "2021-04-15", "active": true}
'
Important: The bulk API uses NDJSON format — each line must be on its own line, and the request must end with a newline.
Expected output (truncated):
{
"took": 30,
"errors": false,
"items": [
{ "index": { "_id": "emp-002", "result": "created", "status": 201 } },
{ "index": { "_id": "emp-003", "result": "created", "status": 201 } },
{ "index": { "_id": "emp-004", "result": "created", "status": 201 } },
{ "index": { "_id": "emp-005", "result": "created", "status": 201 } }
]
}
Read (Retrieve) Documents
Get a Single Document by ID:
$ curl -u elastic:changeme123 \
"http://localhost:9200/employees/_doc/emp-001?pretty"
Expected output:
{
"_index": "employees",
"_id": "emp-001",
"_version": 1,
"found": true,
"_source": {
"name": "Bob Smith",
"department": "Marketing",
"salary": 72000,
"hire_date": "2021-08-01",
"active": true
}
}
Get Only Specific Fields:
$ curl -u elastic:changeme123 \
"http://localhost:9200/employees/_doc/emp-001?_source_includes=name,salary&pretty"
Check If a Document Exists (HEAD request):
$ curl -I -u elastic:changeme123 \
"http://localhost:9200/employees/_doc/emp-001"
Returns 200 OK if found, 404 Not Found if not.
Get Multiple Documents (Multi-GET):
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_mget" \
-H "Content-Type: application/json" \
-d '{
"ids": ["emp-001", "emp-002", "emp-003"]
}'
Search All Documents:
$ curl -u elastic:changeme123 \
"http://localhost:9200/employees/_search?pretty"
This returns all documents (up to the default limit of 10).
Update Documents
Partial Update:
Update specific fields without replacing the entire document:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_update/emp-001" \
-H "Content-Type: application/json" \
-d '{
"doc": {
"salary": 78000,
"department": "Digital Marketing"
}
}'
Expected output:
{
"_index": "employees",
"_id": "emp-001",
"_version": 2,
"result": "updated"
}
Notice _version incremented from 1 to 2.
Scripted Update:
Use a script to modify a value based on its current value:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_update/emp-001" \
-H "Content-Type: application/json" \
-d '{
"script": {
"source": "ctx._source.salary += params.raise_amount",
"params": {
"raise_amount": 5000
}
}
}'
This adds $5,000 to Bob’s current salary (78000 + 5000 = 83000).
Upsert (Update or Insert):
If the document exists, update it. If it doesn’t exist, create it:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_update/emp-006" \
-H "Content-Type: application/json" \
-d '{
"doc": {
"name": "Grace Lee",
"department": "HR",
"salary": 70000,
"active": true
},
"doc_as_upsert": true
}'
Update by Query:
Update all documents matching a condition:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_update_by_query" \
-H "Content-Type: application/json" \
-d '{
"query": {
"term": { "department": "Engineering" }
},
"script": {
"source": "ctx._source.salary += 10000"
}
}'
This gives all Engineering employees a $10,000 raise.
Delete Documents
Delete by ID:
$ curl -X DELETE -u elastic:changeme123 \
"http://localhost:9200/employees/_doc/emp-004"
Expected output:
{
"_index": "employees",
"_id": "emp-004",
"_version": 2,
"result": "deleted"
}
Delete by Query:
Delete all documents matching a condition:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_delete_by_query" \
-H "Content-Type: application/json" \
-d '{
"query": {
"term": { "active": false }
}
}'
Delete All Documents in an Index:
$ curl -X POST -u elastic:changeme123 \
"http://localhost:9200/employees/_delete_by_query" \
-H "Content-Type: application/json" \
-d '{
"query": { "match_all": {} }
}'
Note: This deletes documents but keeps the index. Use DELETE /employees to delete the entire index.
Versioning and Concurrency
Elasticsearch uses optimistic concurrency control with version numbers:
# Get the document (note _seq_no and _primary_term)
$ curl -u elastic:changeme123 "http://localhost:9200/employees/_doc/emp-001?pretty"
# Update only if seq_no and primary_term match
$ curl -X PUT -u elastic:changeme123 \
"http://localhost:9200/employees/_doc/emp-001?if_seq_no=5&if_primary_term=1" \
-H "Content-Type: application/json" \
-d '{ "name": "Bob Smith", "salary": 90000, "department": "Marketing" }'
If another process updated the document between your read and write, the update fails with a 409 Conflict error.
CRUD Operations Summary
| Operation | HTTP Method | Endpoint | Example |
|---|---|---|---|
| Create (auto ID) | POST | /<index>/_doc |
Index a new document |
| Create (custom ID) | PUT | /<index>/_doc/<id> |
Index with specific ID |
| Bulk create | POST | /<index>/_bulk |
Index many documents |
| Read by ID | GET | /<index>/_doc/<id> |
Retrieve one document |
| Read multiple | POST | /<index>/_mget |
Retrieve several documents |
| Search | GET/POST | /<index>/_search |
Find documents by query |
| Partial update | POST | /<index>/_update/<id> |
Update specific fields |
| Update by query | POST | /<index>/_update_by_query |
Update matching documents |
| Delete by ID | DELETE | /<index>/_doc/<id> |
Delete one document |
| Delete by query | POST | /<index>/_delete_by_query |
Delete matching documents |
Quick Reference
Operation Command Index document POST /index/_doc(auto ID) orPUT /index/_doc/idGet document GET /index/_doc/idUpdate fields POST /index/_update/idwith"doc": {...}Script update POST /index/_update/idwith"script": {...}Delete document DELETE /index/_doc/idBulk operations POST /index/_bulk(NDJSON format)Delete by query POST /index/_delete_by_queryVersioning Use if_seq_no+if_primary_termfor optimistic locking
Next Step
You can now perform all CRUD operations. Let’s explore the Elasticsearch REST API in more depth — cluster management, cat APIs, and more.