NodeJs操作MongoDB之多表查詢($lookup)與常見問題


NodeJs操作MongoDB之多表查詢($lookup)與常見問題

一,方法介紹

aggregate()方法來對數據進行聚合操作。aggregate()方法的語法如下

1 aggregate(operators,[options],callback)

operators參數是如表1所示的聚合運算符的數組,它允許你定義對數據執行什么匯總操作。options參數允許你設置readPreference屬性,它定義了從哪里讀取數據。callback參數是接受err和res

$lookup:可以做多表查詢

1  {
2             $lookup://$lookup是如果涉及關聯"_id",注意兩個字段的類型,用string類型匹配ObjectId類型是沒有結果的
3             {
4                 from: 'User',            // 右集合
5                 localField: 'UserId',    // 左集合 join 字段 數據類型得統一
6                 foreignField: '_id',         // 右集合 join 字段 數據類型得統一
7                 as: 'fromRole',           // 新生成字段(類型array)   
8             },
9         },

$match:通過使用query對象運算符來過濾文檔集。

1 {$match:{"UserId:'5c429fe2c2462128fccc569b'}}

$unwind:unwind方法會將數組解開,每條包含數組中的一個值。

1 { $unwind: "$fromRole" },//數據打散

$lookup多表查詢一起使用,$加$lookup as 的值(新生成字段)

$project:通過重命名,添加或刪除字段重塑文檔。你也可以重新計算值,並添加子文檔。

1 //以下是包括title並排除name的例子:
2 {$project:{title:1,name:0}}
3 //以下是把name重命名為title的例子:
4 {$project{title:"$name"}}
5 //下面是添加一個新的total字段,並用price和tax字段計算它的值的例子:
6 {$project{total:{$add:["$price","$tax"]}}}

$limit:用來限制MongoDB聚合管道返回的文檔數。

1 {$limit:5}//查詢五條

$skip:指定處理聚合操作的下一個管道前跳過的一些文檔。

和limit()以及skip()的寫法也是一樣的。

1 { $skip : 5 }//跳過五條 從0開始

$sort:將輸入文檔排序后輸出。

排序指定一個帶有field(需要排序的字段名):<sort_order>屬性的對象,其中<sort_order>為1表示升序,而-1表示降序

$sort和我們find()中排序的寫法也是一樣的。

$group:將集合中的文檔分組,可用於統計結果。

 把文檔分成一組新的文檔用於在管道中的下一級。新對象的字段必須在$group對象中定義。

  1. $addToSet 返回一組文檔中所有文檔所選字段的全部唯一值的數組。例如:colors:{$addToSet:"color"}
  2. $first 返回一組文檔中一個字段的第一個值。例如:firstValue:{$first:"$value"}
  3. $last 返回一組文檔中一個字段的最后一個值。例如:lastValue:{$last:"$value"}
  4. $max 返回一組文檔中一個字段的最大值。例如:maxValue:{$max:"$value"}
  5. $min 返回一組文檔中一個字段的最小值。例如:minValue:{$min:"$value"}
  6. $avg 返回一組文檔中以個字段的平均值。例如:avgValue:{$avg:"$value"}
  7. $push 返回一組文檔中所有文檔所選字段的全部值的數組。例如:username:{$push:"$username"}
  8. $sum 返回一組文檔中以個字段的全部值的總和。例如:total:{$sum:"$value"}

可用在聚合表達式的字符串和算術運算符

  1. $add:計算數值的總和。例如:valuePlus5:{$add:["$value",5]}
  2. $divide:給定兩個數值,用第一個數除以第二個數。例如:valueDividedBy5:{$divide:["$value",5]}
  3. $mod:取模。例如:{$mod:["$value",5]}
  4. $multiply:計算數值數組的乘積。例如:{$multiply:["$value",5]}
  5. $subtract:給定兩個數值,用第一個數減去第二個數。例如:{$subtract:["$value",5]}
  6. $concat:連接兩個字符串 例如:{$concat:["str1","str2"]}
  7. $strcasecmp:比較兩個字符串並返回一個整數來反應比較結果。例如 {$strcasecmp:["$value","$value"]}
  8. $substr:返回字符串的一部分。例如:hasTest:{$substr:["$value","test"]}
  9. $toLower:將字符串轉化為小寫。
  10. $toUpper:將字符串轉化為大寫

二,表結構與數據

2.1,用戶集合(表)User

1,表結構

 1 "User": {
 2         "Code": "string",
 3         "Name": "string",
 4         "Email": "string",
 5         "Phone": "string",
 6         "Password": "string",
 7         "IsEnable": "bool",
 8         "LoginTime": "date",
 9         "CreateTime": "date",
10         "UpdateTime": "date"
11 },

2,插入的數據

 1 {
 2     "_id" : ObjectId("5c429fe2c2462128fccc569b"),
 3     "Code" : "1234567@qq.com",
 4     "Name" : "jackson影琪",
 5     "Email" : "123456@qq.com",
 6     "Phone" : "15454545454",
 7     "Password" : "5f4dcc3b5aa765d61d8327deb882cf99",
 8     "IsEnable" : true,
 9     "CreateTime" : ISODate("2019-01-19T03:56:18.966Z")
10 }

2.2,角色集合(表)Role

1,表結構

1  "Role": {
2         "Code": "string",
3         "Name": "string",
4         "Description": "string",
5         "CreateTime": "date"
6     },

2,插入的數據

 1 {
 2     "_id" : ObjectId("5c42cd8fa450b70a55efdf7e"),
 3     "Code" : "yingqiRole",
 4     "Name" : "yingqi角色",
 5     "Description" : "yingqi角色",
 6     "CreateTime" : ISODate("2019-01-19T03:56:18.966Z")
 7 },
 8 {
 9     "_id" : ObjectId("5c4564848e297d394920f380"),
10     "Code" : "adminRole",
11     "Name" : "管理員角色",
12     "Description" : "管理員角色",
13     "CreateTime" : ISODate("2019-01-21T03:56:18.966Z")
14 }

2.3,用戶與角色關系集合(表)RoleToUser

1,表結構

1   "RoleToUser": {
2         "RoleId": "objectId",//角色表主鍵_id
3         "UserId": "objectId",//用戶表主鍵_id
4         "CreateTime": "date"
5     }

2,插入的數據

 1 {
 2     "_id" : ObjectId("5c42cec9a450b70a55efe01a"),
 3     "RoleId" : ObjectId("5c42cd8fa450b70a55efdf7e"),
 4     "UserId" : ObjectId("5c429fe2c2462128fccc569b"),
 5     "CreateTime" : ISODate("2019-01-19T03:56:18.966Z")
 6 },
 7 {
 8     "_id" : ObjectId("5c4564508e297d394920f363"),
 9     "RoleId" : ObjectId("5c4564848e297d394920f380"),
10     "UserId" : ObjectId("5c429fe2c2462128fccc569b"),
11     "CreateTime" : ISODate("2019-01-21T03:56:18.966Z")
12 }

三,聚合查詢與接口拋出

3.1,聚合查詢方法封裝

 1 /**
 2  * 聚合查詢 查詢多條數據 多表查詢
 3  * @param table_name 表名
 4  * @param pipeLine 管道 [{$lookup: {
 5                         from:'表名',            // 右集合
 6                         localField: 'UserId',    // 左集合 join 字段 數據類型得統一
 7                         foreignField: '_id',         // 右集合 join 字段 數據類型得統一
 8                         as: 'fromUser',           // 新生成字段(類型array)   
 9                     }}
10                     ,{$match:{"_id:''}},
11                      { $unwind: "$fromUser" },//數據打散
12                 ]
13  * @param callback 回調方法
14  */
15 MongoDbAction.queryAggregateMultiTable = function (table_name, pipeLine, callback) {
16     var node_model = this.getConnection(table_name);
17     if (!node_model || node_model.message) {
18         if (callback) callback(1, node_model)
19     } else {
20         node_model.aggregate(pipeLine)
21             .exec(function (err, res) {
22                 if (err) {
23                     if (callback) callback(err);
24                 } else {
25                     if (callback) callback(null, res);
26                 }
27             });
28     }
29 };

3.2,連接查詢並拋出接口

 1 //聚合查詢數據 多表連接查詢 根據用戶id獲取角色信息
 2 router.put('/user/getRoleInfoByUserId', function (req, res) {
 3     var tableName = req.body.tableName;//'User'
 4     var singleId = req.body.Code;
 5     let conditions = {
 6         UserId:mongoose.Types.ObjectId(singleId)
 7         //_id:{$type:3}
 8     }
 9     let data = {
10         httpCode: 200,
11         message: "查詢成功!",
12         status: 1,
13         data: null,
14     }
15     let pipeLine = [
16         {
17             $lookup:
18             {
19                 from: 'User',            // 右集合
20                 localField: 'UserId',    // 左集合 join 字段 數據類型得統一
21                 foreignField: '_id',         // 右集合 join 字段 數據類型得統一
22                 as: 'fromUser',           // 新生成字段(類型array)   
23             },
24         },
25         {
26             $lookup:
27             {
28                 from: 'Role',            // 右集合
29                 localField: 'RoleId',    // 左集合 join 字段 數據類型得統一
30                 foreignField: '_id',         // 右集合 join 字段 數據類型得統一
31                 as: 'fromRole',           // 新生成字段(類型array)
32             }
33         },
34         { $match: conditions },
35         { $unwind: "$fromUser" },
36         { $unwind: "$fromRole" },//數據打散
37 ]
38     MongoDbAction.queryAggregateMultiTable(tableName, pipeLine, function (err, result) {
39         if (!err) {
40             data.data = result
41             res.status(data.httpCode).json(data);
42         } else {
43             data.status = 0
44             data.message = "未查詢到數據!"
45             data.data = result
46             res.status(data.httpCode).json(data);
47         }
48     });
49 })

3.3,查詢結果

查詢的條件

查詢的結果,已使用unwind方法會將數組解開

四,常見問題

1,$match是如果涉及到"_id",直接傳入是沒有結果返回的,這是坑1

解決思路:使用aggregate()方法的$match過濾,數據類型必須統一

解決辦法:使用mongoose將字符串轉成ObjectId,mongoose.Types.ObjectId()方法的使用如下:

1  let conditions = {
2         UserId:mongoose.Types.ObjectId(singleId)//aggregate的$match是如果涉及到"_id",注意字段的類型,如果數據庫是ObjectId類型,直接傳入是沒有結果的,需要將傳入的string類型轉成ObjectId類型才有結果
3         //_id:{$type:3}
4     }

2,$lookup是如果涉及到"_id",兩字段的類型不統一是沒有結果返回的,這是坑2

注意兩個字段的類型,用string類型匹配ObjectId類型是沒有結果的
解決辦法:建立集合時與插入數據時,注意類型統一
1  "_id" : ObjectId("5c42cec9a450b70a55efe01a"), "UserId" : ObjectId("5c429fe2c2462128fccc569b"),

 


免責聲明!

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



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