ELK Stack — CRUD Operations

4 minute read

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) or PUT /index/_doc/id
Get document GET /index/_doc/id
Update fields POST /index/_update/id with "doc": {...}
Script update POST /index/_update/id with "script": {...}
Delete document DELETE /index/_doc/id
Bulk operations POST /index/_bulk (NDJSON format)
Delete by query POST /index/_delete_by_query
Versioning Use if_seq_no + if_primary_term for 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.