在使用MongoDB存儲數據的時候,我們查詢的時候,有時候難免會需要進行連表查詢。但是MongoDB本身是非關系性數據庫,連表查詢,很多時候,需要我們自己在代碼里手工操作。但是從 MongoDB 3.2 版本過后,我們可以使用 $lookup 進行連表查詢。下面就簡單介紹一下 MongoDB 的 $lookup 的簡單使用。
比如現在我們有兩張表, user 和 order 表。其中 user 表中的字段有 _id、uid、name、age;order 表中的字段有:_id、uid、product、money; 兩張表存儲的數據為:
users = [{
_id: ObjectId("5af2b2c6b138c267e414c072"),
uid: "uid000",
name: "小紅",
age: 26
}, {
_id: ObjectId("5af2b2c6b138c267e414c073"),
uid: "uid001",
name: "小芳",
age: 27
}]
orders = [{ _id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "產品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c072"), uid: "uid000", product: "產品2", money: 200 }, { _id: ObjectId("4af2b2c6b138c267e414c073"), uid: "uid001", product: "產品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c074"), uid: "uid001", product: "產品2", money: 200 }]
假如現在有兩個需求:
查詢用戶信息並且顯示該用戶的總消費金額(用戶名、年齡、總消費金額)
查詢用戶的訂單信息(訂單id、產品、價格、用戶名)
1. 首先來看第一個需求:
這個需求如果我們不考慮連表,只考慮關聯的話,應該是
先查詢出用戶表所有的數據
在訂單表中求出每一個用戶的消費總金額
遍歷用戶和訂單數據,然后一一通過 uid 進行匹配對應。
如果按照我們的數據庫連表來說:那應該是我們查詢 user 表關聯到 order 表,然后分組根據 uid 統計求和;下面來看一看具體的實現方式。
1.1 連表查詢
db.user.aggregate([{ $lookup: { // 左連接 from: "order", // 關聯到order表 localField: "uid", // user 表關聯的字段 foreignField: "uid", // order 表關聯的字段 as: "orders" } }]);
這個時候出來的結果應該為:
users = [{ _id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小紅", age: 26, orders: [{ _id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "產品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c072"), uid: "uid000", product: "產品2", money: 200 }] }, { _id: ObjectId("5af2b2c6b138c267e414c073"), uid: "uid001", name: "小芳", age: 27, orders: [{ _id: ObjectId("4af2b2c6b138c267e414c073"), uid: "uid001", product: "產品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c073"), uid: "uid001", product: "產品1", money: 200 }] }]
1.2 拆分 orders 數組
{ $unwind: { // 拆分子數組 path: "$orders", preserveNullAndEmptyArrays: true // 空的數組也拆分 } }
這個時候的數據結果應該是這樣的
[{ _id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小紅", age: 26, orders: { _id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "產品1", money: 100 } }, { _id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小紅", age: 26, orders: { _id: ObjectId("4af2b2c6b138c267e414c072"), uid: "uid000", product: "產品2", money: 200 } } …… ]
1.3 分組求和並返回字段數據
{ $group: { // 分組查詢 _id: "$_id", name: { $first: "$name" }, age: { $first: "$age" }, money: {$sum: "$orders.money"} } }
這樣就查詢出了我們所需要的數據。將代碼總結一下為:
db.user.aggregate([{ $lookup: { // 左連接 from: "order", // 關聯到order表 localField: "uid", // user 表關聯的字段 foreignField: "uid", // order 表關聯的字段 as: "orders" } }, { $unwind: { // 拆分子數組 path: "$orders", preserveNullAndEmptyArrays: true // 空的數組也拆分 } }, { // 分組求和並返回 $group: { // 分組查詢 _id: "$_id", name: { $first: "$name" }, age: { $first: "$age" }, money: {$sum: "$orders.money"} } }]);
2. 查詢用戶的訂單信息
2.1 連表查詢
這個時候的連表是 order 表 跟 user 表關聯(上一個是 user 表 和 order 表關聯)
{ $lookup: { from: "users", localField: "openid", foreignField: "openid", as: "u" } }
2.2 拆分子數組
{ $unwind: "$u" }
2.3 只返回需要的字段
將 user 中需要返回的字段,提到子目錄來
{$addFields: { name: "$u.name" }}
2.4 返回最終需要的字段結果
{ $project: { _id: 1, product: 1, money: 1, name: 1 } }
最終的代碼為:
db.order.aggregate([{ $lookup: { from: "users", localField: "openid", foreignField: "openid", as: "u" } }, { $unwind: "$u" }, { $addFields: { name: "$u.name" } }, { $project: { _id: 1, product: 1, money: 1, name: 1 } }]);
雖然在 MongoDB 3.2 后我們能夠進行連表查詢了,方便了很多。但是其實 MongoDB
本身是非關系性數據庫。如果需要進行頻繁的這種連表查詢,我們可以考慮優化我們的數據庫表。比如在訂單表里面,每一條的訂單記錄都把我們的用戶信息放進去。
[{ _id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "產品1", money: 100, user: { _id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小紅", age: 26 } }, { _id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "產品1", money: 100, user: { _id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小紅", age: 26 } }]
這個時候,在實現兩個需求就很簡單了:
// 1. 查詢用戶信息並且顯示該用戶的總消費金額(用戶名、年齡、總消費金額) db.order.aggregate([{ // 根據 uid 求和 $group: { _id: '$user.uid', money: { $sum: "$money" }, name: { $first: "$user.name" }, age: { $first: "$user.age" } } }]); // 2. 查詢用戶的訂單信息(訂單id、產品、價格、用戶名) db.order.aggregate([{ {$addFields: { name: "$user.name" }} }, { // 根據 uid 求和 $project: { _id: 1, money: 1, product: 1, name: 1 } }]);