• 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"),
  • 相关阅读:
    “不裁员”才是公司度过艰难时期的聪明选择
    oracle服务端与客户端字符集不同导致中文乱码解决方案
    [转]ABAP数据库操作系列(5)
    [转]ABAP数据库操作系列(2)
    [转]整理的函数
    [转]ABAP数据库操作系列(6)
    [转]ABAP数据库操作系列(7)
    [转]Authoritycheck
    [转]ABAP Program to Display SAP Icons
    [转]ABAP数据库操作系列(3)
  • 原文地址:https://www.cnblogs.com/jackson-yqj/p/10291767.html
Copyright © 2020-2023  润新知