MongoDB'de belge silmek için iki temel yöntem vardır: db.collection.deleteOne() filtreye uyan ilk belgeyi, db.collection.deleteMany() ise filtreye uyan tüm belgeleri siler. Silinen belgenin kendisini geri almak istiyorsanız findOneAndDelete(), koleksiyonu indeksleriyle birlikte tamamen kaldırmak istiyorsanız db.collection.drop() kullanılır. Bu yazıda dördünü de mongosh üzerinden, seçenekleri ve tuzaklarıyla birlikte ele alıyoruz.

Silme işlemi geri alınamaz; bu yüzden yöntemin ne döndürdüğünü, boş filtrenin ne anlama geldiğini, silmenin indekslere ve oplog'a etkisini bilmek önemlidir. Filtre yazımının ayrıntıları için find sorguları ve sorgu operatörleri yazılarına, serinin tamamı için MongoDB öğrenme rehberine bakabilirsiniz.

Silme yöntemlerine genel bakış

Silme yöntemlerine genel bakış: deleteOne(filtre), deleteMany(filtre), findOneAndDelete(filtre), drop()

Dört yöntem de bir filtre (sorgu koşulu) alır ve find() ile aynı sorgu operatörlerini kabul eder. Aralarındaki fark, kaç belgeyi etkiledikleri ve size ne döndürdükleridir. Aşağıdaki tablo seçim yaparken hızlı bir başvuru olarak kullanılabilir.

YöntemNe silerNe döndürürİndeksler
deleteOne(filtre)Filtreye uyan ilk belgeacknowledged ve deletedCountKorunur
deleteMany(filtre)Filtreye uyan tüm belgeleracknowledged ve deletedCountKorunur
findOneAndDelete(filtre)Filtreye (ve sort seçeneğine) göre ilk belgeSilinen belgenin kendisiKorunur
drop()Koleksiyonun tamamıtrueKoleksiyonla birlikte silinir

Belgelere göre silme işlemleri, koleksiyondaki bütün belgeler silinse bile indeksleri kaldırmaz. Yani deleteMany({}) sonrasında koleksiyon boş ama yapısı yerinde kalır; indeks tanımları, doğrulama kuralları ve koleksiyon seçenekleri aynen durur. drop() ise koleksiyonu ve ona bağlı indeksleri birlikte ortadan kaldırır.

deleteOne: tek bir belgeyi silmek

deleteOne() ilk parametre olarak filtre, isteğe bağlı ikinci parametre olarak bir seçenek belgesi alır. Filtreye birden fazla belge uysa bile yalnızca ilk eşleşen belge silinir. "İlk" kavramı burada sizin belirlediğiniz bir sıralama anlamına gelmez; hangi belgenin önce bulunacağı garanti edilmez. Belgeler bu yüzden tam olarak hangi belgeyi silmek istiyorsanız _id gibi benzersiz bir indekse dahil bir alanla filtre yazmanızı önerir.

javascript
// Örnek veri
db.siparisler.insertMany([
  { _id: 1, musteri: "ayse", durum: "iptal", tutar: 120 },
  { _id: 2, musteri: "mehmet", durum: "teslim", tutar: 80 },
  { _id: 3, musteri: "ayse", durum: "iptal", tutar: 45 },
  { _id: 4, musteri: "zeynep", durum: "beklemede", tutar: 300 }
])

// _id ile kesin silme
db.siparisler.deleteOne({ _id: 3 })
// çıktı: { acknowledged: true, deletedCount: 1 }

Dönen belgede iki alan bulunur. acknowledged, işlem bir write concern ile çalıştıysa true, write concern devre dışı bırakıldıysa false olur. deletedCount ise silinen belge sayısıdır. Filtreye uyan belge yoksa hata almazsınız; işlem başarıyla döner ve deletedCount sıfır olur. Uygulama kodunda "silindi mi" kontrolünü bu sayıya bakarak yapmak gerekir.

Boş filtre {} verirseniz deleteOne() koleksiyondan dönen ilk belgeyi siler. Bu çoğu zaman istenen bir davranış değildir ve genellikle bir değişkenin boş kalmasından kaynaklanan bir hatanın sonucudur. Filtreyi bir değişkenden oluşturuyorsanız, boş olup olmadığını silmeden önce kontrol edin.

deleteMany: koşula uyan tüm belgeleri silmek

deleteMany() aynı söz dizimini kullanır ama filtreye uyan her belgeyi siler. Örneğin iptal edilmiş bütün siparişleri temizlemek veya belirli bir tarihten eski kayıtları kaldırmak için kullanılır. Dönüş değeri yine acknowledged ve deletedCount alanlarından oluşur; bu kez sayı birden büyük olabilir.

javascript
// Durumu "iptal" olan tüm siparişleri sil
db.siparisler.deleteMany({ durum: "iptal" })

// Operatörlerle koşul: tutarı 50'den küçük ve teslim edilmiş siparişler
db.siparisler.deleteMany({ durum: "teslim", tutar: { $lt: 50 } })

// Koleksiyondaki BÜTÜN belgeleri sil (indeksler kalır)
db.siparisler.deleteMany({})

Belgelerde deleteMany() için önemli bir davranış notu vardır: yöntem belgeleri tek tek siler. Bir replica set'te primary düğüm işlem sırasında çökerse, henüz silinmemiş belgeler silinmeden kalır; yani işlem yarıda kesilmiş olabilir. Bir silme işleminin ya tamamen ya hiç gerçekleşmesi gerekiyorsa işlemi bir transaction içinde çalıştırmayı değerlendirin. MongoDB'de yazma işlemleri tek belge düzeyinde atomiktir; birden çok belgeye yayılan bir silme, tek bir atomik işlem değildir.

Oplog açısından da fark vardır: deleteMany() en az bir belgeyi başarıyla silerse, silinen her belge için oplog'a ayrı bir kayıt ekler. İşlem başarısız olursa veya silinecek belge bulamazsa oplog'a kayıt düşmez. Milyonlarca belgeyi tek seferde silmek, replikasyon tarafında da o kadar kayıt üretir. Bu, ikincil düğümlerin işi yetiştirmesi için zaman gerektirebilir; büyük temizlik işlerini yoğun olmayan saatlere planlamak bu yüzden mantıklıdır.

findOneAndDelete: silinen belgeyi geri almak

Bazen bir belgeyi silerken içeriğine de ihtiyaç duyarsınız. Tipik örnek, bir iş kuyruğundan sıradaki işi alıp aynı anda kuyruktan çıkarmaktır. findOneAndDelete() tam bunu yapar: filtreye uyan ilk belgeyi siler ve silinen belgenin orijinal halini döndürür. deleteOne()'dan farklı olarak sort seçeneği alır; böylece "ilk" belgenin hangisi olacağını siz belirlersiniz.

javascript
db.isler.insertMany([
  { _id: 10, tur: "eposta", oncelik: 2 },
  { _id: 11, tur: "eposta", oncelik: 1 },
  { _id: 12, tur: "rapor", oncelik: 3 }
])

// En düşük öncelik değerine sahip e-posta işini al ve sil
db.isler.findOneAndDelete(
  { tur: "eposta" },
  { sort: { oncelik: 1 } }
)
// çıktı: { _id: 11, tur: "eposta", oncelik: 1 }

Seçenek belgesinde projection ile dönen belgede hangi alanların yer alacağını, maxTimeMS ile işlemin süre sınırını, writeConcern ile onay düzeyini ve collation ile metin karşılaştırma kurallarını belirleyebilirsiniz. Süre sınırı aşılırsa işlem hata fırlatır; belgelerdeki örnekte bu hata MaxTimeMSExpired kod adıyla ve 50 koduyla görünür. Filtre parametresi belge değilse işlem hata verir; parametre hiç verilmezse boş belge kabul edilir.

Sharded bir koleksiyonda findOneAndDelete() kullanırken, shard key alanı eksik belgeleri hedeflemek için belgeler _id gibi başka bir koşulla birlikte shard key alanına null eşitliği yazmayı gösterir. Sharding kavramları için sharding ve shard key yazısına bakın.

Silme seçenekleri: writeConcern, collation, hint, maxTimeMS, let

Silme seçenekleri: writeConcern, collation, hint, maxTimeMS, let: writeConcern, collation, hint, maxTimeMS

deleteOne() ve deleteMany() aynı seçenek setini kabul eder. Bunları bilmek hem performans hem de doğruluk açısından işinize yarar.

  • writeConcern: İşlemin kaç düğüm tarafından onaylanacağını belirler. Verilmezse varsayılan write concern kullanılır. Örneğin üç üyeli bir replica set'te { w: "majority", wtimeout: 100 } verilir ve onay bu sürede gelmezse MongoDB bir write concern hatası döndürür. Ayrıntılar için write concern ve read concern yazısı. Transaction içinde çalışırken işlem bazında write concern belirtmeyin.
  • collation: Metin karşılaştırmasında dil kurallarını (büyük/küçük harf, aksan) belirler. strength: 2 ile büyük/küçük harf farkı gözetmeyen bir eşleşme yapılabilir. Collation verilmezse ve koleksiyonun varsayılan collation'ı da yoksa basit ikili karşılaştırma kullanılır. Bir işlem için yalnızca tek bir collation belirtilebilir.
  • hint: Filtreyi desteklemek için kullanılacak indeksi, indeks tanım belgesiyle veya indeks adıyla belirtir. Var olmayan bir indeks verirseniz işlem hata verir.
  • maxTimeMS: Silme işleminin zaman aşımına uğramadan önce çalışabileceği süreyi milisaniye cinsinden belirler.
  • let: Filtrede kullanılacak değişkenleri tanımlar. Değişkene $$degisken biçiminde erişilir ve filtrede kullanmak için $expr operatörü gerekir.
javascript
// Türkçe kurallarla, büyük/küçük harf farkı gözetmeden eşleşen silme
db.siparisler.deleteMany(
  { durum: "iptal" },
  { collation: { locale: "tr", strength: 2 } }
)

// Belirli bir indeksi kullanmaya zorla
db.siparisler.createIndex({ durum: 1 })
db.siparisler.deleteMany({ durum: "iptal" }, { hint: { durum: 1 } })

// let ve $expr ile değişkenli filtre, 3 saniye süre sınırı
db.siparisler.deleteMany(
  { $expr: { $lt: ["$tutar", "$$altSinir"] } },
  { let: { altSinir: 50 }, maxTimeMS: 3000 }
)

Collation örneğinde locale: "tr" kullanıldı; Türkçe, MongoDB'nin desteklediği collation dilleri arasında tr koduyla yer alır. Bir silme işleminde hangi indeksin kullanıldığını sonradan görmek isterseniz $indexStats aşamasının çıktısındaki accesses.ops alanına bakabilirsiniz; bu alan indeksi kullanan işlem sayısını gösterir. Silme filtrelerini destekleyecek indeksleri planlamak için index oluşturma yazısına göz atın.

deleteMany({}) mi, drop() mu?

Bir koleksiyonu tamamen boşaltmanın iki yolu vardır ve sonuçları farklıdır. deleteMany({}) belgeleri tek tek siler, indeksleri ve koleksiyonu yerinde bırakır. drop() ise koleksiyonu ve ilişkili tüm indeksleri kaldırır; aslında drop veritabanı komutunun mongosh sarmalayıcısıdır.

ÖzellikdeleteMany({})drop()
İndekslerKalırSilinir, yeniden oluşturmanız gerekir
Dönüş değeriacknowledged, deletedCounttrue (koleksiyon yoksa da true)
OplogSilinen her belge için bir kayıtTek bir koleksiyon işlemi
KilitBelge düzeyinde yazmaKoleksiyon üzerinde işlem süresince özel kilit
Change streamHer silme için olayAçık change stream'ler için invalidate olayı
ShardingShard yapılandırması kalırYeniden oluşturulan koleksiyonu tekrar shard etmeniz gerekir
javascript
db.gecici_loglar.drop()
// çıktı: true

Belgeler, büyük bir koleksiyondaki bütün belgeleri silecekseniz koleksiyonu düşürüp yeniden oluşturmanın daha hızlı olabileceğini belirtir. Ancak bunu yapmadan önce mevcut indeksleri not etmeniz (getIndexes() ile) ve yeni koleksiyonda hepsini yeniden oluşturmanız gerekir; koleksiyon shard edilmişse yeni koleksiyonu da shard etmelisiniz. drop() işlem süresince koleksiyon üzerinde özel kilit tutar, yani sonraki tüm işlemler kilit bırakılana kadar bekler. Ayrıca koleksiyonun var olup olmadığına bakmadan true döndürdüğü için, yanlış ada yazılmış bir koleksiyon için de "başarılı" sonucu görürsünüz. Koleksiyon oluşturma ve listeleme için veritabanı ve koleksiyon oluşturma yazısına bakın.

Sharded koleksiyonlarda silme

Sharded bir kümede deleteMany() transaction dışında çalıştırılırsa ve birden fazla shard'ı hedefliyorsa, işlem kümedeki bütün shard'lara yayınlanır. Transaction içinde çalıştırıldığında yalnızca ilgili shard'lar hedeflenir. Belgeler ayrıca eşzamanlı chunk taşımaları nedeniyle deleteMany()'nin filtreye uyan bütün belgeleri silmeden tamamlanabileceği konusunda uyarır. Hepsinin silindiğinden emin olmak için üç yol önerilir: aynı filtreyle yapılan find() sorgusu sonuç döndürmeyene kadar işlemi tekrarlamak, işlemi bir transaction içinde çalıştırmak veya balancer için bir zaman penceresi tanımlayıp silmeleri o pencere dışında yapmak.

deleteOne() için durum daha basittir: yöntem doğası gereği tek belgeyle sınırlı olduğundan filtrede shard key veya _id bulunması zorunlu değildir. Tek bir shard'ı hedefliyorsanız kısmi bir shard key de kullanabilirsiniz.

Güvenli silme alışkanlıkları

Güvenli silme alışkanlıkları: Önce sayın, sonra silin, Filtreyi find ile önizleyin, Yedek alın, Süreli veriyi elle silmeyin

Silme işlemleri geri alınamadığı için birkaç alışkanlık, üretim ortamında yaşanabilecek veri kayıplarının çoğunu önler. Bunlar MongoDB'ye özgü sihirli çözümler değil, belgelerdeki davranışlardan çıkan pratik sonuçlardır.

  • Önce sayın, sonra silin: Aynı filtreyi countDocuments() ile çalıştırıp kaç belgenin etkileneceğini görün. Sayı beklediğinizden çok büyükse filtrede bir hata vardır. Sayma yöntemleri için sort, limit, skip ve sayfalama yazısına bakın.
  • Filtreyi find ile önizleyin: find(filtre).limit(5) ile örnek belgelere bakmak, alan adı yazım hatalarını ve tip uyuşmazlıklarını (sayı yerine metin gibi) yakalamanın en kolay yoludur.
  • Yedek alın: Toplu silme veya koleksiyon düşürme öncesinde mongodump ile ilgili koleksiyonun yedeğini alın. Genel yedekleme yaklaşımı için veritabanı yedekleme stratejileri yazısına bakabilirsiniz.
  • Süreli veriyi elle silmeyin: Oturum, log veya geçici kayıt gibi belirli bir süre sonra anlamını yitiren veriler için TTL indeksleri MongoDB'nin bu belgeleri kendisinin silmesini sağlar.
  • Eski remove() yöntemini kullanmayın: mongosh'ta db.collection.remove() kullanımdan kaldırılmış (deprecated) olarak işaretlidir; yerine deleteOne() veya deleteMany() kullanılmalıdır.

Sık yapılan hatalar

  • deleteOne ile "belirli" bir belgeyi silmeye çalışmak: Filtreye birden fazla belge uyuyorsa hangisinin silineceğini kontrol edemezsiniz. Ya _id ile filtreleyin ya da sıralama gerekiyorsa findOneAndDelete() ve sort kullanın.
  • Silme sonrası alanın boşaldığını sanmak: deleteMany({}) diski hemen küçültmez ve indeksleri silmez; koleksiyon ve indeks tanımları yerinde kalır.
  • drop() sonucuna güvenmek: Koleksiyon adı yanlış yazılsa bile sonuç true olur. Gerçekten silindiğini doğrulamak için koleksiyon listesine bakın.
  • Var olmayan indeks için hint vermek: hint ile belirtilen indeks yoksa işlem hiç çalışmadan hata verir; indeks adlarını getIndexes() ile kontrol edin.
  • let değişkenini doğrudan filtrede kullanmak: $$degisken yalnızca $expr içinde anlam kazanır; düz bir eşitlik filtresine yazılırsa beklediğiniz karşılaştırma yapılmaz.

Sık Sorulan Sorular

Filtreye uyan belge yoksa deleteOne hata verir mi?

Hayır. İşlem başarıyla tamamlanır ve deletedCount değeri sıfır olur. Bu durumda oplog'a da kayıt eklenmez. Uygulamanızda silinecek belgenin bulunamamasını bir hata olarak ele almak istiyorsanız bu sayıyı kontrol etmeniz gerekir.

Silinen belgeyi geri getirebilir miyim?

Silme komutlarının bir geri alma özelliği yoktur. Silinen veriyi geri almanın yolu, işlemden önce alınmış bir yedekten geri yüklemektir. Silinen belgenin içeriğine işlem anında ihtiyacınız varsa findOneAndDelete() silinen belgeyi size döndürür; bunu başka bir koleksiyona arşiv olarak yazabilirsiniz.

Toplu silme ile bulkWrite arasında ne fark var?

deleteMany() tek bir filtreyle çok sayıda belgeyi siler. Farklı filtrelerle birden fazla silme, ekleme ve güncelleme işlemini tek istekte göndermek istiyorsanız bulkWrite daha uygundur; orada deleteOne ve deleteMany işlem türleri de bulunur.

Belgeleri silmek yerine güncellemek daha mı iyi?

Bu, uygulamanızın ihtiyacına bağlıdır. Silinen kayıtları denetim veya geri getirme amacıyla saklamanız gerekiyorsa, belgeyi silmek yerine bir durum alanını güncellemek (örneğin silindi: true) yaygın bir tasarım tercihidir. Güncelleme yöntemleri için updateOne ve updateMany yazısına bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: MongoDB öğrenme rehberi · find sorguları · updateOne ve updateMany · bulkWrite toplu işlemler · TTL indeksleri · transaction kullanımı · mongodump ve mongorestore