db.collection.bulkWrite(), tek bir koleksiyon üzerinde birden fazla yazma işlemini (ekleme, güncelleme, değiştirme, silme) tek bir çağrıda göndermenizi sağlar. Varsayılan olarak işlemler sırayla çalışır ve ilk hatada durur; ordered: false verirseniz hatalı işlemler atlanır, geri kalan her şey tamamlanır. MongoDB 8.0 ve sonrasında Mongo.bulkWrite() ile farklı veritabanı ve koleksiyonlara tek istekte yazmak da mümkündür.
Toplu işlemler, binlerce küçük yazmayı ağ üzerinden tek tek göndermek yerine gruplayarak hem kodu sadeleştirir hem de gidiş dönüş sayısını azaltır. Ama sıralı ve sırasız çalışma, hata raporlama ve transaction davranışı iyi anlaşılmazsa yarım kalmış veri güncellemelerine yol açabilir. Bu yazıda her işlem türünün söz dizimini, sonuç belgesini ve hata senaryolarını inceliyoruz. Seri için MongoDB öğrenme rehberine bakın.
Söz dizimi ve desteklenen işlemler

bulkWrite() iki parametre alır: işlemlerden oluşan bir dizi ve isteğe bağlı bir seçenek belgesi. Seçenek belgesinde writeConcern ve ordered bulunur. Dizideki her eleman, işlem adını anahtar olarak taşıyan bir belgedir.
db.koleksiyon.bulkWrite(
[ <işlem 1>, <işlem 2>, ... ],
{ writeConcern: <belge>, ordered: <boolean> }
)| İşlem | Alanlar | Ne yapar |
|---|---|---|
| insertOne | document | Tek bir belge ekler |
| updateOne | filter, update, upsert, collation, arrayFilters, hint | Filtreye uyan ilk belgeyi günceller |
| updateMany | filter, update, upsert, collation, arrayFilters, hint | Filtreye uyan tüm belgeleri günceller |
| replaceOne | filter, replacement, upsert, collation, hint | Filtreye uyan ilk belgeyi tamamen değiştirir |
| deleteOne | filter, collation | Filtreye uyan ilk belgeyi siler |
| deleteMany | filter, collation | Filtreye uyan tüm belgeleri siler |
Güncelleme işlemlerinde update alanı ya yalnızca güncelleme operatörleri içeren bir belge ($set, $inc gibi) ya da bir aggregation pipeline olabilir. replaceOne işlemindeki replacement belgesi ise güncelleme operatörü içeremez; düz bir belge olmalıdır. upsert varsayılan olarak false'tur. hint ile var olmayan bir indeks belirtirseniz işlem hata verir. Bu işlemlerin tek başına kullanımı için insertOne ve insertMany, updateOne ve updateMany ve deleteOne ve deleteMany yazılarına bakabilirsiniz.
Adım adım bir örnek
Küçük bir stok koleksiyonu üzerinde beş işlemi tek çağrıda yapalım: iki ürün ekleyelim, birinin adedini azaltalım, adedi sıfır olanı silelim ve bir belgeyi tamamen değiştirelim.
db.stok.insertMany([
{ _id: 1, urun: "kalem", adet: 10 },
{ _id: 2, urun: "defter", adet: 0 },
{ _id: 3, urun: "silgi", adet: 5 }
])
try {
db.stok.bulkWrite([
{ insertOne: { document: { _id: 4, urun: "cetvel", adet: 7 } } },
{ insertOne: { document: { _id: 5, urun: "boya", adet: 3 } } },
{ updateOne: { filter: { urun: "kalem" }, update: { $inc: { adet: -2 } } } },
{ deleteOne: { filter: { adet: 0 } } },
{ replaceOne: {
filter: { urun: "silgi" },
replacement: { urun: "silgi", adet: 20, renk: "beyaz" }
} }
])
} catch (hata) {
print(hata)
}
// çıktı:
// {
// acknowledged: true,
// insertedCount: 2,
// insertedIds: { '0': 4, '1': 5 },
// matchedCount: 2,
// modifiedCount: 2,
// deletedCount: 1,
// upsertedCount: 0,
// upsertedIds: {}
// }Sonuç belgesi her işlem türü için ayrı sayaçlar içerir. insertedIds ve upsertedIds alanlarındaki anahtarlar, işlemin dizideki sırasını (index) gösterir; burada 0 ve 1 numaralı işlemler _id değeri 4 ve 5 olan belgeleri eklemiştir. matchedCount ve modifiedCount güncelleme ile değiştirme işlemlerinin toplamıdır. acknowledged yine işlemin bir write concern ile çalışıp çalışmadığını gösterir. Örnekteki try/catch bloğu önemlidir, çünkü hata durumunda bulkWrite() bir istisna fırlatır.
Belgede _id yoksa, eklemeden veya upsert yapmadan önce mongod bir ObjectId üretip _id alanını ekler; çoğu sürücü bunu zaten istemci tarafında yapar. _id verdiyseniz değerin koleksiyonda benzersiz olması gerekir, aksi halde duplicate key hatası alırsınız. Güncelleme ve değiştirme işlemleri, belgenin mevcut _id değerinden farklı bir _id belirtemez.
ordered: true ve ordered: false
ordered seçeneği toplu işlemin en kritik ayarıdır. Varsayılan değer true'dur: işlemler dizideki sırayla, seri olarak çalışır ve bir işlem hata verirse o işlem ve sonrasındakiler çalıştırılmaz. Hatadan önceki işlemler ise tamamlanmış olarak kalır; yani sıralı modda bile işlem "ya hepsi ya hiçbiri" değildir.
ordered: false ile işlemler herhangi bir sırada, hatta paralel çalışabilir (paralellik garanti edilmez). Hata veren işlemler tamamlanmaz ama hatasız olan bütün işlemler tamamlanır. Sıranın önemli olmadığı toplu veri yüklemelerinde bu mod daha uygundur; belgelere göre sırasız işlemler özellikle sharded koleksiyonlarda genellikle daha hızlıdır, çünkü sıralı listede her işlem bir öncekinin bitmesini beklemek zorundadır.
// Aynı _id iki kez: ikinci insertOne duplicate key hatası verir
try {
db.stok.bulkWrite([
{ insertOne: { document: { _id: 6, urun: "makas", adet: 4 } } },
{ insertOne: { document: { _id: 6, urun: "yapıştırıcı", adet: 9 } } },
{ updateOne: { filter: { urun: "boya" }, update: { $set: { adet: 8 } } } }
], { ordered: false })
} catch (hata) {
print(hata)
}Bu örnekte ordered: false olduğu için ilk ekleme ve updateOne tamamlanır, yalnızca ikinci ekleme başarısız olur. Aynı dizi varsayılan sıralı modda çalışsaydı ilk ekleme yapılır, ikinci ekleme hata verir ve updateOne hiç çalıştırılmazdı. Hata çıktısında writeErrors dizisi, hatalı işlemin dizideki sırasını (index: 1), hata kodunu (11000) ve mesajı gösterir. Bu hatanın nedenleri ve çözümü için E11000 duplicate key hatası yazısına bakın.
| Davranış | ordered: true (varsayılan) | ordered: false |
|---|---|---|
| Çalışma sırası | Dizideki sırayla, seri | Herhangi bir sırada, paralel olabilir |
| Hata olunca | Hatalı işlem ve sonrası çalışmaz | Yalnızca hatalı işlemler atlanır |
| Hatadan önceki işlemler | Tamamlanmış kalır | Tamamlanmış kalır |
| Hata raporu | Karşılaşılan tek hata | Her hata bir dizi elemanı olarak |
| Uygun senaryo | Sıranın önemli olduğu bağımlı işlemler | Bağımsız işlemler, toplu veri yükleme |
Hata yönetimi: writeErrors ve writeConcernErrors

bulkWrite() hata durumunda bir BulkWriteError istisnası fırlatır. Hatalar iki gruba ayrılır: write concern hataları writeConcernErrors alanında, diğer bütün hatalar writeErrors alanında görünür. Hata oluştuğunda sonuçta eklenen _id değerleri yerine başarılı yazma işlemlerinin sayıları gösterilir (nInserted, nMatched, nModified, nRemoved gibi alanlar).
Write concern hataları özel bir durumdur: sıralı işlemlerin hatada durması kuralı write concern hataları için geçerli değildir. Örneğin { w: "majority", wtimeout: 100 } ile çalışan bir toplu işlemde çoğunluk onayı 100 milisaniyede gelmezse, işlemler yapılmış olabilir ama sonuçta bir write concern hatası ve tamamlanan işlemlerin özeti görünür. Belgelerdeki örnekte bu hata WriteConcernTimeout kod adıyla ve 64 koduyla raporlanır. Write concern kavramı için write concern ve read concern yazısına bakın.
Koleksiyonunuzda şema doğrulama varsa ve validationAction değeri error ise, geçersiz bir belge eklemek veya bir belgeyi geçersiz değerlerle güncellemek hata verir. Dizide geçersiz işlemden önce gelen işlemler yürütülmüş ve koleksiyona yazılmış olur; kalan işlemlerin çalışıp çalışmayacağını yine ordered belirler.
Upsert ve arrayFilters ile güncelleme
Toplu işlemler, "varsa güncelle, yoksa ekle" mantığını çok sayıda kayıt için uygulamanın pratik bir yoludur. Her updateOne işlemine upsert: true verdiğinizde, filtreye uyan belge yoksa yeni bir belge oluşturulur ve sonuçtaki upsertedCount ile upsertedIds alanları bunu gösterir. Dizi elemanlarını hedefleyen güncellemelerde arrayFilters alanı da kullanılabilir; ayrıntılar için dizi güncelleme yazısına bakın.
// Dış sistemden gelen fiyat listesini uygula: varsa güncelle, yoksa ekle
const fiyatlar = [ { kod: "A1", fiyat: 50 }, { kod: "B7", fiyat: 120 } ]
db.urunler.bulkWrite(
fiyatlar.map(f => ({
updateOne: {
filter: { kod: f.kod },
update: { $set: { fiyat: f.fiyat } },
upsert: true
}
})),
{ ordered: false }
)Grup boyutu sınırı: maxWriteBatchSize
Toplu işlemler sunucuya gruplar halinde gönderilir ve her gruptaki işlem sayısı veritabanının maxWriteBatchSize değerini aşamaz. Belgelere göre varsayılan değer 100.000'dir ve hello.maxWriteBatchSize alanında görülebilir. Bir grup bu sınırı aşarsa istemci sürücüsü grubu sınırı aşmayan daha küçük gruplara böler; örneğin 200.000 işlemlik bir kuyruk, her biri 100.000 işlem içeren iki gruba ayrılır. Bu bölme yalnızca üst düzey API'de yapılır; db.runCommand() ile doğrudan komut gönderiyorsanız sınırı aşan bir grup hataya yol açar.
Hata raporları da sınırlıdır: tek bir grubun hata raporu çok büyürse MongoDB kalan hata mesajlarını boş metne kısaltır. Toplam boyutu 1 MB'ı aşan en az iki hata mesajı varsa kısaltma uygulanır. Belgeler bu boyutların ve gruplama mekaniğinin iç performans ayrıntıları olduğunu ve ileride değişebileceğini de belirtir. Pratik sonuç şudur: çok büyük toplu işlemlerde hataları ayrıntılı incelemek istiyorsanız işi daha küçük parçalara bölün.
Transaction içinde bulkWrite
bulkWrite() dağıtık transaction'lar içinde kullanılabilir. Transaction içinde davranış önemli ölçüde değişir: toplu işlemdeki ilk hata, işlem sırasız (ordered: false) olsa bile toplu işlemin tamamının başarısız olmasına ve transaction'ın iptal edilmesine yol açar. Ayrıca transaction içinde var olmayan bir koleksiyona ekleme veya upsert: true ile güncelleme yapılırsa koleksiyon örtük olarak oluşturulur. Transaction içinde işlem bazında write concern belirtmemelisiniz.
Gerçek bir "ya hepsi ya hiçbiri" davranışına ihtiyacınız varsa doğru araç budur; ancak belgeler transaction'ların tek belge yazmalarına göre çoğu durumda daha yüksek performans maliyeti taşıdığını ve iyi bir şema tasarımının yerini tutmadığını hatırlatır. Ayrıntılar için transaction kullanımı yazısına bakın.
Mongo.bulkWrite: birden fazla koleksiyona tek istekte yazmak

MongoDB 8.0 veya sonrasını gerektiren Mongo.bulkWrite(), işlemleri koleksiyon yerine bağlantı (Mongo örneği) üzerinden çalıştırır. Her işlem belgesi hedefini namespace alanında "veritabani.koleksiyon" biçiminde, işlem türünü name alanında taşır. Böylece farklı veritabanlarındaki koleksiyonlara tek istekte yazabilirsiniz.
db.getMongo().bulkWrite(
[
{ namespace: "magaza.stok", name: "updateOne",
filter: { urun: "kalem" }, update: { $inc: { adet: -1 } } },
{ namespace: "magaza.hareketler", name: "insertOne",
document: { urun: "kalem", tur: "satis", miktar: 1 } }
],
{ ordered: true, verboseResults: true }
)Bu çağrı önce magaza.stok koleksiyonunda kalemin adedini azaltır, ardından magaza.hareketler koleksiyonuna bir hareket kaydı ekler. Seçenekler şunlardır: ordered (varsayılan true), ayrıntılı sonuç için verboseResults (varsayılan false), belge doğrulamasını atlamak için bypassDocumentValidation (varsayılan false) ve aggregation değişkenleri için let. Sonuç belgesi acknowledged, insertedCount, matchedCount, modifiedCount, deletedCount, upsertedCount sayaçlarını ve isteğe bağlı olarak işlem bazında insertResults, updateResults, deleteResults haritalarını içerir. insertOne belgesinde _id yoksa mongosh bir tane üretir. Bu yöntemi bir transaction içinde kullanmak için Session.bulkWrite() çağrılır; aynı parametreleri kabul eder ve işlemi oturumla ilişkilendirir.
| Özellik | db.collection.bulkWrite() | Mongo.bulkWrite() |
|---|---|---|
| Hedef | Tek koleksiyon | Birden fazla veritabanı ve koleksiyon |
| İşlem yazımı | { insertOne: { document: ... } } | { namespace, name: "insertOne", document } |
| Gerekli sürüm | Belgelerde sürüm şartı yok | MongoDB 8.0 veya sonrası |
| Transaction | Oturum içinde doğrudan | Session.bulkWrite() ile |
| Ek seçenekler | writeConcern, ordered | ordered, verboseResults, bypassDocumentValidation, let |
Sık yapılan hatalar
- Sıralı modun atomik olduğunu sanmak:
ordered: truehatada durur ama önceki işlemleri geri almaz. Atomiklik gerekiyorsa transaction kullanın. - try/catch kullanmamak: Hata durumunda istisna fırlatılır; yakalanmazsa hangi işlemlerin tamamlandığını göremezsiniz.
- replaceOne içinde $set kullanmak:
replacementgüncelleme operatörü içeremez. - Capped koleksiyonlarda silme denemek: Capped koleksiyonlarda
deleteOnevedeleteManyWriteErrorfırlatır; belge boyutunu artıran güncellemeler ve orijinalden büyük değiştirmeler de aynı hatayı verir. - Transaction içinde sırasız modun hataları atlayacağını beklemek: Transaction içinde ilk hata bütün toplu işlemi başarısız kılar ve transaction'ı iptal eder.
Sık Sorulan Sorular
insertMany yerine neden bulkWrite kullanayım?
Yalnızca ekleme yapıyorsanız insertMany() yeterlidir. bulkWrite() ekleme, güncelleme, değiştirme ve silmeyi aynı çağrıda karıştırmanız gerektiğinde, örneğin bir dış sistemden gelen değişiklik listesini uygularken işe yarar.
Atlas arayüzünden bulkWrite çalıştırabilir miyim?
Hayır. Belgelere göre toplu yazma işlemleri Atlas UI üzerinden yapılamaz; mongosh veya bir sürücü kullanmanız gerekir.
Hangi işlemin başarısız olduğunu nasıl anlarım?
Fırlatılan hatadaki writeErrors dizisinin her elemanında index alanı bulunur; bu, işlemin gönderdiğiniz dizideki sırasıdır. Aynı alanda hata kodu ve hatalı işlemin kendisi (op) de yer alır.
Toplu işlemde kaç işlem gönderebilirim?
Tek bir çağrıda gönderebileceğiniz işlem sayısını sürücü gruplara böler; her grup varsayılan olarak en fazla 100.000 işlem içerir. Ancak çok büyük çağrılarda hata raporları kısaltılabileceği için işi makul boyutlu parçalara bölmek, hataları izlemeyi kolaylaştırır.
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 · insertOne ve insertMany · updateOne ve updateMany · deleteOne ve deleteMany · transaction kullanımı · E11000 hatası