MongoDB / $lookup (JOIN)

$lookup (JOIN)

MongoDB нь document-based санд хэдий ч $lookup stage ашиглан өөр collection-ийн өгөгдлийг холбох боломжтой. SQL-ийн JOIN-тэй адил зарчим.

$lookup үндсэн хэлбэр

javascript
{
  $lookup: {
    from: "холбогдох collection",
    localField: "одоогийн collection-ийн талбар",
    foreignField: "холбогдох collection-ийн талбар",
    as: "үр дүнг хадгалах талбарын нэр"
  }
}

Жишээ: Захиалга — Хэрэглэгч

javascript
// orders collection:
// { _id: ObjectId("..."), userId: ObjectId("u1"), amount: 50000, product: "Ном" }

// users collection:
// { _id: ObjectId("u1"), username: "bold", email: "bold@example.com" }

db.orders.aggregate([
  {
    $lookup: {
      from: "users", // холбогдох collection
      localField: "userId", // orders-ийн userId
      foreignField: "_id", // users-ийн _id
      as: "userInfo", // үр дүн хадгалах талбар
    },
  },
]);

// Үр дүн:
// {
//   _id: ObjectId("..."),
//   userId: ObjectId("u1"),
//   amount: 50000,
//   product: "Ном",
//   userInfo: [
//     { _id: ObjectId("u1"), username: "bold", email: "bold@example.com" }
//   ]
// }

as талбар нь массив болж ирдэг — тохирох бичлэг нэг ч байсан.

$unwind — массивыг задлах

$lookup-ийн үр дүн массив тул $unwind ашиглан ганц объект болгоно:

javascript
db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "userInfo",
    },
  },
  { $unwind: "$userInfo" }, // массивыг задалж ганц объект болгох
]);

// Үр дүн:
// {
//   _id: ObjectId("..."),
//   amount: 50000,
//   product: "Ном",
//   userInfo: { _id: ObjectId("u1"), username: "bold", email: "bold@example.com" }
//   // массив биш — ганц объект
// }

$project-тэй хослуулах

Lookup-ийн дараа зөвхөн хэрэгтэй талбаруудаа сонго:

javascript
db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "userInfo",
    },
  },
  { $unwind: "$userInfo" },
  {
    $project: {
      _id: 0,
      product: 1,
      amount: 1,
      buyerName: "$userInfo.username",
      buyerEmail: "$userInfo.email",
    },
  },
]);

// Үр дүн:
// { product: "Ном", amount: 50000, buyerName: "bold", buyerEmail: "bold@example.com" }

Nested $lookup

Pipeline дотор $lookup-ийг дахин ашиглаж, гурван collection-ийг холбож болно:

javascript
// orders → users → profiles гэж холбох
db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "user",
    },
  },
  { $unwind: "$user" },
  {
    $lookup: {
      from: "profiles",
      localField: "user._id",
      foreignField: "userId",
      as: "user.profile",
    },
  },
  { $unwind: { path: "$user.profile", preserveNullAndEmpty: true } },
]);

preserveNullAndEmpty: true — тохирох profile олдохгүй бол document-ийг хаяхгүй (SQL-ийн LEFT JOIN-тэй адил).

SQL JOIN-тэй харьцуулах

| SQL | MongoDB | | ------------------------- | ---------------------------------------------------- | | INNER JOIN | $lookup + $unwind | | LEFT JOIN | $lookup + $unwind + preserveNullAndEmpty: true | | JOIN ... ON a.id = b.fk | localField + foreignField | | Үр дүн нэг эгнээ | $unwind хийнэ | | Үр дүн массив | $unwind хийхгүй |

$lookup ашиглах зөвлөмж

MongoDB-д холбогдсон өгөгдлийг embedding (document дотор хадгалах) болон referencing ($lookup) гэсэн хоёр аргаар удирддаг.

$lookup байнга хэрэгтэй болж байвал — тухайн өгөгдлийг embedding хийх нь илүү тохиромжтой эсэхийг бод. Жишээлбэл, захиалгад хэрэглэгчийн нэр, и-мэйлийг шууд хадгалбал lookup хийх шаардлагагүй болно.

javascript
// Embedding жишээ — lookup шаардлагагүй
{
  _id: ObjectId("..."),
  amount: 50000,
  product: "Ном",
  buyer: {
    userId: ObjectId("u1"),
    username: "bold",       // шууд хадгалсан
    email: "bold@example.com"
  }
}

Дараагийн хичээлд:

Schema дизайны зарчмууд — embedding vs referencing хэзээ алийг нь сонгох талаар үзнэ.