MongoDB'de bir indexin davranışını, oluştururken verdiğiniz seçenekler belirler: unique: true yinelenen değerleri reddeder, partialFilterExpression yalnızca bir filtreye uyan belgeleri indexler, sparse: true alanı olmayan belgeleri atlar, hidden: true indexi sorgu planlayıcıdan gizler, collation ise büyük-küçük harf duyarsız karşılaştırma sağlar. Belgelerin açık önerisi, sparse yerine daha esnek olan partial indexi kullanmaktır.
Bu yazıda MongoDB 9.0 belgelerinin "index properties" başlığı altında topladığı bu özellikleri tek tek, sınırları ve sık düşülen tuzaklarıyla birlikte anlatıyoruz. Index oluşturmanın temelleri için MongoDB index oluşturma, otomatik silme yapan TTL özelliği için TTL index yazısına bakabilirsiniz.
| Özellik | Seçenek | Ne zaman kullanılır |
|---|---|---|
| Unique | unique: true | E-posta, kullanıcı adı gibi benzersiz olması gereken alanlar |
| Partial | partialFilterExpression | Sorgular hep belirli bir alt kümeye bakıyorsa |
| Sparse | sparse: true | Alanın varlığına göre indexleme (partial ile de yapılabilir) |
| Hidden | hidden: true | Silmeden önce indexin etkisini ölçmek |
| Büyük-küçük harf duyarsız | collation ile strength 1 veya 2 | Harf büyüklüğünden bağımsız eşitlik aramaları |
Unique index

Unique index, indexlenen alanda aynı değerin en fazla bir kez bulunmasını sağlar. Compound bir unique index ise tek tek alanları değil, değerlerin birleşimini benzersiz tutar. MongoDB her koleksiyonda _id alanı için zaten unique bir index kurar. Kendi alanlarınız için unique seçeneğini kullanırsınız:
// Tek alan: her e-posta yalnızca bir kez
db.uyeler.createIndex( { eposta: 1 }, { unique: true } )
// Birleşim: aynı grupta aynı ad-soyad ikilisi bir kez
db.uyeler.createIndex(
{ grupNo: 1, soyad: 1, ad: 1 },
{ unique: true }
)Kural ihlal edildiğinde ekleme veya güncelleme E11000 duplicate key error ile reddedilir. Hatanın nedenleri ve çözüm yolları E11000 duplicate key hatası yazısının konusu; burada indexin kendi davranışına odaklanıyoruz.
Eksik alan ve null davranışı
En sık şaşırtan ayrıntı şudur: tek alanlı unique bir indexte alanı hiç olmayan ya da değeri null olan belge için index null değeri saklar. Unique kural gereği bu yüzden koleksiyonda yalnızca bir belge alanı eksik olabilir. İkinci bir eksik belge eklemek yinelenen anahtar hatasıyla sonuçlanır; mevcut veride birden çok eksik belge varsa index oluşturma da aynı hatayla başarısız olur.
Alanı zorunlu olmayan bir veride benzersizlik istiyorsanız iki seçenek vardır: sparse ve unique seçeneklerini birlikte kullanmak ya da daha esnek olan partial ve unique birleşimine geçmek. Her iki durumda da kural yalnızca alanı taşıyan (veya filtreye uyan) belgeler için uygulanır.
Diziler ve unique
Kısıt ayrı belgeler arasında geçerlidir. Unique bir multikey indexte bir belgenin kendi dizisinde aynı değer birden çok kez geçebilir; belge başka bir belgeyle çakışmadığı sürece bu sorun değildir ve tekrar eden değer indexe yalnızca bir kez yazılır. Örneğin { "depolar.kod": 1, "depolar.adet": 1 } üzerinde unique bir index varken, belgeler birleşimler farklı olduğu sürece aynı depo koduna sahip dizi elemanları içerebilir.
Sınırlar
- Koleksiyonda kuralı ihlal eden veri varsa MongoDB unique indexi oluşturamaz.
- Hashed indexlerde unique kısıtı kullanılamaz.
- Sharded cluster ortamında unique kural, shardlar arasında yalnızca index shard key üzerindeyse veya shard key'i önek olarak içeriyorsa uygulanır.
- Replica set veya sharded cluster'da yuvarlanan (rolling) yöntemle unique index kurmak, işlem boyunca koleksiyona yazmaların durdurulmasını gerektirir; yazmaları durduramıyorsanız indexi replica set'te primary üzerinde, sharded cluster'da
mongosüzerinden oluşturun.
Var olan bir indexi unique yapmak
Normal bir indexi silmeden unique hale getirmek için belgeler collMod komutunu ve iki aşamalı bir yol önerir. Önce prepareUnique: true ile index hazırlanır; bu andan itibaren yeni yinelenen değerler reddedilir. Sonra dryRun: true ile çakışan belgeler listelenir, temizlenir ve dönüşüm tamamlanır. prepareUnique ayrı bir collMod çağrısında verilmelidir, başka index değişiklikleriyle birleştirilemez ve time series koleksiyonlarında kullanılamaz.
// 1. Hazırlık: bundan sonra yinelenen değer eklenemez
db.runCommand( {
collMod: "uyeler",
index: { keyPattern: { telefon: 1 }, prepareUnique: true }
} )
// 2. Deneme: çakışan belge var mı?
db.runCommand( {
collMod: "uyeler",
index: { keyPattern: { telefon: 1 }, unique: true },
dryRun: true
} )
// çakışma kalmadıysa çıktı: { ok: 1 }
// 3. Dönüşümü tamamla
db.runCommand( {
collMod: "uyeler",
index: { keyPattern: { telefon: 1 }, unique: true }
} )
// çıktı: { unique_new: true, ok: 1 }Deneme adımında çakışma varsa komut, "Cannot convert the index to unique" ifadesiyle başlayan bir hata ve çakışan belgelerin _id değerlerini döndürür. Belgelere göre bu liste 8 MB'ı aşarsa ayrıntı yerine çok fazla çakışma bulunduğunu söyleyen kısa bir mesaj gelir; o durumda bir kısmını temizleyip komutu yeniden çalıştırırsınız. Sharded koleksiyonda herhangi bir shardda yinelenen anahtar varsa komut CannotConvertIndexToUnique döndürür ve hiçbir shardda dönüşüm yapılmaz. Şema değişikliği niteliğindeki bu işlemden önce yedek almanızı öneririz; yedekleme araçları için mongodump ve mongorestore yazısına bakın.
Partial index
Partial index, koleksiyondaki belgelerin yalnızca belirttiğiniz filtreye uyanlarını indexler. Daha az belge indexlendiği için hem disk ihtiyacı hem de oluşturma ve bakım maliyeti düşer. Filtre partialFilterExpression seçeneğiyle verilir ve belgelere göre yalnızca şu ifadeleri içerebilir: eşitlik (alan: değer veya $eq), $exists: true, $gt, $gte, $lt, $lte, $type, $and, $or, $in, $geoWithin ve $geoIntersects.
// Yalnızca aktif siparişleri indexle
db.siparisler.createIndex(
{ musteriId: 1, tarih: -1 },
{ partialFilterExpression: { durum: "aktif" } }
)Sorgunun partial indexi kullanma şartı
MongoDB, eksik sonuç döndüreceği durumda partial indexi kullanmaz. Indexin seçilebilmesi için sorgu koşulunun index filtresini (ya da filtrenin daha dar bir alt kümesini) içermesi gerekir. Yukarıdaki index için { musteriId: 7, durum: "aktif" } sorgusu indexi kullanabilir; { musteriId: 7 } sorgusu ise kullanamaz, çünkü aktif olmayan siparişler indexte yoktur ve sonuç eksik kalırdı.
Karşılaştırma operatörlerinde kural "eşit veya daha dar aralık" biçimindedir. Filtresi { yil: { $gt: 2000 } } olan bir indexi { yil: 2005 } ve { yil: { $gt: 2010 } } sorguları kullanabilir, { yil: { $gt: 1990 } } sorgusu kullanamaz. Belgeler, alt küme eşleşmesinin desteklenen her operatörde aynı biçimde işlemediğini, bu yüzden burada gösterilmeyen bir kalıba güvenmeden önce explain ile indexin gerçekten seçildiğini doğrulamanızı ister.
db.filmler.createIndex(
{ baslik: 1 },
{ partialFilterExpression: { yil: { $gt: 2000 } } }
)
db.filmler.find( { baslik: "Kış Uykusu", yil: 2014 } ) // indexi kullanabilir
db.filmler.find( { baslik: "Kış Uykusu", yil: { $gt: 2010 } } ) // indexi kullanabilir
db.filmler.find( { baslik: "Kış Uykusu", yil: { $gt: 1990 } } ) // kullanamaz
db.filmler.find( { baslik: "Kış Uykusu" } ) // kullanamazPartial ve unique birlikte
İki seçeneği birlikte verdiğinizde benzersizlik kuralı yalnızca filtreye uyan belgelere uygulanır. Belgelerdeki örnekte email alanı unique, filtre ise password: { $exists: true } şeklindedir: şifresi olan iki kullanıcı aynı e-postayı taşıyamaz, ama şifre alanı olmayan belgeler aynı e-postayla eklenebilir. Bu, örneğin "silinmemiş kayıtlar arasında benzersiz" gibi kuralları uygulamanın temiz bir yoludur.
db.hesaplar.createIndex(
{ eposta: 1 },
{ unique: true, partialFilterExpression: { sifre: { $exists: true } } }
)Partial index sınırları
partialFilterExpressionilesparseaynı indexte birlikte verilemez._idindexi ve shard key indexleri partial olamaz.- MongoDB 7.3 ve sonrasında aynı anahtarlara ve collation kullanan aynı filtre ifadelerine sahip eşdeğer partial indexler oluşturulamaz; örneğin yalnızca filtredeki harf büyüklüğüyle ayrışan iki index kurulamaz.
- Time series koleksiyonundaki bir TTL indexte filtre yalnızca
metaFieldüzerinde olabilir.
Sparse index
Sparse index yalnızca indexlenen alanı içeren belgeler için girdi tutar; alan null değerli olsa bile belge indexe girer, alanı hiç olmayan belge ise atlanır. Normal (sparse olmayan) indexler ise koleksiyonun bütün belgelerini içerir ve alanı olmayanlar için null saklar.
db.uyeler.createIndex( { sadakatKartNo: 1 }, { sparse: true } )Sparse indexin önemli bir yan etkisi var: index tüm belgeleri içermediği için, eksik sonuç üreteceği sorgu ve sıralamalarda MongoDB bu indexi kendiliğinden seçmez. Örneğin bütün üyeleri sadakatKartNo alanına göre sıralamak istediğinizde sparse index kullanılmaz; hint() ile zorlarsanız kullanılır, ama yalnızca alanı olan belgeler döner. Benzer biçimde boş filtreli bir sayımda sparse indexi hint() ile zorlamak yanlış sayı verir. Doğru toplam için o sayımda sparse indexe hint vermeyin.
Bazı index türleri her zaman sparse davranır ve sparse seçeneğini yok sayar: 2d, 2dsphere (sürüm 2), text ve wildcard indexler. Sparse ile unique birlikte kullanıldığında koleksiyon yinelenen değerleri reddeder ama alanı hiç olmayan birden çok belgeye izin verir. MongoDB 5.0 ve sonrasında aynı anahtar desenine sahip sparse ve sparse olmayan unique indexler aynı koleksiyonda birlikte bulunabilir.
| Soru | Sparse | Partial |
|---|---|---|
| Hangi belgeler indexlenir? | Alanı olanlar | Filtreye uyanlar |
| Index anahtarı dışındaki alana göre filtre? | Hayır | Evet |
| Varlık dışında koşul (aralık, eşitlik)? | Hayır | Evet |
| Belgelerin önerisi | Özel bir gereksinim yoksa partial kullanın | Tercih edilen |
Partial index, sparse indexin yaptığı her şeyi yapabilir: { ad: 1 } üzerinde partialFilterExpression: { ad: { $exists: true } } veren bir index, sparse bir ad indexiyle aynı sorguları destekler. Üstelik filtre başka alanlara da bakabilir; örneğin ad indexi yalnızca eposta alanı olan belgeleri kapsayabilir. Bu durumda planlayıcının indexi seçmesi için sorgunun hem ad koşulunu hem de eposta için null olmayan bir eşleşmeyi içermesi gerekir.
Büyük-küçük harf duyarsız index
Harf büyüklüğünden bağımsız arama için index oluştururken collation seçeneğini verirsiniz. Collation belgesindeki locale dil kurallarını, strength ise karşılaştırma düzeyini belirler. Belgelere göre strength: 1 yalnızca temel karakterleri karşılaştırır, aksan ve büyük-küçük harf farklarını yok sayar; strength: 2 aksan farklarını da hesaba katar ama büyük-küçük harfi yine yok sayar. Varsayılan düzey olan 3 harf büyüklüğünü de ayırt eder. MongoDB'nin desteklediği diller arasında Türkçe için tr yerel ayarı bulunur.
db.sehirler.createIndex(
{ ad: 1 },
{ collation: { locale: "tr", strength: 2 } }
)
// Indexi kullanmak için sorgu aynı collation'ı belirtmelidir
db.sehirler.find( { ad: "izmir" } ).collation( { locale: "tr", strength: 2 } )
// Collation belirtilmezse bu index kullanılmaz
db.sehirler.find( { ad: "izmir" } )Kural basittir: collation tanımlı bir indexi kullanmak için sorgu ve sıralama işlemleri aynı collation'ı belirtmelidir. Belgelerdeki örnekte strength: 2 ile kurulmuş bir indexe, aynı collation'la yapılan sorgu indexi kullanıp üç sonucu bulurken, strength: 1 ile yapılan sorgu yine üç sonucu bulur ama indexi kullanmaz; collation'sız sorgu ise yalnızca bir sonuç döndürür. Koleksiyon bir varsayılan collation ile oluşturulduysa sonradan kurulan indexler ve farklı collation belirtmeyen sorgular bu varsayılanı devralır.
Sık yapılan hatalar
- Unique indexte eksik alanın tek bir kez kabul edileceğini unutmak: İsteğe bağlı alanlar için partial veya sparse ile birleştirin.
- Partial indexi filtre koşulu olmadan sorgulamak: Filtre sorguda yoksa index seçilmez ve sorgu koleksiyon taramasına düşebilir.
- Sparse indexe hint verip sayım yapmak: Yalnızca alanı olan belgeler sayılır, toplam yanlış çıkar.
- Index collation'ını sorguda belirtmemek: Index kullanılmaz ve sonuçlar beklenenden farklı olur.
- Hidden indexin kısıtlarının kalktığını sanmak: Unique ve TTL davranışı gizliyken de sürer.
- Partial ile sparse'ı aynı indexte vermek: Bu birleşime izin verilmez.
Sık Sorulan Sorular
Sparse mı partial mı kullanmalıyım?
Belgeler, özel bir gereksiniminiz yoksa partial indexi önerir. Partial index sparse davranışını birebir taklit edebilir ve buna ek olarak başka alanlara ve koşullara göre filtreleyebilir.
Gizli bir index disk alanı kullanmaya devam eder mi?
Evet. Gizli indexler yazmalarla güncellenir, disk ve bellek kullanır ve istatistiklerde görünür. Tek farkları sorgu planlarında kullanılmamalarıdır.
Unique index aynı belgedeki dizi elemanlarının tekrarını engeller mi?
Hayır. Kural ayrı belgeler arasında geçerlidir; bir belgenin kendi dizisinde aynı değerin tekrarlanması, başka bir belgeyle çakışmadığı sürece reddedilmez.
Var olan bir indexi silmeden unique yapabilir miyim?
Evet. collMod ile önce prepareUnique: true, ardından dryRun: true ile kontrol ve son olarak unique: true adımlarını izleyebilirsiniz.
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 · MongoDB index oluşturma · Compound index ve ESR kuralı · TTL index · E11000 duplicate key hatası · Regex sorguları · Schema validation
