MongoDB sorgu operatörleri, filtre belgesinde basit eşitliğin ötesine geçmenizi sağlayan, adı $ ile başlayan anahtarlardır. Karşılaştırma için $eq, $ne, $gt, $gte, $lt, $lte, $in ve $nin; mantıksal birleştirme için $and, $or, $not ve $nor; alan varlığı ve türü için $exists ile $type; aynı belgedeki iki alanı karşılaştırmak için ise $expr kullanılır.

Operatörlerin sözdizimi kolaydır ama ayrıntıları sürpriz doğurur: $ne ve $nin alanı olmayan belgeleri de getirir, $gt farklı türdeki değerleri sessizce eler, $not ile $lte aynı şey değildir. Bu yazıda her operatörü MongoDB 9.0 belgelerindeki davranış kurallarıyla anlatıyor ve sık yapılan hataları gösteriyoruz. Temel find() kullanımı için find sorguları yazısını okumanızı öneririz.

Operatörlerin genel biçimi

Operatörlerin genel biçimi: Değere eşit, Değere eşit değil, $gt / $gte, $lt / $lte

Alan düzeyindeki operatörler { alan: { $operatör: değer } } biçiminde yazılır. Mantıksal operatörlerden $and, $or ve $nor ise üst düzeyde durur ve bir koşul dizisi alır: { $or: [ { ... }, { ... } ] }. $not istisnadır; alanın içine yazılır ve başka bir operatör ifadesini sarar.

OperatörNe yaparAlanı olmayan belgeyi eşler mi?
$eqDeğere eşitHayır (değer null değilse)
$neDeğere eşit değilEvet
$gt / $gteBüyük / büyük veya eşitHayır
$lt / $lteKüçük / küçük veya eşitHayır
$inListedeki değerlerden birine eşitHayır
$ninListedeki hiçbir değere eşit değilEvet
$notVerilen operatör ifadesinin tersiEvet
$norDizideki tüm koşulların başarısız olmasıEvet ($exists ile dışlanmadıkça)
$existsAlan var mı yok muYalnızca false ile
$typeDeğerin BSON türüHayır

$eq ve örtük eşitlik

{ adet: 20 } ile { adet: { $eq: 20 } } aynı sonucu verir. Belgeye göre tek fark, değer bir düzenli ifade olduğunda ortaya çıkar: örtük biçimde { alan: /desen/ } yazarsanız MongoDB bunu $regex eşleşmesine çevirir; açık biçim olan $eq ise bu çeviriyi yapmaz.

Değer bir belge olduğunda alanların sırası önemlidir. Değer bir dizi olduğunda ise alan o diziye birebir eşitse ya da o diziyi eleman olarak içeriyorsa eşleşme olur; burada da eleman sırası önemlidir. Yani { etiket: [ "A", "B" ] } sorgusu [ "B", "A" ] dizisini eşlemez.

javascript
// İkisi aynı sonucu verir
db.stok.find( { adet: 20 } )
db.stok.find( { adet: { $eq: 20 } } )

// Kullanıcı girdisi için önerilen kalıp
const kullaniciAdi = girdi.kullanici;   // uygulamadan gelen değer
db.kullanicilar.findOne( { kullanici: { $eq: kullaniciAdi } } )

$gt, $gte, $lt, $lte ve tür sınırlaması

Büyüklük-küçüklük operatörleri sayılar, tarihler ve metinler üzerinde çalışır. Önemli ayrıntı, belgenin type bracketing (tür sınırlaması) dediği kuraldır: bu dört operatör yalnızca sorgudaki değerle aynı BSON türünde olan değerleri karşılaştırır, farklı türdekileri sessizce eler. Örneğin { a: { $gt: 0 } } aslında "a bir sayıdır ve 0'dan büyüktür" anlamına gelir. Sayısal türler (int, long, double, decimal) bu kural açısından eşdeğer kabul edilir; yani sayı ile yapılan bir karşılaştırma bu dört türün hepsini kapsar.

javascript
// qty değerleri: 5, 7, 3, { "in stock": 8, "ordered": 12 }, "fourteen"
db.meyveler.find( { qty: { $gt: 4 } } )
// Yalnızca sayısal ve 4'ten büyük olanlar (5 ve 7) eşleşir;
// metin ve gömülü belge değerleri tür farkı nedeniyle elenir.

Bu davranış, alan türleri tutarsız olan koleksiyonlarda "kayıp" belgelerin ana sebebidir. Bir alanın bazı belgelerde sayı, bazılarında metin olarak saklandığını düşünün: $gt sorgusu metin olanları hiç görmez. Çözüm, veriyi tek türde tutmak ve gerekirse şema doğrulaması ile türü zorunlu kılmaktır. Dizi alanlarında da aynı kural geçerlidir: { a: { $gt: 3 } } filtresi [ "red", 4, "blue" ] dizisini eşler, çünkü 4 hem sayıdır hem de 3'ten büyüktür; ama [ "red", "blue" ] eşleşmez.

javascript
// Aralık sorgusu: aynı alanda iki operatör
db.siparisler.find( { toplam: { $gte: 100, $lt: 500 } } )

// Tarih karşılaştırması
db.siparisler.find( { tarih: { $gte: new Date("2026-10-01") } } )

$ne ve $nin: eksik alanları da getirir

$ne ve $nin: eksik alanları da getirir

$ne, değeri verilen değere eşit olmayan belgeleri seçer ve buna alanı hiç içermeyen belgeler de dahildir. $nin aynı şekilde, değeri listede olmayan veya alanı bulunmayan belgeleri getirir. Ayrıca bu iki operatör tür sınırlaması uygulamaz: { a: { $ne: 2 } } filtresi a alanı metin olan belgeleri de eşler.

javascript
// rated alanı "G" olmayanlar + rated alanı hiç olmayanlar
db.filmler.find( { rated: { $ne: "G" } } )

// Yalnızca alanı olan ve "G" olmayanlar
db.filmler.find( { rated: { $exists: true, $ne: "G" } } )

// rated değeri G veya PG olmayanlar (eksik alanlar dahil)
db.filmler.find( { rated: { $nin: [ "G", "PG" ] } } )

Dizi alanlarında $ne skaler bir değerle kullanıldığında, o değeri eleman olarak içermeyen dizileri eşler. Bir diziyle kullanıldığında ise alan dizisi verilen diziyle birebir aynı olmayan belgeleri eşler; eleman sırası ya da sayısı farklı olan diziler de buna dahildir. $nin bir diziye uygulandığında, listedeki değerlerden hiçbirine eşit eleman içermeyen dizileri seçer.

$in: aynı alanda birden çok değer

$in, alan değeri listedeki değerlerden herhangi birine eşit olan belgeleri seçer. Alan bir diziyse, dizinin en az bir elemanı listedeki bir değerle eşleştiğinde belge seçilir. Belge, aynı alan üzerindeki eşitlik kontrolleri için $or yerine $in kullanmayı önerir. Ayrıca $in her değeri her belgeyle karşılaştırdığı için sorgulanan alanda bir indeks oluşturmanızı tavsiye eder; indeks, listedeki her eleman için sınırlar kurarak aramayı hızlandırır.

javascript
// Önerilen
db.filmler.find( { rated: { $in: [ "G", "TV-G" ] } } )

// Aynı anlam ama aynı alan için önerilmeyen yazım
db.filmler.find( { $or: [ { rated: "G" }, { rated: "TV-G" } ] } )

// _id listesiyle toplu getirme
db.urunler.find( { _id: { $in: [ 5, 8, 13 ] } } )

$in listesine düzenli ifadeler de koyabilirsiniz; bu kullanım regex sorguları yazısında anlatılıyor.

$and ve $or: mantıksal birleştirme

Virgülle ayrılmış koşullar zaten örtük bir AND oluşturur, bu yüzden $and çoğu zaman gerekmez. Gerektiği yer, aynı alan üzerinde aynı operatörü iki kez kullanmak istediğiniz durumdur. mongosh dahil çoğu dil aynı nesne düzeyinde yinelenen anahtarlarla nesne kurulmasına izin vermez; { fiyat: { $in: [...], $in: [...] } } yazarsanız sunucuya giden sorgu niyetinizden farklı olur. Bu durumda açık $and kullanın.

javascript
// Hatalı: aynı nesnede iki $in anahtarı
db.stok.find( { fiyat: { $in: [ 7.99, 3.99 ], $in: [ 4.99, 1.99 ] } } )

// Doğru: iki koşul ayrı ayrı
db.stok.find( {
   $and: [
      { fiyat: { $in: [ 7.99, 3.99 ] } },
      { fiyat: { $in: [ 4.99, 1.99 ] } }
   ]
} )

$or, dizideki koşullardan en az birini sağlayan belgeleri seçer. İndeks davranışı önemlidir: belgeye göre $or ifadesinin indeksle değerlendirilebilmesi için bütün maddelerinin indeksle desteklenmesi gerekir; biri bile desteklenmiyorsa MongoDB koleksiyon taraması yapar. Her madde kendi indeksini kullanabilir; bu yüzden $or içindeki iki farklı alan için bileşik indeks yerine iki ayrı indeks önerilir.

javascript
db.filmler.find( { $or: [ { sure: { $gt: 1000 } }, { yil: { $lt: 1910 } } ] } )

// Bu sorguyu desteklemek için iki ayrı indeks
db.filmler.createIndex( { sure: 1 } )
db.filmler.createIndex( { yil: 1 } )

Hata davranışı da bilinmelidir: sorgu motorunun iyileştirme yapabilmesi için $and ve $or, tek başına hata üretecek bir ifade içerdiğinde hata verebilir ama hata vermesi garanti değildir. İlk ifade sonucu belirlemiş olsa bile sonraki bir ifade hata üretebilir. Yani kısa devre (short circuit) davranışına güvenerek hatalı ifadeleri korumaya çalışmayın.

$not ve $nor

$not bir operatör ifadesinin tersini alır ve alanı olmayan belgeleri de seçer. Belgedeki örnekte { runtime: { $not: { $gt: 180 } } } filtresi, değeri 180'e eşit veya küçük olanları ve bu alanı hiç olmayanları getirir. { runtime: { $lte: 180 } } ise yalnızca alanı var olup değeri 180 veya altında olanları getirir. İki sorgu aynı değildir.

javascript
db.filmler.find( { sure: { $not: { $gt: 180 } } } )   // eksik alanlar dahil
db.filmler.find( { sure: { $lte: 180 } } )            // yalnızca alanı olanlar

// Geçersiz: $not doğrudan değer almaz
// { sure: { $not: 120 } }
// Eşitsizlik için { sure: { $not: { $eq: 120 } } } ya da { sure: { $ne: 120 } }

$not düzenli ifade nesneleriyle ve $regex ifadeleriyle de kullanılabilir; örneğin başlığı T ile başlamayan belgeleri bulmak için. Dizilerle kullanıldığında beklenmedik sonuçlar verebileceğini belge özellikle not eder ve birden çok yanlış koşulu eşlemek için $nor önerir.

$nor, dizideki koşulların hepsinde başarısız olan belgeleri seçer. { $nor: [ { fiyat: 1.99 }, { indirim: true } ] } filtresi fiyatı 1.99 olmayan ve indirimi true olmayan belgeleri getirir; bu alanların bir ya da ikisini hiç içermeyen belgeler de sonuca girer. Eksik alanları dışarıda bırakmak istiyorsanız $exists: false koşullarını da $nor dizisine eklersiniz.

javascript
// Eksik alanlar da gelir
db.stok.find( { $nor: [ { fiyat: 1.99 }, { indirim: true } ] } )

// Yalnızca iki alanı da olan ve koşulları sağlamayanlar
db.stok.find( { $nor: [ { fiyat: 1.99 }, { fiyat: { $exists: false } },
                        { indirim: true }, { indirim: { $exists: false } } ] } )

$exists ve $type

$exists ve $type: Double, String, Object, Array

$exists: true alanı içeren belgeleri, değeri null olsa bile seçer; $exists: false ise yalnızca alanı hiç bulunmayan belgeleri getirir. Belge, bu operatörün SQL'deki EXISTS ile aynı şey olmadığını özellikle belirtir. Aggregation ifadeleri içinde $exists kullanılamaz; ifade bağlamında alan varlığını kontrol etmek için $type aggregation operatörüyle türün missing olup olmadığına bakılır.

$type, alanın değerinin belirtilen BSON türünde olduğu belgeleri seçer. Türü numarasıyla ya da takma adıyla verebilirsiniz ve bir dizi içinde birden çok tür sayabilirsiniz. "number" takma adı double, int, long ve decimal türlerinin hepsini kapsar. Alan bir diziyse, en az bir elemanı istenen türde olan belgeler döner; $type: "array" ise alanın kendisi dizi olan belgeleri seçer.

TürNumaraTakma ad
Double1double
String2string
Object3object
Array4array
ObjectId7objectId
Boolean8bool
Date9date
Null10null
32-bit integer16int
64-bit integer18long
Decimal12819decimal
javascript
// puan alanı metin olarak saklanmış belgeleri bul (veri temizliği için)
db.filmler.find( { puan: { $type: "string" } } )
db.filmler.find( { puan: { $type: 2 } } )            // aynı anlam

// Her türlü sayı
db.filmler.find( { puan: { $type: "number" } } )

// Metin veya double
db.filmler.find( { puan: { $type: [ "string", "double" ] } } )

Bu iki operatör, tür sınırlamasıyla birlikte düşünüldüğünde veri kalitesi kontrolünün temel aracıdır: $gt sorgunuz beklediğinizden az belge döndürüyorsa önce $type ile alanın farklı türlerde saklanıp saklanmadığına bakın. Performans tarafında belge, { $exists: true } sorgularının sparse indeksle en verimli çalıştığını, { $exists: false } sorgularının ise sparse indeksi kullanamadığını belirtir; ayrıntılar unique, partial ve sparse indeks yazısında.

$expr: aynı belgedeki alanları karşılaştırmak

Normal sorgu operatörleri bir alanı sabit bir değerle karşılaştırır. Aynı belgedeki iki alanı karşılaştırmak (örneğin harcamanın bütçeyi aşıp aşmadığını bulmak) için $expr gerekir. $expr sorgu filtresi içinde aggregation ifadelerini kullanmanıza izin verir; alanlara "$alanAdi" biçiminde başvurulur. Koşullu mantık için $cond ile birlikte de kullanılabilir.

javascript
// harcama alanı butce alanından büyük olan belgeler
db.projeler.find( { $expr: { $gt: [ "$harcama", "$butce" ] } } )

// Koşullu: oy sayısı 1000'in altındaysa puanın yarısını hesaba kat
db.filmler.find( {
   $expr: {
      $gt: [
         { $cond: { if: { $gte: [ "$oy", 1000 ] }, then: "$puan", else: { $multiply: [ "$puan", 0.5 ] } } },
         9
      ]
   }
} )

Dikkat edilmesi gereken nokta, $expr içindeki operatörlerin sorgu operatörü değil aggregation operatörü olmasıdır. Sözdizimi farklıdır: sorgu operatöründe { alan: { $gt: 5 } } yazılırken, aggregation ifadesinde { $gt: [ "$alan", 5 ] } yazılır. $expr aynı zamanda $lookup alt boru hatlarında let değişkenlerine başvurmanın da yoludur. Basit sabit karşılaştırmalarda $expr yerine normal operatörleri tercih edin; okunması daha kolaydır.

Uygulamalı mini örnek: sipariş filtreleri

Bir sipariş koleksiyonunda farklı operatörleri birlikte kullanalım. Önce örnek veri:

javascript
db.siparis.insertMany( [
   { no: 1, durum: "hazir",   toplam: 250,   kargo: 30, kupon: "YAZ10" },
   { no: 2, durum: "iptal",   toplam: 90,    kargo: 20 },
   { no: 3, durum: "kargoda", toplam: 40,    kargo: 45, kupon: null },
   { no: 4, durum: "hazir",   toplam: "120", kargo: 25 }
] )

İptal edilmemiş ve toplamı 100 veya üzeri olan siparişleri isteyen ilk sorgu yalnızca 1 numaralı siparişi eşler. 4 numaralı siparişin toplam değeri metin olarak saklandığı için tür sınırlaması onu eler. Bu tutarsızlığı bulmak için ikinci sorgu kullanılır ve 4 numaralı siparişi getirir.

javascript
db.siparis.find( { durum: { $ne: "iptal" }, toplam: { $gte: 100 } } )

db.siparis.find( { toplam: { $not: { $type: "number" } } } )

Kargo ücreti toplamdan büyük olan siparişler için $expr gerekir ve bu veride 3 numaralı sipariş eşleşir. Kupon alanı hiç olmayan siparişler $exists: false ile bulunur (2 ve 4); { kupon: null } yazsaydık değeri null olan 3 numaralı sipariş de sonuca girerdi. $expr sorgusunda metin olan "120" değeri sayıyla karşılaştırıldığında aggregation karşılaştırma kurallarına göre değerlendirilir; bu yüzden karışık türlü alanlarla ifade yazmaktan kaçının.

javascript
db.siparis.find( { $expr: { $gt: [ "$kargo", "$toplam" ] } } )

db.siparis.find( { kupon: { $exists: false } } )   // 2 ve 4 eşleşir
db.siparis.find( { kupon: null } )                 // 2, 3 ve 4 eşleşir

Sık yapılan hatalar

  • $ne ve $nin sonuçlarında eksik alanları beklememek: yalnızca alanı olanları istiyorsanız $exists: true ekleyin.
  • Karışık türlü alanlarda $gt kullanmak: tür sınırlaması farklı türdeki değerleri sessizce eler; önce $type ile kontrol edin.
  • Aynı nesnede aynı operatörü iki kez yazmak: sorgu niyetinizden farklı gider; açık $and kullanın.
  • $not'a doğrudan değer vermek: { $not: 120 } geçersizdir; $not bir operatör ifadesi ister.
  • Aynı alan için $or yazmak: eşitlik kontrollerinde $in belgenin önerdiği yoldur.
  • $or maddelerinden birini indekssiz bırakmak: tek bir indekssiz madde bütün sorguyu koleksiyon taramasına düşürür.
  • $expr içinde sorgu operatörü sözdizimi kullanmak: ifade operatörleri dizi argümanı alır: { $gt: [ "$a", "$b" ] }.

Sık Sorulan Sorular

$in ile $or arasında performans farkı var mı?

Belge, aynı alan üzerindeki eşitlik kontrolleri için $or yerine $in kullanmayı önerir. Farklı alanlardaki koşullar için ise $or gerekir ve her maddenin indeksle desteklenmesi önemlidir. Kendi sorgunuzu explain() ile inceleyin.

Büyük-küçük harf duyarsız eşitlik nasıl yapılır?

Bunun için collation veya düzenli ifade seçenekleri kullanılır. Ayrıntılar ve indeks açısından farkları regex sorguları yazısında.

Dizi alanlarında bu operatörler nasıl davranır?

Karşılaştırma operatörleri dizinin elemanlarına tek tek uygulanır ve en az bir elemanın koşulu sağlaması yeterlidir. Aynı elemanın birden çok koşulu birlikte sağlaması gerekiyorsa $elemMatch kullanılır; bu konu dizi sorgulama yazısında.

Bu operatörler güncelleme ve silme işlemlerinde de geçerli mi?

Evet. updateOne(), updateMany(), deleteMany() gibi yöntemlerin filtre parametresi aynı sorgu operatörlerini kabul eder. Örnekler için updateOne ve updateMany ile deleteOne ve deleteMany yazıları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 ve findOne sorguları · Dizi sorgulama ve $elemMatch · Regex sorguları · BSON veri tipleri · Index oluşturma