MongoDB 聚合 -- 連表查詢


在使用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
  }
}]);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM