MongoDB'de var olan belgeleri değiştirmek için üç yöntem kullanılır: updateOne() filtreye uyan ilk belgeyi, updateMany() uyan tüm belgeleri günceller; replaceOne() ise ilk eşleşen belgenin _id dışındaki bütün içeriğini yeni bir belgeyle değiştirir. Güncelleme belgesinde $set, $inc, $unset, $rename gibi güncelleme operatörleri kullanılır; upsert: true seçeneği ise eşleşme yoksa yeni belge ekler.
Bu yazıda yöntemlerin sözdizimini, döndürdükleri sonuç belgesini, temel alan operatörlerinin davranışını, upsert kurallarını ve atomiklik gibi üretimde önemli olan noktaları MongoDB 9.0 belgelerine dayanarak anlatıyoruz. Dizi elemanlarını değiştiren $push, $pull ve konumsal operatörler dizi güncelleme yazısında ayrıca ele alınıyor.
updateOne, updateMany ve replaceOne söz dizimi

Üç yöntem de ilk parametre olarak find() ile aynı sorgu operatörlerini kabul eden bir filtre alır. İkinci parametre updateOne() ve updateMany() için yalnızca güncelleme operatörü ifadeleri içeren bir belge ya da bir aggregation boru hattıdır; replaceOne() için ise yalnızca alan-değer çiftlerinden oluşan bir yedek belgedir. Üçüncü parametre seçenekler belgesidir: upsert, writeConcern, collation, hint, sort ve yönteme göre arrayFilters, let, maxTimeMS.
db.collection.updateOne(
<filter>,
<update>,
{ upsert: <boolean>, writeConcern: <document>, collation: <document>,
arrayFilters: [ ... ], hint: <document|string>, let: <document>,
sort: <document>, maxTimeMS: <int> }
)| Yöntem | Kaç belge | İkinci parametre |
|---|---|---|
| updateOne() | İlk eşleşen | Güncelleme operatörleri veya pipeline |
| updateMany() | Tüm eşleşenler | Güncelleme operatörleri veya pipeline |
| replaceOne() | İlk eşleşen | Yalnızca alan-değer çiftleri (operatör yok) |
updateOne() filtre için boş belge ({}) alırsa koleksiyonda dönen ilk belgeyi günceller. Birden çok belge eşleştiğinde hangisinin güncelleneceğini belirlemek için sort seçeneği vardır; belge bu seçeneğin 8.0 sürümünde eklendiğini belirtir. Bu seçenek verildiğinde updateOne() sıralamaya göre ilk gelen belgeyi günceller.
Dönen sonuç belgesi
updateOne() ve updateMany() bir sonuç belgesi döndürür: matchedCount filtreye uyan belge sayısı, modifiedCount gerçekten değişen belge sayısı, upsertedId upsert ile eklenen belgenin _id değeri, upsertedCount eklenen belge sayısıdır. acknowledged ise işlem bir write concern ile çalıştıysa true, write concern kapatıldıysa false olur.
db.restoran.updateOne(
{ ad: "Central Perk Cafe" },
{ $set: { ihlal: 3 } }
)
// çıktı: { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
// Eşleşme yoksa
// çıktı: { "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }matchedCount ile modifiedCount arasındaki fark önemlidir. Bir alanı zaten sahip olduğu değere ayarlarsanız belge eşleşir ama değişmez; bu durumda modifiedCount, matchedCount'tan küçük olur. Uygulama kodunda "güncelleme başarılı mı?" sorusunu yanıtlarken hangi sayıya baktığınızı bilinçli seçin: kaydın var olup olmadığını matchedCount, gerçekten değişiklik olup olmadığını modifiedCount söyler.
$set: alan atamak ve oluşturmak
$set bir alanın değerini verilen değerle değiştirir. Alan yoksa, tür kısıtını ihlal etmediği sürece yeni alan ekler. Nokta gösterimiyle var olmayan iç içe bir yol verirseniz, yolu tamamlamak için gereken gömülü belgeleri de oluşturur. Belge, $set'in tüm belgeyi yeniden yazmaya göre avantajlarını da sıralar: yalnızca belirtilen alanları değiştirir, oplog'a yalnızca değişen alanları yazdığı için replikasyonu hafifletir ve uygulamanın değişen alanları kendisinin hesaplamasına gerek bırakmaz.
// Birden çok alan ve iç içe yol
db.urunler.updateOne(
{ sku: "KTP-001" },
{ $set: { fiyat: 45, "boyut.birim": "cm", "etiket.renk": "mavi" } }
)
// etiket alanı yoksa { renk: "mavi" } gömülü belgesiyle birlikte oluşturulurGüncelleme belgesinde birden çok operatörü bir arada kullanabilirsiniz: { $set: { durum: "D" }, $inc: { adet: 2 } }. Belgeye göre MongoDB 5.0'dan itibaren güncelleme operatörleri adı metin olan alanları sözlük sırasıyla, adı sayı olan alanları ise sayısal sırayla işler. Ayrıca boş bir operand ile (örneğin { $set: {} }) gönderilen güncelleme artık hata vermez; hiçbir değişiklik yapmaz ve oplog kaydı oluşturmaz.
$inc: sayaçları atomik artırmak
$inc bir alanı verilen miktar kadar artırır; negatif değer azaltır. Alan yoksa oluşturur ve değerini verilen miktara ayarlar. Değeri null olan bir alanda $inc kullanmak hata üretir. $inc tek belge içinde atomik bir işlemdir; bu yüzden stok azaltma, görüntülenme sayacı gibi işlemlerde önce okuyup sonra yazmak yerine doğrudan $inc kullanmak eşzamanlı isteklerde kayıp güncellemeleri önler.
db.urunler.insertOne( { _id: 1, sku: "abc123", quantity: 10, metrics: { orders: 2, ratings: 3.5 } } )
db.urunler.updateOne(
{ sku: "abc123" },
{ $inc: { quantity: -2, "metrics.orders": 1 } }
)
// Belgeye göre güncellenmiş belge:
// { _id: 1, sku: 'abc123', quantity: 8, metrics: { orders: 3, ratings: 3.5 } }Stoğun eksiye düşmesini engellemek istiyorsanız koşulu filtreye koyun: { sku: "abc123", quantity: { $gte: 2 } }. Filtre ve güncelleme aynı belge üzerinde atomik çalıştığı için, stok yetersizse matchedCount 0 döner ve uygulama bunu "stok yok" olarak yorumlayabilir. Benzer amaçlarla $min, $max, $mul ve $currentDate operatörleri de vardır.
Alan güncelleme operatörleri özeti
Resmî güncelleme operatörleri sayfası alan düzeyindeki operatörleri aşağıdaki gibi listeler. $min ve $max özellikle "en düşük fiyatı sakla" ya da "en yüksek skoru tut" gibi karşılaştırmalı güncellemelerde işe yarar, çünkü değer yalnızca koşul sağlanırsa yazılır ve ayrı bir okuma gerekmez.
| Operatör | Ne yapar |
|---|---|
| $set | Alanın değerini atar, yoksa oluşturur |
| $inc | Alanı verilen miktar kadar artırır veya azaltır |
| $mul | Alanı verilen miktarla çarpar |
| $min | Verilen değer mevcut değerden küçükse günceller |
| $max | Verilen değer mevcut değerden büyükse günceller |
| $currentDate | Alanı geçerli tarihe (Date veya Timestamp) ayarlar |
| $rename | Alanın adını değiştirir |
| $unset | Alanı belgeden siler |
| $setOnInsert | Yalnızca upsert ekleme yaptığında değer atar |
$unset ve $rename

$unset bir alanı belgeden siler. Sözdiziminde verilen değer (genellikle "") işlemi etkilemez; alan yoksa işlem hiçbir şey yapmaz. Dizi elemanında konumsal $ ile kullanıldığında ise elemanı silmez, null yapar; dizi boyu ve eleman konumları korunur.
db.musteriler.updateMany( { }, { $unset: { eskiAlan: "", "adres.faks": "" } } )$rename bir alanın adını değiştirir. Belgeye göre işlem içeride önce eski alanı ve yeni addaki alanı $unset ile siler, sonra eski alanın değeriyle yeni adı $set eder. Bunun sonuçları şunlardır: belgede yeni adla zaten bir alan varsa o alan silinir; eski alan yoksa işlem hiçbir şey yapmaz; alan sırası korunmayabilir. $rename gömülü belgelerin içine ve dışına alan taşıyabilir ama dizilerin içindeki gömülü belgelerde çalışmaz. Yeni ad eski adla aynı olamaz.
// nickname alanını alias, cell alanını mobile olarak yeniden adlandır
db.ogrenciler.updateOne( { _id: 1 }, { $rename: { nickname: "alias", cell: "mobile" } } )
// Yazım hatalı alan adını tüm koleksiyonda düzelt
db.ogrenciler.updateMany( { nmae: { $exists: true } }, { $rename: { nmae: "name" } } )replaceOne: belgeyi bütünüyle değiştirmek
replaceOne(), ilk eşleşen belgenin _id dışındaki tüm içeriğini ikinci parametredeki belgeyle değiştirir. Yedek belge yalnızca alan-değer çiftlerinden oluşmalıdır; güncelleme operatörü içeremez. Yedek belgedeki alanlar eskisinden farklı olabilir. _id değiştirilemez olduğu için yedek belgede bu alanı yazmanıza gerek yoktur; yazarsanız değeri mevcut değerle aynı olmalıdır.
db.restoran.replaceOne(
{ ad: "Central Perk Cafe" },
{ ad: "Central Pork Cafe", semt: "Manhattan" }
)
// çıktı: { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }upsert: yoksa ekle
upsert: true verildiğinde, filtreye uyan belge varsa güncellenir; yoksa yeni bir belge eklenir. Güncelleme operatörleriyle yapılan upsert'te yeni belge, filtredeki eşitlik koşullarından bir taban belge oluşturularak ve ardından güncelleme ifadeleri uygulanarak kurulur. Filtredeki karşılaştırma koşulları ($gt gibi) yeni belgeye girmez. Belgede _id yoksa MongoDB bir ObjectId ekler.
db.restoran.updateOne(
{ ad: "Pizza Rat's Pizzaria" },
{ $set: { _id: 4, ihlal: 7, semt: "Manhattan" } },
{ upsert: true }
)
// çıktı: {
// "acknowledged" : true,
// "matchedCount" : 0,
// "modifiedCount" : 0,
// "upsertedId" : 4,
// "upsertedCount": 1
// }Bu örnekte yeni belgenin ad alanı filtreden, diğer alanları $set'ten gelir. Yalnızca ekleme sırasında yazılması gereken alanlar (oluşturulma tarihi, varsayılan değerler) için $setOnInsert kullanılır: işlem ekleme ile sonuçlanırsa bu alanlar atanır, var olan bir belge güncellenirse $setOnInsert hiçbir şey yapmaz.
db.urunler.updateOne(
{ _id: 1 },
{ $set: { item: "apple" }, $setOnInsert: { defaultQty: 100 } },
{ upsert: true }
)
// Koleksiyon boşsa eklenen belge: { "_id" : 1, "item" : "apple", "defaultQty" : 100 }
// Belge zaten varsa yalnızca $set uygulanırEşzamanlı upsert ve yinelenen belgeler
Belge önemli bir uyarı yapar: aynı filtreyle neredeyse aynı anda birden çok upsert gönderilirse ve hepsi sorgu aşamasını herhangi biri ekleme yapmadan önce tamamlarsa, her biri ayrı bir belge ekleyebilir. Bunu önlemenin yolu filtre alanlarında unique indeks bulunmasıdır. Unique indeks varken yalnızca bir işlem ekleme yapar; diğerleri ya yeni eklenen belgeyi günceller ya da benzersiz anahtar çakışmasıyla başarısız olur. Bu çakışma hatasının ayrıntıları E11000 duplicate key hatası yazısında, unique indeks oluşturma unique ve partial indeks yazısında anlatılıyor.
Atomiklik, updateMany ve idempotent güncellemeler
MongoDB'de tüm yazma işlemleri belge düzeyinde atomiktir. updateMany() her belgeyi ayrı ayrı günceller; her belge yazımı atomiktir ama işlemin bütünü atomik değildir. Bir belgenin güncellenmesi başarısız olursa, ondan önce yazılmış güncellemeler kalır, kalan belgeler güncellenmez. Birden çok belge üzerinde ya hep ya hiç davranışı gerekiyorsa transaction kullanın.
Bu nedenle belge, updateMany()'nin yalnızca idempotent işlemler için kullanılmasını önerir: aynı komutu iki kez çalıştırmak sonucu değiştirmemelidir. Örneğin "puanı 3'ün altında olanlara 0.5 ekle ve puanArtti: true yap" komutu, filtreye puanArtti alanının olmadığı koşulunu eklerseniz güvenle yeniden çalıştırılabilir; her belge yalnızca bir kez artırılır.
db.filmler.updateMany(
{ puan: { $lt: 3.0 }, puanArtti: { $exists: false } },
{ $inc: { puan: 0.5 }, $set: { puanArtti: true } }
)Diğer davranış notları: _id alanı bir kez atandıktan sonra güncellenemez. Yazma işlemleri alan sırasını korur; istisnalar _id'nin her zaman ilk alan olması ve $rename içeren güncellemelerin sırayı değiştirebilmesidir. updateOne() başarılı olursa oplog'a bir kayıt yazılır; başarısız olursa ya da güncellenecek belge bulunamazsa yazılmaz. Onay düzeyini write concern belirler. Çok sayıda farklı güncellemeyi tek istekte göndermek için bulkWrite kullanabilirsiniz.
Aggregation boru hattıyla güncelleme
Bir alanı başka bir alanın değerine göre hesaplamak ya da koşullu güncelleme yapmak gerektiğinde, ikinci parametre olarak bir dizi içinde aggregation aşamaları verilebilir. Belgeye göre bu boru hattında yalnızca $addFields ve takma adı $set, $project ve takma adı $unset, $replaceRoot ve takma adı $replaceWith aşamaları kullanılabilir. Buradaki $set güncelleme operatörü değil aggregation aşamasıdır.
db.ogrenciler.updateOne(
{ _id: 1 },
[
{ $set: { yorumlar: [ "$yorum1", "$yorum2" ], sonGuncelleme: "$$NOW" } },
{ $unset: [ "yorum1", "yorum2" ] }
]
)Aşamaların ayrıntıları için $addFields, $set ve $project, boru hattı kavramı için aggregation pipeline yazılarına bakın.
Uygulamalı mini örnek: stok ve sayaç
Bir e-ticaret sepetinde ödeme anında stok düşmek, satış sayacını artırmak ve son satış tarihini yazmak istiyoruz. Tek bir updateOne() çağrısı bu üç değişikliği aynı belgede atomik olarak yapar. Filtredeki stok: { $gte: 2 } koşulu, stok yetersizse hiçbir şeyin değişmemesini sağlar.
const sonuc = db.urunler.updateOne(
{ sku: "KTP-001", stok: { $gte: 2 } },
{ $inc: { stok: -2, satis: 2 }, $currentDate: { sonSatis: true } }
)
if ( sonuc.matchedCount === 0 ) {
print( "Stok yetersiz veya ürün yok" )
}Ürün kataloğunu dış bir kaynaktan eşitlerken ise upsert kullanırız: ürün varsa fiyatı güncellenir, yoksa oluşturulma tarihiyle birlikte eklenir. sku alanında unique indeks bulunması, eşzamanlı eşitleme işlerinde yinelenen ürün oluşmasını engeller.
db.urunler.createIndex( { sku: 1 }, { unique: true } )
db.urunler.updateOne(
{ sku: "KTP-002" },
{ $set: { fiyat: 120 }, $setOnInsert: { olusturma: new Date(), stok: 0 } },
{ upsert: true }
)Sık yapılan hatalar

- Güncelleme operatörü olmadan alan vermek:
updateOne()ikinci parametrede yalnızca operatör ifadeleri (veya pipeline) kabul eder; tam belge değiştirmek içinreplaceOne()vardır. - Kısmi değişiklik için replaceOne() kullanmak: yazılmayan alanlar silinir.
- Oku-değiştir-yaz yapmak: eşzamanlı isteklerde güncelleme kaybolur;
$incve filtre koşulu kullanın. - Unique indeks olmadan upsert: eşzamanlı isteklerde yinelenen belgeler oluşabilir.
- null alanda $inc: hata üretir; alanı önce sayı yapın ya da
$setOnInsertile başlangıç değeri verin. - updateMany()'yi idempotent olmayan biçimde yazmak: yarıda kalan bir işlemi tekrar çalıştırmak bazı belgeleri iki kez değiştirir.
- _id'yi değiştirmeye çalışmak:
_iddeğiştirilemez; yeni belge ekleyip eskisini silmeniz gerekir.
Sık Sorulan Sorular
Güncellenen belgeyi sonuç olarak nasıl alırım?
updateOne() yalnızca sayıları döndürür. Güncellenmiş belgenin kendisine ihtiyacınız varsa findOneAndUpdate() yöntemi kullanılır; bu yöntem de upsert seçeneğini destekler.
modifiedCount neden 0 geliyor?
Filtre bir belgeyi eşleştirmiş ama belgede değişiklik olmamış olabilir; örneğin alan zaten aynı değere sahiptir. matchedCount değerine bakarak belgenin bulunup bulunmadığını ayırt edebilirsiniz.
Sharded koleksiyonda upsert için shard key gerekir mi?
Belgeye göre MongoDB 8.0'dan itibaren updateOne() ve replaceOne() ile upsert: true kullanırken filtrede tam shard key bulunması gerekmez. updateMany() sayfası ise sharded koleksiyonda upsert için filtrede tam shard key gerektiğini belirtir. Sharding temelleri sharding ve shard key yazısında.
Dizi elemanlarını nasıl güncellerim?
Dizilere eleman eklemek, çıkarmak veya belirli elemanları değiştirmek için $push, $pull, $, $[] ve arrayFilters kullanılır; hepsi dizi güncelleme yazısında.
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 · Dizi güncelleme · find sorguları · E11000 duplicate key hatası · bulkWrite toplu işlemler · Transaction kullanımı