引言
MongoDB是一個非關係型資料庫,大受歡迎的原因之一是很方便在多個集合(就是”表”的概念)中進行聯合查詢。
如果你是初學者,那麼你可能會問什麼時候需要聯合查詢。舉個例子,如果你有一個學生集合和一個成績集合,你可能會想要查詢一個學生所有課程的成績。
本文將介紹如何使用MongoDB進行多表聯合查詢,包括內嵌文檔和$lookup操作符兩種方法。
內嵌文檔
內嵌文檔是MongoDB中最基本且最常見的多表聯合查詢方法。
小標題1:內嵌文檔基礎
首先,需要知道如何在一個文檔中嵌入其他文檔。這個操作可以使用Mongo Shell或任何MongoDB操作客戶端。這裡使用Mongo Shell作為演示。
> db.students.insert({ "name": "小明", "age": 20, "score": { "math": 80, "chinese": 90, "english": 85 } })
上面這個例子將一個名為”小明”的學生文檔插入了學生集合中。此文檔有一個嵌套的分數文檔。
接下來,我們將展示如何查詢學生的成績記錄:
> db.students.find({"name": "小明"}) { "_id" : ObjectId("60efa25968ca5ef8843f0c0c"), "name" : "小明", "age" : 20, "score" : { "math" : 80, "chinese" : 90, "english" : 85 } }
很簡單,只需要將查詢條件指定為”name”為”小明”即可。
小標題2:多個文檔的聯合查詢
接下來,我們將學生文檔和成績文檔嵌入一個班級文檔,以此演示如何使用內嵌文檔查詢多個文檔:
> db.classes.insert({ "name": "一年級", "students": [ { "name": "小明", "age": 20, "score": { "math": 80, "chinese": 90, "english": 85 } }, { "name": "小紅", "age": 19, "score": { "math": 78, "chinese": 88, "english": 86 } } ] })
上面這個例子將一個名為”一年級”的班級文檔插入了班級集合中。此文檔有一個嵌套的學生文檔數組,數組裡包含兩個學生文檔,每個學生文檔有一個嵌套的成績文檔。
接下來,我們將展示如何查詢這個班級里所有學生的姓名和成績記錄:
> db.classes.find({}, {"students.name": 1, "students.score": 1}) { "_id" : ObjectId("60efa5c068ca5ef8843f0c0d"), "students" : [ { "name" : "小明", "score" : { "math" : 80, "chinese" : 90, "english" : 85 } }, { "name" : "小紅", "score" : { "math" : 78, "chinese" : 88, "english" : 86 } } ] }
只需將查詢條件指定為一個空文檔,然後指定需要返回的欄位即可。在這個例子中,我們指定了”name”和”score”兩個欄位。
$lookup操作符
$lookup操作符是MongoDB中用於執行多表聯合查詢的最強大且最靈活的方法。
小標題1:單個查詢
首先,我們需要知道如何使用$lookup操作符查詢單個文檔中包含的多個子文檔。這個操作將會使用到餐廳和菜品兩個文檔。
> db.restaurants.insert({ "name": "餐廳1", "dishes": [ { "name": "紅燒肉", "price": 28 }, { "name": "魚香肉絲", "price": 18 } ] })
上面這個例子將一個名為”餐廳1″的餐廳文檔插入了餐廳集合中。此文檔有一個嵌套的菜品文檔數組,數組裡包含兩個菜品文檔。
接下來,我們將展示如何查詢一個餐廳,並將其菜品信息一併返回:
> db.restaurants.aggregate([ { $match: {"name": "餐廳1"} }, { $lookup: { from: "dishes", localField: "dishes.name", foreignField: "name", as: "dishes" } }, { $project: {"dishes.name": 1, "dishes.price": 1} } ])
這是一個比較複雜的查詢,需要使用聚合管道(aggregate pipeline)來實現。首先使用$match操作符找到名稱為”餐廳1″的餐廳文檔,並將其作為管道的第一個操作。
然後,使用$lookup操作符查找”dishes”集合,並將其結果嵌入”restaurants”文檔中。$lookup操作符需要指定四個參數:
- from:需要聯結的文檔集合名
- localField:連接到當前文檔的本地鍵欄位
- foreignField:連接到目標文檔的外鍵欄位
- as:聯結到當前文檔的新鍵名
最後使用$project操作符將$dishes數組中的”name”和”price”欄位投影出來。
小標題2:多個查詢
現在,我們將舉一個更複雜的例子,使用$lookup操作符查詢多個文檔的聯合結果。這個例子將使用到學生、成績和課程三個文檔。
首先,我們需要在Mongo Shell中插入所有的測試文檔:
> db.students.insertMany([ {"name": "小明", "age": 20}, {"name": "小紅", "age": 19} ]) > db.courses.insertMany([ {"name": "math", "credit": 3}, {"name": "chinese", "credit": 3}, {"name": "english", "credit": 2} ]) > db.scores.insertMany([ {"student": "小明", "course": "math", "score": 80}, {"student": "小明", "course": "chinese", "score": 90}, {"student": "小明", "course": "english", "score": 85}, {"student": "小紅", "course": "math", "score": 78}, {"student": "小紅", "course": "chinese", "score": 88}, {"student": "小紅", "course": "english", "score": 86} ])
接下來,我們將展示如何使用$lookup操作符查詢所有學生的所有成績和對應的課程信息:
> db.students.aggregate([ { $lookup: { from: "scores", localField: "name", foreignField: "student", as: "scores" } }, { $unwind: "$scores" }, { $lookup: { from: "courses", localField: "scores.course", foreignField: "name", as: "course_info" } }, { $unwind: "$course_info" }, { $project: {"name": 1, "course": "$course_info.name", "credit": "$course_info.credit", "score": "$scores.score"} } ])
這是一個相對複雜的查詢,以便演示如何在多個MongoDB文檔中執行聯合查詢。
首先,使用$lookup操作符將”students”文檔和”scores”文檔聯結起來。將”students.name”欄位和”scores.student”欄位匹配。聯結結果將插入到”students.scores”數組中。
然後,使用$unwind操作符展開”students.scores”數組,以便進行下一步聯結。這裡使用了兩次$unwind,因為後續還會有一個聯結操作。
接下來,使用$lookup操作符將課程信息加入聯結結果中。將”students.scores.course”欄位和”courses.name”欄位匹配。聯結結果將插入到”students.scores.course_info”數組中。
最後,使用$project操作符來投影出我們想要的結果。在這個例子中,我們將”name”、”course”、”credit”和”score”欄位都投影出來。
結論
本文介紹了MongoDB中的兩種多表聯合查詢方法:內嵌文檔和$lookup操作符。內嵌文檔可以很容易地在一個文檔中嵌入其他文檔,但是只適用於簡單的聯合查詢場景。$lookup操作符則可以在多個MongoDB文檔之間執行更為靈活和複雜的聯合查詢。這些方法都可以用於多個領域,例如學生成績、餐廳菜品和體育比賽信息等。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/240369.html