İçeriğe geç

Menü

Berk Akademi
Yorumlar Google yorumları Özel Ders Başvuru Ücretsiz ön görüşme Ana Sayfa

mongodb lookup ile İki Koleksiyon Nasıl Birleştirilir? MongoDB $lookup

mongodb-lookup-ile-iki-koleksiyon-nasil-birlestirilir
Bu yazıda neler var?
  1. $lookup aggregate işlem hattında nerede kullanılır?
  2. $lookup söz dizimindeki dört alan neyi gösterir?
  3. Öğrenci ve kurs belgelerini mongosh ile nasıl birleştirirsin?
  4. $unwind sonucu diziden tek belgeye nasıl dönüştürür?
  5. Eşleşme yoksa boş dizi neden LEFT JOIN'e benzer?
  6. $match ve indeks $lookup performansını nasıl etkiler?
  7. Pipeline kullanan gelişmiş $lookup ne zaman gerekir?
  8. Sık Sorulan Sorular

MongoDB $lookup ile iki koleksiyonu SQL'deki JOIN işlemine benzer biçimde birleştirebilirsin. Bunun için bir koleksiyon üzerinde aggregate() çağrısı yapar, eşleşme alanlarını tanımlar ve ilişkili belgeleri yeni bir alan içinde alırsın. Temel kullanımda sonuç, ilişkili belgeleri içeren bir dizi olarak eklenir.

Aşağıdaki bölümlerde önce işlem hattındaki yerini, ardından temel söz dizimindeki dört alanı ve çalışan bir öğrenci-kurs örneğini göreceksin.

$lookup aggregate işlem hattında nerede kullanılır?

MongoDB'de iki koleksiyon, SQL'deki JOIN işlemine benzer biçimde $lookup aşamasıyla birleştirilir. Koleksiyon SQL tablosuna, belge satıra ve alan da sütuna benzer bir rol üstlenir; ancak MongoDB'de belgeler JSON benzeri, iç içe veri yapıları taşıyabilir.

aggregate() çağrısı, aşamaların sırayla uygulandığı bir işlem hattı başlatır. Her aşama önceki aşamadan gelen belgeleri alır ve bunları filtreleyebilir, zenginleştirebilir, dönüştürebilir veya farklı bir biçimde düzenleyebilir. MongoDB'nin resmi belgelerinde $lookup, mevcut koleksiyondaki belgelerle aynı veritabanındaki başka bir koleksiyondan gelen belgeleri eşleştiren bir aggregation aşaması olarak tanımlanır.

db.ogrenciler.aggregate([
  { $match: { aktif: true } },
  {
    $lookup: {
      from: "kurslar",
      localField: "kursId",
      foreignField: "_id",
      as: "kursBilgisi"
    }
  },
  { $unwind: "$kursBilgisi" },
  { $project: { ad: 1, "kursBilgisi.ad": 1 } }
])

Bu akışta $match başlangıç belgelerini filtreler, $lookup ilişkili kurs belgelerini bulur, $unwind dizi içindeki öğeleri ayrı belgelere açar ve gerekirse $project gösterilecek alanları seçer. $lookup aşaması eşleşen belgeleri as alanında belirtilen yeni bir dizi alanına ekler.

$group ise belgeleri ortak değerlere göre toplulaştırmak, saymak veya toplam hesaplamak için kullanılan farklı bir aşamadır. Bu nedenle koleksiyonları birleştirme görevinde doğrudan $lookup'ın yerine geçmez.

$lookup söz dizimindeki dört alan neyi gösterir?

$lookup söz dizimindeki dört alan neyi gösterir?

Temel $lookup biçiminde from, localField, foreignField ve as alanları verinin hangi koleksiyondan alınacağını, hangi alanların eşleştirileceğini ve sonucun nereye yazılacağını belirler.

{
  $lookup: {
    from: "hedefKoleksiyon",
    localField: "mevcutKoleksiyondakiAlan",
    foreignField: "hedefKoleksiyondakiAlan",
    as: "sonucAlani"
  }
}
  • from: SQL'deki hedef tabloya benzer. Eşleştirilecek belgelerin alınacağı koleksiyonun adıdır.
  • localField: SQL sorgusundaki mevcut tablonun JOIN alanına benzer. aggregate() çağrısını yaptığın koleksiyondaki alandır.
  • foreignField: Hedef koleksiyondaki eşleşme alanıdır. MongoDB, bu alandaki değeri localField değeriyle karşılaştırır.
  • as: Eşleşen belgelerin yazılacağı yeni alanın adıdır. Bu alanın değeri temel kullanımda bir dizi olur.

Örneğin öğrenciler koleksiyonunda kurs kimliği kursId alanında, kurslar koleksiyonunda ise aynı kimlik belge kimliği olan _id alanında tutuluyorsa yön şu şekilde kurulmalıdır:

localField: "kursId",
foreignField: "_id"

Alanların yönü önemlidir. localField mevcut koleksiyonun alanını, foreignField ise from ile belirtilen koleksiyonun alanını ifade eder. Temel söz dizimindeki bu alanların eşitlik eşleşmesiyle çalışması ve sonucun yeni bir dizi alanına eklenmesi resmi MongoDB belgelerinde açıklanır.

Öğrenci ve kurs belgelerini mongosh ile nasıl birleştirirsin?

Aşağıdaki örnek, tekrar çalıştırıldığında önceki deneme verilerini temizlemek için ayrı bir lookup_demo veritabanı kullanır. İki kurs, bu kurslardan birine kayıtlı iki öğrenci ve eşleşmeyen bir kursId taşıyan üçüncü öğrenci oluşturulur.

db = db.getSiblingDB("lookup_demo");
db.dropDatabase();

db.kurslar.insertMany([
  {
    _id: ObjectId("64f100000000000000000001"),
    ad: "Python Temelleri",
    seviye: "Başlangıç"
  },
  {
    _id: ObjectId("64f100000000000000000002"),
    ad: "Java Temelleri",
    seviye: "Başlangıç"
  }
]);

db.ogrenciler.insertMany([
  {
    _id: ObjectId("64f200000000000000000001"),
    ad: "Ayşe",
    kursId: ObjectId("64f100000000000000000001")
  },
  {
    _id: ObjectId("64f200000000000000000002"),
    ad: "Mehmet",
    kursId: ObjectId("64f100000000000000000002")
  },
  {
    _id: ObjectId("64f200000000000000000003"),
    ad: "Zeynep",
    kursId: ObjectId("64f100000000000000000003")
  }
]);

const sonuc = db.ogrenciler.aggregate([
  {
    $lookup: {
      from: "kurslar",
      localField: "kursId",
      foreignField: "_id",
      as: "kursBilgisi"
    }
  }
]).toArray();

sonuc.forEach(doc => printjson({
  ad: doc.ad,
  kursBilgisi: doc.kursBilgisi.map(kurs => kurs.ad)
}));

Bu sorguda Ayşe ve Mehmet için kursBilgisi alanı, eşleşen kurs adını içeren bir dizi olarak yazdırılır. Zeynep'in kursId değeri kurslar koleksiyonunda bulunmadığı için aynı alanın değeri boş dizi, yani [] olur. ObjectId() ifadesi mongosh içinde BSON ObjectId değerleri oluşturmak için kullanılır; aggregate() ise koleksiyon üzerinde aggregation işlem hattına erişim sağlar.

$unwind sonucu diziden tek belgeye nasıl dönüştürür?

$unwind aşaması, $lookup ile oluşan dizi alanındaki elemanları ayrı belge sonuçlarına açar; böylece her eşleşme ayrı bir sonuç belgesinde işlenebilir. Basit kullanımda dizi alanının yolu doğrudan yazılır:

db.ogrenciler.aggregate([
  {
    $lookup: {
      from: "kurslar",
      localField: "kursId",
      foreignField: "_id",
      as: "kursBilgisi"
    }
  },
  { $unwind: "$kursBilgisi" },
  {
    $project: {
      _id: 0,
      ad: 1,
      "kursBilgisi._id": 1,
      "kursBilgisi.ad": 1
    }
  }
])

Bu sorguda kursBilgisi artık dizi olarak değil, eşleşen kurs belgesi nesnesi olarak görünür. Örneğin eşleşen bir öğrenci için çıktı şu yapıda olur:

{
  ad: "Ayşe",
  kursBilgisi: {
    _id: ObjectId("665c1f2a8b7e4d6a1c9f0031"),
    ad: "Python Temelleri"
  }
}

$unwind varsayılan olarak boş, eksik veya null değerli alanlar için sonuç belgesi üretmez. Eşleşmesi olmayan öğrencileri de korumak istiyorsan preserveNullAndEmptyArrays: true seçeneğini kullanabilirsin. Bu durumda kursBilgisi alanı boş kalırken öğrenci belgesi sonuçta tutulur.

db.ogrenciler.aggregate([
  {
    $lookup: {
      from: "kurslar",
      localField: "kursId",
      foreignField: "_id",
      as: "kursBilgisi"
    }
  },
  {
    $unwind: {
      path: "$kursBilgisi",
      preserveNullAndEmptyArrays: true
    }
  }
])

Eşleşme yoksa boş dizi neden LEFT JOIN'e benzer?

Eşleşme yoksa boş dizi neden LEFT JOIN'e benzer?

Temel $lookup sonucunda eşleşme bulunmadığında as alanının boş dizi alması, ana koleksiyondaki belgenin korunması bakımından SQL LEFT JOIN davranışına benzer. Ancak bu, SQL sözdiziminin birebir karşılığı değildir; MongoDB sonucu belge yapısı içinde, eşleşen kayıtları taşıyan bir diziyle temsil eder.

Boş diziyle karşılaştığında şu üç noktayı kontrol et:

  • Alanları ters yazma: localField ana koleksiyondaki, yani ogrenciler belgesindeki alanı; foreignField ise kurslar koleksiyonunda aranacak alanı göstermelidir.
  • BSON türlerinin uyuşmaması: Bir tarafta ObjectId, diğer tarafta aynı değerin string biçimi varsa eşleşme oluşmayabilir. Değerleri ve türlerini birlikte kontrol et.
  • Diziyi nesne sanma: kursBilgisi alanı $lookup sonrasında dizidir. Uygulama kodunda onu doğrudan tek bir kurs nesnesi gibi kullanmak yerine ham çıktıyı incele veya gerektiğinde $unwind ekle.

Teşhis için alan adlarını karşılaştır, iki alandaki BSON türlerini kontrol et, yalnızca $lookup içeren ham işlem hattını çalıştır ve dönen kursBilgisi değerini incele. Dizi yapısını tek belgeye çevirmek gerekiyorsa sonraki aşamaya $unwind ekle. $lookup alanları eşitlik karşılaştırmasıyla çalıştığı için alan adı ve değer türü kontrolü özellikle önemlidir.

$match ve indeks $lookup performansını nasıl etkiler?

Birleştirmeden önce uygun bir $match aşaması kullanmak, işlenecek ana koleksiyon belgelerini azaltmaya yardımcı olabilir. Örneğin yalnızca aktif öğrencileri seçip ardından kurs bilgilerini getirebilirsin:

db.ogrenciler.aggregate([
  { $match: { durum: "aktif" } },
  {
    $lookup: {
      from: "kurslar",
      localField: "kursId",
      foreignField: "_id",
      as: "kursBilgisi"
    }
  }
])

$match aşamasını mümkün olduğunca erken konumlandırmak, sonraki aşamalara aktarılacak belge sayısını azaltabilir. $lookup içindeki eşleşme için kullanılan foreignField üzerinde uygun bir indeks bulunması da aramaları destekleyebilir. Bunun etkisi sorgunun yapısı, veri dağılımı ve yürütme planıyla birlikte değerlendirilmelidir; tek başına indeks eklemek belirli bir sonuç garantilemez.

Teşhis sırasını şu şekilde kurabilirsin: önce localField ve foreignField değerlerinin BSON türlerini doğrula, sonra $match aşamasının $lookup öncesinde olup olmadığını incele, ardından yabancı koleksiyondaki foreignField indeksini kontrol et ve son olarak explain() çıktısında kullanılan yürütme planını değerlendir.

db.ogrenciler.explain("executionStats").aggregate([
  { $match: { durum: "aktif" } },
  {
    $lookup: {
      from: "kurslar",
      localField: "kursId",
      foreignField: "_id",
      as: "kursBilgisi"
    }
  }
])

explain() çıktısı, MongoDB'nin işlem hattını nasıl planladığını ve indeks kullanımının plan içinde nasıl göründüğünü incelemeye yardımcı olur.

Pipeline kullanan gelişmiş $lookup ne zaman gerekir?

Temel localField ve foreignField eşleşmesine ek olarak hedef koleksiyondaki belgeleri filtrelemek veya birden fazla koşula göre eşleştirmek gerektiğinde, pipeline kullanan $lookup biçimi tercih edilebilir. Bu yapıda let ile dış koleksiyondaki belgeden alınacak alanlar değişken olarak tanımlanır ve işlem hattındaki özellikle $match aşamasında bu değişkenlere $$degiskenAdi biçimiyle erişilir. $match içinde dış belgenin değişkenlerini kullanmak için $expr gerekir. Böylece yalnızca aynı anahtara sahip belgeleri getirmek yerine, eşleşen belgelerin durumuna, tarihine veya başka alanlarına bağlı ek koşullar uygulanabilir. pipeline içindeki aşamalar dış belgenin alanlarına doğrudan erişemediği için bu aktarımın let üzerinden yapılması gerekir. $out ve $merge aşamaları ise bu işlem hattında kullanılamaz.

Sık Sorulan Sorular

$lookup ile $unwind arasındaki fark nedir?

$lookup, iki koleksiyon arasında eşleşme kurarak sonucu mevcut belgeye bir dizi alanı olarak ekler. $unwind ise bu dizinin elemanlarını ayrı belgeler hâline getirir. Bu nedenle $lookup birleştirme işlemini yaparken, $unwind birleştirme sonucundaki dizi yapısını düzleştirmek için kullanılır.

$lookup eşleşme bulamadığında neden boş dizi döndürür?

$lookup her giriş belgesini korur ve eşleşen yabancı belgeleri yeni bir dizi alanında toplar. Uygun belge bulunmadığında bu alanın değeri boş dizi olur; böylece ana koleksiyondaki belge sonuçtan çıkarılmadan ilişkili kaydın bulunmadığı anlaşılır.

localField ve foreignField türleri farklıysa neden sonuç gelmez?

localField ile foreignField karşılaştırması eşitlik temeline dayanır ve değerlerin yalnızca görünüşte aynı olması yeterli değildir. Örneğin bir tarafta ObjectId, diğer tarafta metin olarak saklanan aynı kimlik değeri bulunuyorsa MongoDB bunları aynı türde eşleşen değerler olarak değerlendirmeyebilir. Alan adlarını, değerleri ve BSON türlerini birlikte kontrol etmek gerekir.

$lookup yavaş çalışıyorsa hangi kontroller yapılmalıdır?

$lookup yavaş çalıştığında önce eşleşme alanlarının doğru seçilip seçilmediği, veri türlerinin uyumlu olup olmadığı ve hedef koleksiyonda uygun indeks bulunup bulunmadığı incelenmelidir. Ardından işlem hattındaki erken filtreleme aşamaları, gereksiz belge alanlarının taşınması ve explain() çıktısındaki sorgu planı değerlendirilmelidir.

$lookup ile iki koleksiyonu birleştirirken önce basit eşitlik eşleşmesini, ek koşullar gerektiğinde ise let, pipeline ve $expr yapısını kullanabilirsin. Sonuçları yorumlarken dizi yapısını ve veri türlerini kontrol etmek, sorgunun beklenen belgeleri getirmesi için temel adımdır.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin, yazılım geliştirici ve eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; İzmir Ekonomi Üniversitesi'ni bölüm birincisi ve yüksek şeref öğrencisi olarak tamamladı. Bugün yalnızca eğitim vermekle kalmıyor, sektörde aktif olarak yazılım projeleri geliştiriyor ve gerçek dünya deneyimini birebir derslerine taşıyor. Ezberden uzak, mühendislik zihniyetini merkeze alan sürdürülebilir öğrenme sistemleri tasarlayarak sorgulayan, üreten ve problem çözebilen yeni nesil yazılımcılar yetiştiriyor.

Sektörel Deneyim & Projeler

  • Ticarify Entegrasyon Yazılım logosu CEO Ticarify Entegrasyon YazılımPazaryerleri ve e-ticaret sitelerine otomatik e-fatura kesimi, sipariş ve kargo takibi hizmetleri sunan e-Dönüşüm platformunun API mimarisini ve yazılım ekibini yönetmektedir.
  • Benim Düğünüm logosu CEO Benim DüğünümDijital etkinlik ve anı paylaşım platformu.
  • Siberdizayn logosu Yazılım Ekibi Lideri SiberdizaynYüksek anlık oyuncu trafiğine sahip oyun kontrol panelleri ve sunucu altyapıları geliştiren yazılım ekibine liderlik etmektedir.
  • MEDYOGRAFYA 360° Dijital Çözümler logosu Dijital Strateji Lideri MEDYOGRAFYA 360° Dijital ÇözümlerŞirketlerin dijital çözümlerde uzun vadede nasıl ilerlemesi gerektiği ve dijital dönüşüm süreçlerinin yönetilmesine destek olmaktadır.
  • İzmir Ekonomi Üniversitesi logosu Danışma Kurulu Üyesi İzmir Ekonomi ÜniversitesiMezun olduğu üniversitesinde, Bilgisayar Programcılığı bölümünün akademik müfredatını güncel sektör ihtiyaçlarına göre şekillendirmek adına Danışma Kurulu'nda görev almaktadır.
WhatsApp Hemen Ara