MongoDB'de veri okumanın temel yolu db.collection.find() ve db.collection.findOne() yöntemleridir. find() filtreye uyan belgeler için bir cursor döndürür; findOne() ise uyan belgelerden yalnızca birini, doğrudan belge olarak verir ve hiçbir belge uymazsa null döner. İki yöntem de aynı üç parametreyi alır: sorgu filtresi, projection (hangi alanların döneceği) ve seçenekler belgesi.
Bu yazıda filtre belgesinin nasıl kurulduğunu, eşitlik ve karşılaştırma sorgularını, gömülü (nested) belgelerde nokta gösterimini, projection kurallarını, null ve eksik alan ayrımını ve mongosh içinde cursor ile çalışmayı resmî MongoDB 9.0 belgelerine dayanarak anlatıyoruz. Operatörlerin tamamı için sorgu operatörleri yazısına, dizi alanları için dizi sorgulama yazısına bakabilirsiniz.
find() ve findOne() söz dizimi

Resmî belgede find() şu biçimde tanımlanır: db.collection.find(query, projection, options). Üç parametre de isteğe bağlıdır. Sorguyu hiç vermezseniz ya da boş bir belge ({}) verirseniz koleksiyondaki bütün belgeler seçilir. Projection verilmezse eşleşen belgelerin tüm alanları döner. Seçenekler belgesi ise sorgunun nasıl çalışacağını ve sonucun nasıl döneceğini değiştirir: limit, skip, sort, maxTimeMS, collation, hint, comment gibi.
// Koleksiyondaki tüm belgeler
db.urunler.find()
db.urunler.find( {} )
// Filtre + projection + seçenekler
db.urunler.find(
{ kategori: "kitap" }, // sorgu filtresi
{ ad: 1, fiyat: 1 }, // projection
{ limit: 5 } // seçenekler
)Belgeye göre find() "belge döndürür" dendiğinde aslında belgelere işaret eden bir cursor döndürmektedir. Bu ayrım önemlidir: cursor üzerinde sort(), limit(), skip() gibi yöntemleri zincirleyebilirsiniz. findOne() tek bir belge döndürdüğü için sonucuna cursor yöntemleri uygulanamaz; dönen nesnenin alanlarına doğrudan erişirsiniz.
const urun = db.urunler.findOne( { sku: "KTP-001" } )
urun.fiyat // belgenin fiyat alanı
// Uyan belge yoksa findOne() null döndürür
db.urunler.findOne( { sku: "OLMAYAN" } )findOne() hangi belgeyi döndürür?
Birden fazla belge filtreye uyduğunda findOne()'ın hangisini döndüreceği sorgu planına bağlıdır. Belgedeki tabloya göre: hiç eşleşme yoksa sonuç null olur; tek eşleşme varsa o belge döner. İki veya daha fazla eşleşmede, plan koleksiyon taraması (collection scan) ise doğal sıraya (natural order) göre ilk belge, indeks taraması ise indeksten ilk okunan belge döner. Capped koleksiyonlarda doğal sıra ekleme sırasıyla aynıdır.
// En eski siparişi tutarlı biçimde almak için sort seçeneği
db.siparisler.findOne(
{ durum: "beklemede" },
{ _id: 0, no: 1, tarih: 1 },
{ sort: { tarih: 1 } }
)Eşitlik filtresi ve örtük AND
En basit filtre { alan: değer } biçimindeki eşitlik koşuludur. Aynı belgeye birden çok alan koşulu yazdığınızda, MongoDB bu koşulları örtük (implicit) bir mantıksal AND ile birleştirir: belge ancak bütün koşulları sağlıyorsa seçilir. Bu, SQL'deki WHERE a = 1 AND b = 2 yapısının karşılığıdır.
// durum alanı "A" olan belgeler
db.urunler.find( { durum: "A" } )
// durum "A" VE kategori "kitap" olan belgeler (örtük AND)
db.urunler.find( { durum: "A", kategori: "kitap" } )
// _id ile tek belge arama
db.urunler.find( { _id: 5 } )Koşullardan herhangi birinin sağlanması yetiyorsa $or kullanılır; aynı alanda birkaç değerden birini arıyorsanız belge $in operatörünü önerir. Bu operatörlerin ayrıntıları ve tuzakları sorgu operatörleri yazısında anlatılıyor.
// fiyatı 50'den küçük VEYA kategorisi "dergi" olanlar
db.urunler.find( { $or: [ { fiyat: { $lt: 50 } }, { kategori: "dergi" } ] } )
// Belirli bir tarih aralığı: iki karşılaştırma aynı alanda
db.siparisler.find( { tarih: { $gt: new Date("2026-01-01"), $lt: new Date("2026-02-01") } } )Son örnekte aynı alan için iki karşılaştırma operatörü tek bir belge içinde yazıldı. Bu, bir aralık sorgusu kurmanın belgede gösterilen yoludur: tarih değeri ilk tarihten büyük ve ikinci tarihten küçük olan belgeler seçilir. Tarihleri metin olarak değil Date nesnesi olarak saklamak bu tür karşılaştırmalar için şarttır; tür konusu BSON veri tipleri yazısında.
Gömülü belgelerde nokta gösterimi
MongoDB belgeleri iç içe alanlar taşıyabilir. Örneğin bir ürünün boyut alanı { h: 14, w: 21, birim: "cm" } gibi gömülü bir belge olabilir. İç alana koşul yazmak için nokta gösterimi (dot notation) kullanılır: "boyut.birim". Belgede özellikle belirtildiği gibi nokta gösterimiyle sorgu yazarken alan adı tırnak içinde olmalıdır; JavaScript nesne anahtarında nokta kullanılabilmesi için bu gereklidir.
// boyut.birim alanı "cm" olan belgeler
db.urunler.find( { "boyut.birim": "cm" } )
// İç alanda karşılaştırma operatörü
db.urunler.find( { "boyut.h": { $lt: 15 } } )
// Birden çok iç alan ve üst düzey alan birlikte (örtük AND)
db.urunler.find( { "boyut.h": { $lt: 15 }, "boyut.birim": "in", durum: "D" } )Gömülü belgenin tamamıyla eşleştirme neden riskli?
Bir alanı doğrudan bir belgeyle karşılaştırmak da mümkündür: { boyut: { h: 14, w: 21, birim: "cm" } }. Ancak bu sorgu birebir eşleşme ister; alanların sırası dahil her şey aynı olmalıdır. Aynı değerleri farklı sırayla yazarsanız ({ w: 21, h: 14, birim: "cm" }) hiçbir belge eşleşmez. Gömülü belgede fazladan bir alan bulunması da eşleşmeyi bozar. MongoDB belgeleri bu tür karşılaştırmaları önermez ve sıralı veri yapısı kullanmayan sürücülerde beklenmedik sonuçlar doğabileceğini belirtir. Pratik kural şudur: gömülü belgelerde her zaman nokta gösterimiyle tek tek alan koşulu yazın. Bu yazım alan sırasından etkilenmez ve belgede başka alanlar olsa da çalışır.
// Kırılgan: alan sırası ve fazladan alanlar eşleşmeyi bozar
db.kisiler.find( { ad: { ilk: "Ayşe", son: "Yılmaz" } } )
// Sağlam: sıra ve ek alanlar önemli değil
db.kisiler.find( { "ad.ilk": "Ayşe", "ad.son": "Yılmaz" } )Projection: hangi alanlar dönsün?

İkinci parametre olan projection, dönen belgelerde hangi alanların yer alacağını belirler. Belgedeki kurallar şöyle özetlenebilir: alan: 1 (veya true) alanı dahil eder, alan: 0 (veya false) alanı hariç tutar. Sıfırdan farklı bir tamsayı da true sayılır. _id alanı, açıkça _id: 0 yazmadığınız sürece her zaman döner.
// Yalnızca ad ve durum (ve varsayılan olarak _id)
db.urunler.find( { durum: "A" }, { ad: 1, durum: 1 } )
// _id'yi de gizle
db.urunler.find( { durum: "A" }, { ad: 1, durum: 1, _id: 0 } )
// Belirtilenler dışındaki her şey
db.urunler.find( { durum: "A" }, { durum: 0, stok: 0 } )En sık yapılan hata dahil etme ile hariç tutmayı karıştırmaktır. Belgeye göre bir projection hem dahil etme hem hariç tutma içeremez; tek istisna _id alanıdır. Dahil etme yapan bir projection'da açıkça hariç tutabileceğiniz tek alan _id'dir; hariç tutma yapan bir projection'da açıkça dahil edebileceğiniz tek alan yine _id'dir. Yani { ad: 1, stok: 0 } geçersizdir.
| Projection | Anlamı |
|---|---|
| { ad: 1, fiyat: 1 } | Yalnızca ad, fiyat ve _id döner |
| { ad: 1, _id: 0 } | Yalnızca ad döner (geçerli istisna) |
| { stok: 0 } | stok dışındaki tüm alanlar döner |
| { ad: 1, stok: 0 } | Geçersiz: dahil etme ve hariç tutma karışık |
| { "boyut.birim": 1 } | Gömülü belgeden yalnızca birim, boyut içinde kalır |
| { boyut: 1, "boyut.birim": 1 } | Hata: Path collision |
Gömülü alanlarda projection
Gömülü bir alanı seçmek için yine nokta gösterimi kullanılır: { ad: 1, "boyut.birim": 1 } sonucu, birim alanı boyut belgesinin içinde kalacak şekilde döndürür. Aynı şeyi iç içe biçimde de yazabilirsiniz: { ad: 1, boyut: { birim: 1 } }. Hariç tutma da aynı mantıkla çalışır; { "boyut.birim": 0 } yalnızca o iç alanı gizler, geri kalan her şey döner.
Bir gömülü belgeyi ve onun bir alanını aynı projection içinde istemek ise hata verir. Belgede { size: 1, "size.uom": 1 } biçimindeki bir projection'ın Path collision hatasıyla başarısız olduğu belirtilir. Ya belgenin tamamını ya da yalnızca istediğiniz iç alanları seçin.
db.urunler.find( { durum: "A" }, { ad: 1, "boyut.birim": 1 } )
db.urunler.find( { durum: "A" }, { ad: 1, boyut: { birim: 1 } } ) // aynı anlam
db.urunler.find( { durum: "A" }, { "boyut.birim": 0 } )Projection içinde ifade kullanmak
find() projection'ı aggregation ifadelerini de kabul eder. Bu sayede yeni bir alan hesaplayabilir ya da var olan bir alanın değerini dönüştürebilirsiniz. Sayı veya boolean olmayan bir değer (metin, dizi, operatör ifadesi) verirseniz alan bu yeni değerle döner. Sabit bir sayıyı ya da true değerini alan değeri olarak döndürmek istiyorsanız $literal kullanmanız gerekir; aksi halde 1 ve true dahil etme olarak yorumlanır.
db.urunler.find(
{ },
{
_id: 0,
ad: 1,
alan: { $multiply: [ "$boyut.h", "$boyut.w" ] },
rapor: { $literal: 1 }
}
)Daha karmaşık dönüşümler için $project ve $addFields aşamaları yazısındaki aggregation yaklaşımı daha uygundur. Dizi elemanlarını seçen $slice, $elemMatch ve konumsal $ projection operatörleri ise dizi sorgulama yazısında ele alınıyor.
null ve eksik alan ayrımı
Yeni başlayanları en çok şaşırtan konu null sorgularıdır. { telefon: null } filtresi hem telefon alanı null olan belgeleri hem de bu alanı hiç içermeyen belgeleri eşler. Yalnızca değeri null olanları istiyorsanız BSON tür numarası 10 olan Null türünü $type ile sorgulayın. Yalnızca alanı olmayan belgeleri istiyorsanız $exists: false kullanın. Alanı var ve null değil koşulu için ise belge { $ne: null } filtresini gösterir.
// null olan VEYA hiç olmayan
db.musteriler.find( { telefon: null } )
// Yalnızca değeri null olan (BSON Null türü = 10)
db.musteriler.find( { telefon: { $type: 10 } } )
// Yalnızca alanı hiç olmayan
db.musteriler.find( { telefon: { $exists: false } } )
// Alan var ve null değil
db.musteriler.find( { telefon: { $ne: null } } )| Filtre | null değer | Alan yok | Gerçek değer |
|---|---|---|---|
| { telefon: null } | Eşleşir | Eşleşir | Eşleşmez |
| { telefon: { $type: 10 } } | Eşleşir | Eşleşmez | Eşleşmez |
| { telefon: { $exists: false } } | Eşleşmez | Eşleşir | Eşleşmez |
| { telefon: { $ne: null } } | Eşleşmez | Eşleşmez | Eşleşir |
Alan bir dizi olduğunda durum daha da incelir: { a: null }, dizi en az bir null eleman içeriyorsa da eşleşir; boş dizi ise eşleşmez. Nokta gösterimi bir dizinin içinden geçiyorsa { $ne: null } ile $exists aynı sonucu vermeyebilir. Belgedeki "Null Comparisons on Array Fields" bölümü bu durumu ayrıntılı örneklerle açıklar; dizi alanlarında null sorgusu yazıyorsanız sonucu küçük bir veriyle mutlaka kontrol edin.
Cursor ile çalışmak
mongosh içinde find() sonucunu bir değişkene atamazsanız kabuk cursor'ı otomatik olarak dolaşır ve ilk 20 belgeyi gösterir. Devamını görmek için it yazarsınız. Bu sayı displayBatchSize ayarıyla değiştirilebilir. Sonucu bir değişkene atadığınızda ise dolaşmayı siz yönetirsiniz: hasNext() ve next() ile tek tek ilerleyebilir ya da forEach() ile her belgeye bir fonksiyon uygulayabilirsiniz.
// Otomatik gösterim sayısını 3 yap
config.set( "displayBatchSize", 3 )
// Elle dolaşma
const c = db.urunler.find( { durum: "A" } )
while ( c.hasNext() ) {
printjson( c.next().ad )
}
// forEach ile
db.urunler.find( { durum: "A" } ).forEach( printjson )Sıralama, sınırlama ve atlama cursor yöntemleriyle zincirlenir. Resmî find komutu sayfasına göre bir sorgu genel olarak şu sırayla işlenir: eşleştirme (match), sıralama (sort), atlama (skip), sınırlama (limit) ve projection. Yöntemleri hangi sırada zincirlediğiniz bu mantıksal sırayı değiştirmez; MongoDB performans için bileşenleri farklı sırada çalıştırsa bile bu sırayı korur. Sayfalama ve sayım ayrıntıları sort, limit ve skip yazısında.
db.urunler.find( { durum: "A" } ).sort( { fiyat: -1 } ).limit( 10 )
db.urunler.find( { durum: "A" } ).limit( 10 ).sort( { fiyat: -1 } ) // aynı sonuçUzun sürebilecek sorgular için maxTimeMS seçeneği sorgunun sunucuda en fazla kaç milisaniye çalışabileceğini belirler. comment seçeneği sorguya bir not ekler ve bu not profiler kayıtlarında görünür; hangi uygulama parçasının hangi sorguyu gönderdiğini izlemek için kullanışlıdır. hint ise sorgu iyileştiricisini belirli bir indeksi kullanmaya zorlar. Sorgunun hangi planla çalıştığını görmek için explain() kullanılır.
db.urunler.find(
{ kategori: "kitap" },
{ ad: 1 },
{ maxTimeMS: 2000, comment: "katalog-listesi" }
)Uygulamalı mini örnek: ürün kataloğu
Küçük bir kataloğu baştan sona sorgulayalım. Önce birkaç belge ekliyoruz (ekleme ayrıntıları için insertOne ve insertMany).
db.katalog.insertMany( [
{ ad: "Defter", fiyat: 40, stok: 120, boyut: { h: 21, w: 15, birim: "cm" }, durum: "A" },
{ ad: "Ajanda", fiyat: 95, stok: 0, boyut: { h: 25, w: 18, birim: "cm" }, durum: "A" },
{ ad: "Kalem", fiyat: 12, stok: 500, durum: "A" },
{ ad: "Poster", fiyat: 60, stok: 30, boyut: { h: 70, w: 50, birim: "cm" }, durum: "D" }
] )Şimdi "satışta (durum A), stokta olan, fiyatı 50'nin altındaki ürünlerin yalnızca adını ve fiyatını, fiyata göre artan sırayla" isteyelim. Bu sorgu Defter ve Kalem belgelerini döndürür: Ajanda'nın stoğu sıfır, Poster'ın durumu D olduğu için elenir.
db.katalog.find(
{ durum: "A", stok: { $gt: 0 }, fiyat: { $lt: 50 } },
{ _id: 0, ad: 1, fiyat: 1 }
).sort( { fiyat: 1 } )Boyut bilgisi olmayan ürünleri bulmak için $exists kullanırız; bu örnekte yalnızca Kalem döner. Yüksekliği 30 cm'den büyük ürünler için ise nokta gösterimi yeterlidir ve Poster döner.
db.katalog.find( { boyut: { $exists: false } }, { _id: 0, ad: 1 } )
db.katalog.find( { "boyut.h": { $gt: 30 } }, { _id: 0, ad: 1, "boyut.h": 1 } )Sık yapılan hatalar

- Nokta gösteriminde tırnak unutmak:
{ boyut.h: 10 }JavaScript sözdizimi hatasıdır; doğru yazım{ "boyut.h": 10 }. - Gömülü belgeyi bütün olarak karşılaştırmak: alan sırası farklıysa hiçbir şey dönmez. İç alanlara tek tek koşul yazın.
- Projection'da 1 ve 0 karıştırmak:
_iddışında dahil etme ile hariç tutma bir arada kullanılamaz. - findOne() sonucunun hep aynı olacağını varsaymak: tutarlılık gerekiyorsa
sortseçeneği verin. - null filtresinin eksik alanları da getirdiğini unutmak: yalnızca null için
$type: 10, yalnızca eksik alan için$exists: false. - Tarihleri metin olarak saklayıp aralık sorgusu yazmak: karşılaştırma tür bazlıdır;
Datekullanın. - Kullanıcı girdisini doğrudan filtreye koymak: belge, kullanıcıdan gelen değerlerle her zaman açık
{ alan: { $eq: değer } }biçiminin kullanılmasını önerir.
Sık Sorulan Sorular
find() ile findOne() arasındaki fark nedir?
find() bir cursor döndürür ve üzerinde sort(), limit() gibi cursor yöntemleri kullanılabilir. findOne() tek bir belge ya da null döndürür; sonucuna cursor yöntemi uygulanamaz ama sıralama ihtiyacını seçenekler belgesindeki sort ile karşılayabilirsiniz.
mongosh neden yalnızca 20 belge gösteriyor?
Atanmamış bir cursor mongosh tarafından otomatik dolaşılır ve varsayılan olarak ilk 20 belge gösterilir. it yazarak devam edebilir ya da config.set("displayBatchSize", ...) ile bu sayıyı değiştirebilirsiniz. Bu yalnızca kabuktaki gösterimi etkiler.
Bir alanın SQL'deki LIKE gibi aranmasını nasıl yaparım?
Belgedeki örnekte { "name.last": { $regex: /^N/ } } sorgusu, soyadı N ile başlayanları seçer ve LIKE 'N%' karşılığı olarak gösterilir. Seçenekler, indeks kullanımı ve büyük-küçük harf konusu regex sorguları yazısında.
Sorgularım yavaşsa ne yapmalıyım?
Önce explain() ile sorgunun koleksiyon taraması mı indeks taraması mı yaptığına bakın. Sık filtrelediğiniz alanlar için indeks oluşturmak çoğu zaman ilk adımdır; birden çok alanlı filtreler için bileşik indeks ve ESR kuralı 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 · Sorgu operatörleri · Dizi sorgulama ve $elemMatch · Sort, limit, skip ve sayfalama · insertOne ve insertMany · SQL ile MongoDB karşılaştırması