Document Silme — DELETE ve Delete by Query
DELETE, Delete by Query, Bulk API
Bir kütüphaneden kitap çıkarmayı düşün. Tek bir kitabı raftan alabilirsin (DELETE). "2010 öncesi tüm kitapları çıkarın" diyebilirsin (Delete by Query). Ya da bir liste verirsin — "Bu 50 kitabı çıkarın" (Bulk DELETE). Elasticsearch'te silme işlemi de tam olarak bu üç yolla yapılır.
Ama bilmen gereken önemli bir detay var: Elasticsearch'te silme anında fiziksel olarak gerçekleşmez. Döküman "deleted" olarak işaretlenir ve arka planda segment merge sırasında temizlenir. Bu, performans ve tutarlılık için kritik bir tasarım kararı.
Silme Nasıl Çalışır? — Perde Arkası
1. DELETE /products/_doc/1
2. Elasticsearch, dökümanı segmentte "deleted" olarak işaretler
→ Döküman hâlâ diskte duruyor
→ Ama aramalarda görünmüyor (filtreleniyor)
3. Segment merge sırasında:
→ Deleted dökümanlar fiziksel olarak temizlenir
→ Disk alanı geri kazanılır
┌──────────────┐ ┌──────────────┐
│ Segment A │ Merge │ Segment C │
│ Doc 1 ✅ │ ───→ │ Doc 1 ✅ │
│ Doc 2 ❌ del │ │ Doc 3 ✅ │
│ Doc 3 ✅ │ │ │
└──────────────┘ └──────────────┘
Doc 2 silindi! Temizlendi!Bu mekanizmanın sonuçları:
Silme hemen disk alanı geri vermez — merge beklemek gerekir
Silme performansı yüksektir — fiziksel silme yapmaz, sadece işaretler
Deleted dökümanlar bellek ve disk kullanmaya devam eder — merge olana kadar
DELETE — Tekil Döküman Silme
En temel silme operasyonu:
// ID ile döküman sil
DELETE /products/_doc/1
// Yanıt (başarılı):
{
"_index": "products",
"_id": "1",
"_version": 4,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 15,
"_primary_term": 1
}Var Olmayan Dökümanı Silme
DELETE /products/_doc/999
// Yanıt (HTTP 404):
{
"_index": "products",
"_id": "999",
"_version": 1,
"result": "not_found",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
}
}result: "not_found" — döküman zaten yoktu. Bu bir hata değil, HTTP 404 döner. İdempotent davranış: aynı silme isteğini tekrar göndersen sorun çıkmaz.
Versiyonlu Silme (Optimistic Concurrency)
// Sadece belirli versiyondaysa sil
DELETE /products/_doc/1?if_seq_no=15&if_primary_term=1
// seq_no veya primary_term uyuşmazsa:
// 409 Conflict hatasıRouting ile Silme
// Custom routing ile index'lenen dökümanı silerken routing belirtmek ZORUNLU
DELETE /orders/_doc/order-1?routing=customer-42
// Routing belirtmezsen yanlış shard'a gider — döküman bulunamaz
DELETE /orders/_doc/order-1
// result: "not_found" — çünkü yanlış shard'a baktı!Refresh Parametresi
// Silme sonrası hemen arama sonuçlarından kaybolsun
DELETE /products/_doc/1?refresh=true
// Bir sonraki refresh cycle'ı bekle (daha verimli)
DELETE /products/_doc/1?refresh=wait_forDelete By Query — Toplu Silme
Belirli kriterlere uyan tüm dökümanları sil:
// Stokta olmayan tüm ürünleri sil
POST /products/_delete_by_query
{
"query": {
"term": {
"in_stock": false
}
}
}
// Yanıt:
{
"took": 85,
"timed_out": false,
"total": 150, // Eşleşen toplam
"deleted": 150, // Silinen
"batches": 1, // Kaç batch'te yapıldı
"version_conflicts": 0, // Çakışma
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"failures": []
}Delete By Query Örnekleri
// 2023 öncesi tüm logları sil
POST /app-logs-*/_delete_by_query
{
"query": {
"range": {
"@timestamp": {
"lt": "2023-01-01"
}
}
}
}
// Belirli bir kullanıcının tüm verilerini sil (GDPR compliance)
POST /user-data/_delete_by_query
{
"query": {
"term": {
"user_id": "user-12345"
}
}
}
// Birden fazla koşul ile silme
POST /products/_delete_by_query
{
"query": {
"bool": {
"must": [
{ "term": { "status": "discontinued" } },
{ "range": { "stock_count": { "lte": 0 } } },
{ "range": { "updated_at": { "lt": "2024-01-01" } } }
]
}
}
}
// Tüm dökümanları sil (index'i silmeden)
POST /products/_delete_by_query
{
"query": {
"match_all": {}
}
}
// ⚠️ Bu mapping ve settings'i korur — sadece veriyi siler
// Index'i tamamen silmek istiyorsan: DELETE /productsDelete By Query Parametreleri
// Çakışmalarda devam et
POST /products/_delete_by_query?conflicts=proceed
{
"query": { "match_all": {} }
}
// Paralel işleme (slices)
POST /products/_delete_by_query?slices=5
{
"query": {
"range": { "created_at": { "lt": "2024-01-01" } }
}
}
// Otomatik slice sayısı (shard sayısı kadar)
POST /products/_delete_by_query?slices=auto
{
"query": { "match_all": {} }
}
// Maksimum silme sayısı
POST /products/_delete_by_query?max_docs=1000
{
"query": { "match_all": {} }
}
// Sadece ilk 1000 eşleşeni siler
// Asenkron çalıştır (büyük silmelerde)
POST /products/_delete_by_query?wait_for_completion=false
{
"query": {
"range": { "created_at": { "lt": "2024-01-01" } }
}
}
// Yanıt: {"task": "node-1:54321"}
// Task durumunu kontrol et
GET /_tasks/node-1:54321
// Task'ı iptal et
POST /_tasks/node-1:54321/_cancelDelete By Query — Scroll Size
// Dahili scroll boyutunu ayarla (varsayılan 1000)
POST /products/_delete_by_query?scroll_size=5000
{
"query": { "match_all": {} }
}
// Daha büyük scroll_size = daha az batch = daha hızlı ama daha fazla bellekIndex Silme vs Delete By Query
İki yaklaşımın farkını anlamak önemli:
// Yöntem 1: Index'i komple sil (ÇOK HIZLI)
DELETE /app-logs-2024.01.15
// Anında tamamlanır — sadece metadata silme
// Mapping, settings, tüm veri gider
// Yöntem 2: Dökümanları sil, index'i koru (YAVAŞ)
POST /app-logs-2024.01.15/_delete_by_query
{
"query": { "match_all": {} }
}
// Her dökümanı tek tek "deleted" işaretler — dakikalar sürebilir
// Mapping ve settings korunur
// Disk alanı hemen geri gelmez (merge bekler)| Özellik | DELETE /index | _delete_by_query |
|---|---|---|
| Hız | Anlık (metadata silme) | Yavaş (döküman bazlı) |
| Mapping/Settings | Silinir | Korunur |
| Disk geri kazanımı | Anında | Merge sonrası |
| Kullanım | Log index'leri, geçici veriler | Seçici silme |
💡 İpucu: Zaman bazlı veriler (loglar, metrikler) için günlük/haftalık index kullan. "30 günden eski logları sil" demek yerine eski index'i komple sil — çok daha hızlı ve temiz.
Bulk API ile Silme
Birden fazla dökümanı tek request'te sil:
POST /_bulk
{"delete": {"_index": "products", "_id": "1"}}
{"delete": {"_index": "products", "_id": "2"}}
{"delete": {"_index": "products", "_id": "3"}}
{"delete": {"_index": "products", "_id": "4"}}
{"delete": {"_index": "products", "_id": "5"}}Dikkat: delete action'da body satırı olmaz! Diğer action'lardan (index, create, update) farklı olarak sadece action satırı yeterli.
Bulk Karışık İşlemler
POST /_bulk
{"index": {"_index": "products", "_id": "10"}}
{"name": "Yeni Ürün", "price": 500}
{"update": {"_index": "products", "_id": "11"}}
{"doc": {"price": 600}}
{"delete": {"_index": "products", "_id": "12"}}
{"delete": {"_index": "products", "_id": "13"}}Belirli Index'te Bulk Silme
POST /products/_bulk
{"delete": {"_id": "1"}}
{"delete": {"_id": "2"}}
{"delete": {"_id": "3"}}Gerçek Dünya Senaryoları
Senaryo 1: GDPR — Kullanıcı Verisi Silme
Avrupa'da GDPR yasası gereği, kullanıcı talepte bulunursa tüm kişisel verileri silinmeli:
// 1. Kullanıcının tüm index'lerdeki verilerini bul
GET /users/_doc/user-12345
GET /orders/_search
{ "query": { "term": { "user_id": "user-12345" } } }
GET /reviews/_search
{ "query": { "term": { "user_id": "user-12345" } } }
GET /activity-logs-*/_search
{ "query": { "term": { "user_id": "user-12345" } } }
// 2. Her index'ten sil
DELETE /users/_doc/user-12345
POST /orders/_delete_by_query
{ "query": { "term": { "user_id": "user-12345" } } }
POST /reviews/_delete_by_query
{ "query": { "term": { "user_id": "user-12345" } } }
POST /activity-logs-*/_delete_by_query
{ "query": { "term": { "user_id": "user-12345" } } }
// 3. Force merge ile fiziksel olarak temizle (disk'ten de kaldır)
POST /users/_forcemerge?only_expunge_deletes=true
POST /orders/_forcemerge?only_expunge_deletes=true
POST /reviews/_forcemerge?only_expunge_deletes=true
// only_expunge_deletes=true: Sadece deleted dökümanları temizleSenaryo 2: Log Retention — Eski Logları Temizleme
// Yöntem 1 (Önerilen): Index silme — ÇOK HIZLI
// Günlük index'ler: app-logs-2024.01.01, app-logs-2024.01.02, ...
DELETE /app-logs-2024.01.*
// Ocak 2024'ün tüm log index'leri silindi — milisaniyede
// Yöntem 2 (Önerilmez): Delete by query — YAVAŞ
POST /app-logs-*/_delete_by_query
{
"query": {
"range": {
"@timestamp": { "lt": "2024-02-01" }
}
}
}
// Her dökümanı tek tek siler — saatler sürebilir
// Yöntem 3 (Otomatik): ILM Policy
PUT /_ilm/policy/log-retention
{
"policy": {
"phases": {
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
// 30 günden eski index'ler otomatik silinirSenaryo 3: E-Ticaret — Ürün Kaldırma
// Tek ürün kaldırma
DELETE /products/_doc/PROD-2025-001
// Marka toplu kaldırma (markayı platform'dan çıkarma)
POST /products/_delete_by_query
{
"query": {
"term": { "brand.keyword": "BannedBrand" }
}
}
// Süresi dolmuş kampanya ürünlerini temizle
POST /products/_delete_by_query
{
"query": {
"bool": {
"must": [
{ "exists": { "field": "campaign_end" } }
],
"filter": [
{ "range": { "campaign_end": { "lt": "now" } } },
{ "term": { "auto_remove": true } }
]
}
}
}Force Merge ile Deleted Döküman Temizleme
Silinen dökümanlar segment merge'e kadar disk'te kalır. Bunu hızlandırabilirsin:
// Sadece deleted dökümanları temizle (güvenli)
POST /products/_forcemerge?only_expunge_deletes=true
// Segmentleri birleştir + deleted temizle (daha agresif)
POST /products/_forcemerge?max_num_segments=1
// Tüm index'lerde temizlik
POST /_forcemerge?only_expunge_deletes=true⚠️ Dikkat: _forcemerge kaynak yoğun bir işlemdir. Aktif yazma olan index'lerde çalıştırma — read-only veya yazma olmayan index'lerde kullan.
Deleted Döküman Sayısını Kontrol Etme
// Segment bilgisinde deleted_docs gösterir
GET /products/_segments
// _stats ile
GET /products/_stats/docs
// Yanıt:
{
"indices": {
"products": {
"primaries": {
"docs": {
"count": 1000, // Aktif döküman
"deleted": 150 // Silinmiş ama henüz temizlenmemiş
}
}
}
}
}
// _cat ile hızlı bakış
GET /_cat/indices/products?v&h=index,docs.count,docs.deleted,store.sizeJava ile Document Silme
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
class Main {
public static void main(String[] args) throws Exception {
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)
).build();
ElasticsearchClient client = new ElasticsearchClient(
new RestClientTransport(restClient, new JacksonJsonpMapper())
);
// 1. Tekil silme
DeleteResponse deleteResponse = client.delete(d -> d
.index("products")
.id("1")
);
System.out.println("Result: " + deleteResponse.result());
// "deleted" veya "not_found"
// 2. Routing ile silme
DeleteResponse routedDelete = client.delete(d -> d
.index("orders")
.id("order-1")
.routing("customer-42")
);
// 3. Delete by query
var dbqResponse = client.deleteByQuery(d -> d
.index("products")
.query(q -> q
.term(t -> t
.field("in_stock")
.value(false)
)
)
);
System.out.println("Deleted: " + dbqResponse.deleted());
System.out.println("Total matched: " + dbqResponse.total());
System.out.println("Conflicts: " + dbqResponse.versionConflicts());
// 4. Delete by query — range
var rangeDelete = client.deleteByQuery(d -> d
.index("app-logs-*")
.query(q -> q
.range(r -> r
.field("@timestamp")
.lt(co.elastic.clients.json.JsonData.of("2024-01-01"))
)
)
.slices(s -> s.value(5)) // Paralel silme
);
System.out.println("Old logs deleted: " + rangeDelete.deleted());
// 5. Bulk silme
var bulkResponse = client.bulk(b -> b
.index("products")
.operations(ops -> ops.delete(d -> d.id("10")))
.operations(ops -> ops.delete(d -> d.id("11")))
.operations(ops -> ops.delete(d -> d.id("12")))
);
System.out.println("Bulk errors: " + bulkResponse.errors());
bulkResponse.items().forEach(item ->
System.out.printf("ID: %s → %s%n", item.id(), item.result())
);
// 6. Index silme
var indexDeleteResponse = client.indices().delete(d -> d
.index("old-index")
);
System.out.println("Index deleted: " + indexDeleteResponse.acknowledged());
restClient.close();
}
}Tehlikeli İşlemler ve Güvenlik
Yanlışlıkla Index Silmeyi Engelleme
// Wildcard ile index silmeyi engelle
PUT /_cluster/settings
{
"persistent": {
"action.destructive_requires_name": true
}
}
// Artık bu ÇALIŞMAZ:
DELETE /app-logs-*
// ❌ Wildcard ile silme engellendi!
// Ama bu çalışır:
DELETE /app-logs-2024.01.15
// ✅ Tam isim ile silme — izin verilirIndex Silme Koruması
// Index'i write-protected yap (silmeye karşı koruma)
PUT /important-data/_settings
{
"index.blocks.read_only": true
}
// Artık DELETE çalışmaz:
DELETE /important-data/_doc/1
// ❌ 403 Forbidden — index read-only
// Korumayı kaldır
PUT /important-data/_settings
{
"index.blocks.read_only": false
}Alias ile Güvenli Silme
// Uygulama alias üzerinden çalışır
// Alias üzerinden index silme yapılamaz — güvenlik katmanı
DELETE /products-alias
// ❌ Sadece alias'ı siler, arkadaki index'e dokunmaz
// Index'i silmek için gerçek adını bilmelisin
DELETE /products-v2
// ✅ Bu gerçek index'i silerPerformans İpuçları
1. Toplu Silme için Index Silmeyi Tercih Et
Yöntem 1 — delete_by_query:
1 milyon döküman silme → ~30 dakika
Disk geri kazanımı → merge sonrası (saatler)
Yöntem 2 — Index silme:
1 milyon döküman silme → ~10 milisaniye
Disk geri kazanımı → anında2. Delete by Query'de Slices Kullan
// 5 paralel dilim — 5x hızlı
POST /big-index/_delete_by_query?slices=5
{
"query": { "range": { "date": { "lt": "2024-01-01" } } }
}3. Asenkron Çalıştır
// Büyük silme işlemlerini asenkron çalıştır
POST /big-index/_delete_by_query?wait_for_completion=false
{
"query": { "match_all": {} }
}
// Task ID al, arka planda çalışsın4. Force Merge ile Disk Geri Kazan
// Silme sonrası disk alanını geri al
POST /products/_forcemerge?only_expunge_deletes=trueBest Practices
✅ Zaman bazlı veriler için index silme kullan — delete_by_query yerine eski index'i komple sil
✅ `action.destructive_requires_name: true` ayarla — Wildcard ile yanlışlıkla silmeyi engelle
✅ Büyük silmelerde `slices` kullan — Paralel işleme ile hızlandır
✅ Custom routing varsa silmede de routing belirt — Yoksa döküman bulunamaz
✅ ILM ile otomatik retention yap — Manuel silme yerine policy tanımla
✅ Force merge ile deleted dökümanları temizle — Disk geri kazanımı için
Yaygın Hatalar
❌ "delete_by_query ile tüm index'i temizliyorum"
Index'i silip yeniden oluşturmak çok daha hızlı. delete_by_query döküman bazlı çalışır, milyonlarca kayıtta çok yavaş.
❌ "Döküman sildim ama disk alanı azalmadı"
Silme fiziksel değil, işaretleme. Merge olana kadar disk alanı geri gelmez. _forcemerge?only_expunge_deletes=true ile hızlandır.
❌ "Custom routing ile eklediğim dökümanı routing'siz siliyorum"
Yanlış shard'a gider, "not_found" döner. Routing belirtmek zorunlu.
❌ "Production'da wildcard ile index siliyorum"
DELETE /logs-* yanlışlıkla beklediğinden fazla index silebilir. destructive_requires_name: true ayarla.
❌ "delete_by_query sırasında yazma devam ediyor"
delete_by_query başladığında snapshot alır. Sonra eklenen dökümanlar silinmez. Çakışma olursa version_conflicts artar. conflicts=proceed ile devam edebilirsin.
CRUD İşlemleri Özet Tablosu
Bu bölümdeki tüm CRUD işlemlerini tek tabloda toparlayalım:
| İşlem | API | Açıklama |
|---|---|---|
| Create (belirli ID) | PUT /index/_doc/ID | Varsa replace eder |
| Create (otomatik ID) | POST /index/_doc | Benzersiz ID üretir |
| Create (strict) | PUT /index/_create/ID | Varsa 409 hata |
| Read (tekil) | GET /index/_doc/ID | Metadata + _source |
| Read (source only) | GET /index/_source/ID | Sadece _source |
| Read (çoklu) | GET /index/_mget | Toplu okuma |
| Read (varlık) | HEAD /index/_doc/ID | 200 veya 404 |
| Update (replace) | PUT /index/_doc/ID | Tüm dokümanı değiştirir |
| Update (partial) | POST /index/_update/ID | Sadece belirtilen alanlar |
| Update (scripted) | POST /index/_update/ID | Painless script ile |
| Update (toplu) | POST /index/_update_by_query | Sorguya uyanları güncelle |
| Delete (tekil) | DELETE /index/_doc/ID | Tek döküman |
| Delete (toplu) | POST /index/_delete_by_query | Sorguya uyanları sil |
| Delete (index) | DELETE /index | Tüm index'i sil |
| Bulk (karma) | POST /_bulk | index/create/update/delete |
Özet
DELETE /index/_doc/ID ile tekil döküman silersin — anlık, ama fiziksel silme merge'de olur
Delete by Query ile sorguya uyan tüm dökümanları toplu silersin —
slicesile paralelize etBulk API ile birden fazla dökümanı tek request'te silersin —
deleteaction'da body yokIndex silme (
DELETE /index), delete_by_query'den çok daha hızlıdır — zaman bazlı veriler için kullanSilinen dökümanlar hemen disk'ten kalkmaz — force merge ile temizlenir
action.destructive_requires_name: trueile wildcard silmeyi engelleCustom routing varsa silmede de routing parametresini belirt
GDPR compliance için kullanıcının tüm verilerini tüm index'lerden sil + force merge
Bir sonraki bölümde Arama Temelleri — Query DSL'e geçeceğiz — Elasticsearch'ün en güçlü silahını öğreneceğiz!
AI Asistan
Sorularını yanıtlamaya hazır