NodeJs操作MongoDB之多表查询($lookup)与常见问题

一,方法介绍

aggregate()方法来对数据进行聚合操作。aggregate()方法的语法如下

1 aggregate(operators,[options],callback)

operators参数是如表1所示的聚合运算符的数组,它允许你定义对数据执行什么汇总操作。options参数允许你设置readPreference属性,它定义了从哪里读取数据。callback参数是接受err和res

$lookup:可以做多表查询

  {
$lookup://$lookup是如果涉及关联"_id",注意两个字段的类型,用string类型匹配ObjectId类型是没有结果的
{
from: 'User', // 右集合
localField: 'UserId', // 左集合 join 字段 数据类型得统一
foreignField: '_id', // 右集合 join 字段 数据类型得统一
as: 'fromRole', // 新生成字段(类型array)
},
},

$match:通过使用query对象运算符来过滤文档集。

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

$unwind:unwind方法会将数组解开,每条包含数组中的一个值。

 { $unwind: "$fromRole" },//数据打散

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

$project:通过重命名,添加或删除字段重塑文档。你也可以重新计算值,并添加子文档。

 //以下是包括title并排除name的例子:
{$project:{title:1,name:0}}
//以下是把name重命名为title的例子:
{$project{title:"$name"}}
//下面是添加一个新的total字段,并用price和tax字段计算它的值的例子:
{$project{total:{$add:["$price","$tax"]}}}

$limit:用来限制MongoDB聚合管道返回的文档数。

 {$limit:5}//查询五条

$skip:指定处理聚合操作的下一个管道前跳过的一些文档。

和limit()以及skip()的写法也是一样的。

 { $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,表结构

 "User": {
"Code": "string",
"Name": "string",
"Email": "string",
"Phone": "string",
"Password": "string",
"IsEnable": "bool",
"LoginTime": "date",
"CreateTime": "date",
"UpdateTime": "date"
},

2,插入的数据

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

2.2,角色集合(表)Role

1,表结构

  "Role": {
"Code": "string",
"Name": "string",
"Description": "string",
"CreateTime": "date"
},

2,插入的数据

 {
"_id" : ObjectId("5c42cd8fa450b70a55efdf7e"),
"Code" : "yingqiRole",
"Name" : "yingqi角色",
"Description" : "yingqi角色",
"CreateTime" : ISODate("2019-01-19T03:56:18.966Z")
},
{
"_id" : ObjectId("5c4564848e297d394920f380"),
"Code" : "adminRole",
"Name" : "管理员角色",
"Description" : "管理员角色",
"CreateTime" : ISODate("2019-01-21T03:56:18.966Z")
}

2.3,用户与角色关系集合(表)RoleToUser

1,表结构

   "RoleToUser": {
"RoleId": "objectId",//角色表主键_id
"UserId": "objectId",//用户表主键_id
"CreateTime": "date"
}

2,插入的数据

 {
"_id" : ObjectId("5c42cec9a450b70a55efe01a"),
"RoleId" : ObjectId("5c42cd8fa450b70a55efdf7e"),
"UserId" : ObjectId("5c429fe2c2462128fccc569b"),
"CreateTime" : ISODate("2019-01-19T03:56:18.966Z")
},
{
"_id" : ObjectId("5c4564508e297d394920f363"),
"RoleId" : ObjectId("5c4564848e297d394920f380"),
"UserId" : ObjectId("5c429fe2c2462128fccc569b"),
"CreateTime" : ISODate("2019-01-21T03:56:18.966Z")
}

三,聚合查询与接口抛出

3.1,聚合查询方法封装

 /**
* 聚合查询 查询多条数据 多表查询
* @param table_name 表名
* @param pipeLine 管道 [{$lookup: {
from:'表名', // 右集合
localField: 'UserId', // 左集合 join 字段 数据类型得统一
foreignField: '_id', // 右集合 join 字段 数据类型得统一
as: 'fromUser', // 新生成字段(类型array)
}}
,{$match:{"_id:''}},
{ $unwind: "$fromUser" },//数据打散
]
* @param callback 回调方法
*/
MongoDbAction.queryAggregateMultiTable = function (table_name, pipeLine, callback) {
var node_model = this.getConnection(table_name);
if (!node_model || node_model.message) {
if (callback) callback(1, node_model)
} else {
node_model.aggregate(pipeLine)
.exec(function (err, res) {
if (err) {
if (callback) callback(err);
} else {
if (callback) callback(null, res);
}
});
}
};

3.2,连接查询并抛出接口

 //聚合查询数据 多表连接查询 根据用户id获取角色信息
router.put('/user/getRoleInfoByUserId', function (req, res) {
var tableName = req.body.tableName;//'User'
var singleId = req.body.Code;
let conditions = {
UserId:mongoose.Types.ObjectId(singleId)
//_id:{$type:3}
}
let data = {
httpCode: 200,
message: "查询成功!",
status: 1,
data: null,
}
let pipeLine = [
{
$lookup:
{
from: 'User', // 右集合
localField: 'UserId', // 左集合 join 字段 数据类型得统一
foreignField: '_id', // 右集合 join 字段 数据类型得统一
as: 'fromUser', // 新生成字段(类型array)
},
},
{
$lookup:
{
from: 'Role', // 右集合
localField: 'RoleId', // 左集合 join 字段 数据类型得统一
foreignField: '_id', // 右集合 join 字段 数据类型得统一
as: 'fromRole', // 新生成字段(类型array)
}
},
{ $match: conditions },
{ $unwind: "$fromUser" },
{ $unwind: "$fromRole" },//数据打散
]
MongoDbAction.queryAggregateMultiTable(tableName, pipeLine, function (err, result) {
if (!err) {
data.data = result
res.status(data.httpCode).json(data);
} else {
data.status = 0
data.message = "未查询到数据!"
data.data = result
res.status(data.httpCode).json(data);
}
});
})

3.3,查询结果

查询的条件

查询的结果,已使用unwind方法会将数组解开

四,常见问题

1,$match是如果涉及到"_id",直接传入是没有结果返回的,这是坑1

解决思路:使用aggregate()方法的$match过滤,数据类型必须统一

解决办法:使用mongoose将字符串转成ObjectId,mongoose.Types.ObjectId()方法的使用如下:

  let conditions = {
UserId:mongoose.Types.ObjectId(singleId)//aggregate的$match是如果涉及到"_id",注意字段的类型,如果数据库是ObjectId类型,直接传入是没有结果的,需要将传入的string类型转成ObjectId类型才有结果
//_id:{$type:3}
}

2,$lookup是如果涉及到"_id",两字段的类型不统一是没有结果返回的,这是坑2

注意两个字段的类型,用string类型匹配ObjectId类型是没有结果的
解决办法:建立集合时与插入数据时,注意类型统一
  "_id" : ObjectId("5c42cec9a450b70a55efe01a"), "UserId" : ObjectId("5c429fe2c2462128fccc569b"),

NodeJs操作MongoDB之多表查询($lookup)与常见问题的更多相关文章

  1. nodejs操作mongodb

    一.下载地址 https://www.mongodb.com/download-center#community 二.控制台操作mongodb 1.安装完后添加环境变量. 2.在某个根目录下新建dat ...

  2. NodeJs操作MongoDB之分页功能与常见问题

    NodeJs操作MongoDB之分页功能与常见问题 一,方法介绍 1,聚合操作之count count()方法可以查询统计符合条件的集合的总数 db.User.count(<query>) ...

  3. nodejs 操作mongodb, 增删改查

    很久没有学node了,之前书看了一半,今天继续学发现版本问题很坑爹,按书例子执行一堆错误.想学nodejs操作db,百度半天,一堆sb写神马鸟玩儿?简简单单写一大堆还运行不了的.需要代码也是看别人写的 ...

  4. koa 基础(二十一)nodejs 操作mongodb数据库 --- 查询数据

    1.app.js /** * nodejs 操作mongodb数据库 * 1.安装 操作mongodb * cnpm install mongodb --save * 2.引入 mongodb 下面的 ...

  5. koa 基础(二十)nodejs 操作mongodb数据库 --- 新增数据

    1.app.js /** * nodejs 操作mongodb数据库 * 1.安装 操作mongodb * cnpm install mongodb --save * 2.引入 mongodb 下面的 ...

  6. nodejs操作mongodb数据库封装DB类

    这个DB类也算是我经历了3个实际项目应用的,现分享出来,有需要的请借鉴批评. 上面的注释都挺详细的,我使用到了nodejs的插件mongoose,用mongoose操作mongodb其实蛮方便的. 关 ...

  7. 二十六、Nodejs 操作 MongoDb 数据库

    一. 在 Nodejs 中使用 Mongodb 前面的课程我们讲了用命令操作 MongoDB,这里我们看下如何用 nodejs 来操作数据库需要引包: npm install mongodb --sa ...

  8. NodeJS操作MongoDB数据库

    一.node.js对于mongodb的基本操作 1.数据库的开机 首先我们要先对数据库进行开机的操作,建立一个文件夹用于存放数据库文档.如D:\mongo,接下去在cmd当中键入命令-> mon ...

  9. nodejs 操作 mongodb 数据库

    操作手册: npmjs.com 搜索: mongodb 使用官方的  mongodb 包来操作  https://github.com/mongodb/node-mongodb-native      ...

随机推荐

  1. java~gradle构建公用包并上传到仓库~使用私有仓库的包

    在新的项目里使用仓库的包 上一讲中我们说了java~gradle构建公用包并上传到仓库,如何发布公用的非自启动类的包到私有仓库,而这一讲我们将学习如何使用这些包,就像我们使用spring框架里的功能包 ...

  2. [深度应用]·首届中国心电智能大赛初赛开源Baseline(基于Keras val_acc: 0.88)

    [深度应用]·首届中国心电智能大赛初赛开源Baseline(基于Keras val_acc: 0.88) 个人主页--> https://xiaosongshine.github.io/ 项目g ...

  3. LINQ之道

    提到LINQ首先我们要了解什么是委托:委托是一种引用方法的类型.一旦为委托分配了方法,委托将与该方法具有完全相同的行为.也就是说当你委托给一个人办一件事的时候,他就拥有这个能力去实现这件事,同样委托也 ...

  4. 阿里、百度等多家公司Java面试记录与总结

    算算自己大概面试了近十家公司,也拿到了几个Offer,现在面试告一段落,简单总结下面试经验. 我现在主要的方向是Java服务端开发,把遇到的问题和大家分享一下,也谈谈关于技术人员如何有方向的提高自己, ...

  5. MySQL逻辑架构概述

    1.MySQL逻辑架构 MySQL逻辑架构图 MySQL逻辑架构分四层 1.连接层:主要完成一些类似连接处理,授权认证及相关的安全方案. 2.服务层:在 MySQL据库系统处理底层数据之前的所有工作都 ...

  6. react+redux+Instagram

    项目地址:https://github.com/xiaoyuqing/react-redux-instagram,喜欢的话动动手指点点赞^-^ 1.初始化项目 IndexRoute是默认路由 2.增加 ...

  7. Django 使用 locals() 函数

    locals() 函数会以字典类型返回当前位置的全部局部变量. 在 views.py 中添加 from django.shortcuts import render,HttpResponse,rend ...

  8. QQ音乐vkey获取,更新播放url

    QQ音乐接口播放经常换, 最开始 url: `http://ws.stream.qqmusic.qq.com/${musicData.songid}.m4a?fromtag=46` 然后 url:`h ...

  9. PHP的简单跳转提示的实现

    在PHP开发中,尤其是MVC框架或者项目中,会碰到很多跳转情况,比如:登录成功或失败后的跳转等等. 以下以MVC框架开发中为基础,示例讲解: 在基础控制器类中:Conrtoller.class.php ...

  10. 解决laravel Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found 错误

    这个错误的原因来自于没有安装一个依赖库: 官方文档说明如下: Modifying Columns Prerequisites Before modifying a column, be sure to ...