iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
自我挑戰組

被MongoDB用Aggregate暴打的後端小菜雞日記系列 第 9

被MongoDB用Aggregate暴打的後端小菜雞日記-day9-$lookup將兩個collection的資料合併(上)

  • 分享至 

  • xImage
  •  

一開始在使用MongoDB的時候,總是在想它不像MySQL關聯資料庫一樣,可以做兩張資料表的join,要把兩個collection的資料,分別搜尋自己組合好麻煩,還是MySQL比較好。

直到我遇見了$lookup,它的作用和關聯資料庫的join類似,可以將兩個collection資料合併再一起,傳遞給下一個pipeline,它的寫法分成兩種,一種是今天要介紹的寫法,比較陽春一點,會把join的collection資料欄位全部加進來。

另一種寫法比較複雜一點,但可以在$lookup內寫pipeline,將要join進來的collection資料先運算,再做join的動作。

此外在進行$lookup,如果兩個collection的資料數量都很多,強烈建議一定要設索引(index),不然搜尋資料的效能會變得超差。

例如:學生的成績這個collection有3000筆資料,而學生基本資料的collection有600筆資料,兩個collection做$lookup搜尋資料,在沒有建立索引的情況下,會搜尋資料3000×600次,十分的沒效率。


假設現在我們有學生成績資料(score)三筆

  { id: 1, student: 1, test: 1, math: 20, english: 45 },
  { id: 2, student: 2, test: 1, math: 30, english: 80 },
  { id: 3, student: 3, test: 1, math: 85, english: 83 },

學生的個人資料(profile)三筆

  { id: 1, name: "小明", sex: 0, seat_number: 23 },
  { id: 2, name: "小王", sex: 0, seat_number: 17 },
  { id: 3, name: "小美", sex: 1, seat_number: 6 }

如果老師想要將兩張資料表合併,製作成績單列印給學生帶回去,會用以下指令。

profile.aggregate([
  {
    $lookup: {
      from: "score", // 從哪一張資料表join資料
      as: "test_score", // join的資料會新增在一個欄位內,as要填就是該欄位的名稱
      localField: "id", // 引用其他collection,必須有哪一個欄位是相等的(本地欄位)
      foreignField: "student_id" // 引用其他collection,必須有哪一個欄位是相等的(外來欄位)
    }
  }
]);

得到的資料會如下

{
    id: 1,
    name: "小明",
    sex: 0,
    seat_number: 23,
    test_score: [{ id: 1, student: 1, test: 1, math: 20, english: 45 }]
  },
  {
    id: 2,
    name: "小王",
    sex: 0,
    seat_number: 17,
    test_score: [{ id: 2, student: 2, test: 1, math: 30, english: 80 }]
  },
  {
    id: 3,
    name: "小美",
    sex: 1,
    seat_number: 6,
    test_score: [{ id: 3, student: 3, test: 1, math: 85, english: 83 }]
  }

join加入的資料會用陣列包裹起來,如果想要讓資料再精簡一點,只取我們想要的欄位,必須在寫一個$project$addFields的pipeline,整理要回傳的資料欄位。

例如:老師成績部分只想保留數學和英文成績,個人資料只想要留下姓名和座號。
我們可以利用$arrayElemAt取得特定陣列的元素,後面接一個陣列,第一個item填要取資料的陣列欄位名稱,第二個item填要取的元素位置。

profile.aggregate([
  {
    $lookup: {
      from: "score",
      as: "test_score",
      localField: "id",
      foreignField: "student_id"
    }
  },
  {
    $$addFields: {
      sore: { $arrayElemAt: ["$test_score", 0] }
    }
  },
  {
    $project: {
      name: 1,
      seat_number: 1,
      math: "$sore.math", // 取得sore物件中math的值
      english: "$sore.english"
    }
  }
]);

回傳的資料會是

  { id: 1, name: "小明", seat_number: 23, math: 20, english: 45 },
  { id: 2, name: "小王", seat_number: 17, math: 30, english: 80 },
  { id: 3, name: "小美", seat_number: 6, math: 85, english: 83 }

ps.如果很想知道每一個pipeline處理完的資料,到底會長怎樣,可以自己console.log()出來看一下。

本篇文章同步放在我的部落格,大家有空可以進來逛逛


上一篇
被MongoDB用Aggregate暴打的後端小菜雞日記-day8-用$group將資料分組(下)
下一篇
被MongoDB用Aggregate暴打的後端小菜雞日記-day10-$lookup將兩個collection的資料合併(下)
系列文
被MongoDB用Aggregate暴打的後端小菜雞日記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言