https://blog.csdn.net/bicheng4769/article/details/79579830

项目背景

最近在项目中使用mongdb来保存压测结果中的监控数据,那么在获取监控数据时,遇到这样一个问题: 一个doucument中包含一个内嵌数组,其中内嵌数组也是分成好几类的数组(可以通过标识判断),那么我只需要返回特定的数组,而不是返回内嵌数组的所有数据。 
原始数据:

{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"addTime" : ISODate("2018-03-16T03:05:04.363Z"),
"tag" : "test",
"userInfo" : [
{
"name" : "cj",
"address" : "江苏",
"age" : 24.0,
"userTag" : "stu"
},
{
"name" : "hj",
"address" : "江苏",
"age" : 26.0,
"userTag" : "stu"
},
{
"name" : "lbl",
"address" : "美国",
"age" : 22.0,
"userTag" : "teach"
}
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

查询条件是 tag =“test” userTag=”teach” 的学生的信息。期望返回的结果如下所示:

{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"userInfo" : [
{
"name" : "lbl",
"address" : "美国",
"age" : 22.0,
"userTag" : "teach"
}
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

但大多是find 的结果 是这样的

db.test.find({"userInfo.userTag":"teach","tag":"test"})
{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"addTime" : ISODate("2018-03-16T03:05:04.363Z"),
"tag" : "test",
"userInfo" : [
{
"name" : "cj",
"address" : "江苏",
"age" : 24.0,
"userTag" : "stu"
},
{
"name" : "hj",
"address" : "江苏",
"age" : 26.0,
"userTag" : "stu"
},
{
"name" : "lbl",
"address" : "美国",
"age" : 22.0,
"userTag" : "teach"
}
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

$elemMatch 介绍

其实我们在学习一个新的东西的时候,我建议是去官方文档查看一下,毕竟官方的才是最权威的。官方地址: 
https://docs.mongodb.com/manual/reference/operator/projection/elemMatch/#proj._S_elemMatch. 
我们可以看到 $elemMatch 是在projections方法下面,projections代表是限制返回字段的方法。 
修改后的方法:

db.test.find({"userInfo.userTag":"teach","tag":"test"},{"userInfo":{$elemMatch{"userTag":"teach"}}})
  • 1

执行结果

{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"userInfo" : [
{
"name" : "lbl",
"address" : "美国",
"age" : 22.0,
"userTag" : "teach"
}
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

终于得到我想要的结果了 hhhhhhhhhhhhhhhhhhhhh。 
然后我又想获取userInfo.userTag = “stu” 的数据,很简单啊

db.test.find({"userInfo.userTag":"teach","tag":"test"},{"userInfo":{$elemMatch:{"userTag":"stu"}}})
  • 1

但是结果出人意料:

{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"userInfo" : [
{
"name" : "cj",
"address" : "江苏",
"age" : 24.0,
"userTag" : "stu"
}
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

明明是2条stu的结果,为什么至返回一条呢? 其实$elemMatch 的定义 在官网中已经说过了,这是原话:

The $elemMatch operator limits the contents of an <array> field from the query results to contain only the first element matching the $elemMatch condition.
  • 1

注意 only the first element 也就是仅仅匹配第一个合适的元素。 
那么 对于数组中只有一个返回元素,我们可以使用$elemMatch来查询,但是对于多个元素$elemMatch 是不适应。

$Aggregation介绍

文档地址:https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/

  1. $unwind: 
    https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#pipe._S_unwind 
    大概意思就是将数组中的每一个元素转为每一条文档

  2. $match: 
    https://docs.mongodb.com/manual/reference/operator/aggregation/match/ 
    简单的过滤文档,条件查询。

  3. $project 
    https://docs.mongodb.com/manual/reference/operator/aggregation/project/ 
    修改输入文档的结构

执行命令:

db.test.aggregate([{"$unwind":"$userInfo"},
{"$match":{"userInfo.userTag":"stu","tag":"test"}},
{"$project":{"userInfo":1}}])
  • 1
  • 2
  • 3

结果

/* 1 */
{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"userInfo" : {
"name" : "cj",
"address" : "江苏",
"age" : 24.0,
"userTag" : "stu"
}
} /* 2 */
{
"_id" : ObjectId("5aab3460353df3bd352e0e15"),
"userInfo" : {
"name" : "hj",
"address" : "江苏",
"age" : 26.0,
"userTag" : "stu"
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这样的一个结果就是我们想要的。感兴趣的同学可以分别执行下这个三个操作(比较看看三个结果有什么不同),你就能理解 $unwind、$match、$project 三个方法的作用

db.test.aggregate([{"$unwind":"$userInfo"}])
db.test.aggregate([{"$unwind":"$userInfo"},{"$match":{"userInfo.userTag":"stu","tag":"test"}}])
db.test.aggregate([{"$unwind":"$userInfo"},{"$match":{"userInfo.userTag":"stu","tag":"test"}},{"$project":{"userInfo":1}}])
  • 1
  • 2
  • 3

总结

  1. 之前查询内嵌数组时,采用的方法是将整条document查询出来之后,在对内嵌数组进行代码过滤。只是觉得这种查询方式并没有用到mongodb的其他的一些方法,还是需求驱动学习。
  2. 学习一个新的东西建议从官方文档开始学习。

结束语:

MongoDB查询内嵌数组(限定返回符合条件的数组中的数据)(1)的更多相关文章

  1. mongodb查询内嵌文档

    mongodb查询内嵌文档   假设有这样一个文档: db.XXX.remove(); db.XXX.insert({"id":1, "members":[{& ...

  2. mongodb 内嵌数组查询问题: 如何限定返回与条件匹配的数组

    原文地址:https://segmentfault.com/q/1010000002943721

  3. mongoskin 是让 Node.js 支持 MongoDB 的内嵌访问层。

    mongoskin 是让 Node.js 支持 MongoDB 的内嵌访问层.

  4. Array 数组filter()方法,遍历数组返回符合条件的一个新数组

    <script> const arr = [ { id: 1, name: "aa", isDone: false }, { id: 2, name: "bb ...

  5. matlab学习笔记12_2创建结构体数组,访问标量结构体,访问非标量结构体数组的属性,访问嵌套结构体中的数据,访问非标量结构体数组中多个元素的字段

    一起来学matlab-matlab学习笔记12 12_2 结构体 创建结构体数组,访问标量结构体,访问非标量结构体数组的属性,访问嵌套结构体中的数据,访问非标量结构体数组中多个元素的字段 觉得有用的话 ...

  6. thymeleaf的内联th:inline(在javascript访问model中的数据)

    thymeleaf模板引擎为前端数据的获取提供了较大的便利,在html标签内可通过th标签加${}表达式访问model里的对象数据.但如果不想通过th标签而是简单地访问model对象数据,或是想在ja ...

  7. MongoDB .Net Driver(C#驱动) - 内嵌数组/嵌入文档的操作(增加、删除、修改、查询(Linq 分页))

    目录 一.前言 1. 运行环境 二.前期准备工作 1. 创建 MongoDBContext MongoDb操作上下文类 2.创建测试类 3.创建测试代码 三.内嵌数组增加元素操作 1.Update.S ...

  8. 学习MongoDB 五: MongoDB查询(数组、内嵌文档)(二)

    一.简介 我们上一篇介绍了db.collection.find()可以实现根据条件查询和指定使用投影运算符返回的字段省略此参数返回匹配文档中的所有字段,我们今天介绍了对数组和内嵌文档的查询操作,尤其是 ...

  9. MongoDB查询或修改内嵌文档

    作为非关系型数据库中的佼佼者,MongoDB一大优势在于能够在一条文档中存储对象类型的数据,适当增加冗余来让数据库更好用.文档中一个对象类型的字段在MongoDB中被称为内嵌文档(Embedded) ...

随机推荐

  1. 谷歌浏览器报错 Active resource loading counts reached to a per-frame

    Active resource loading counts reached to a per-frame limit while the tab is in background. Network ...

  2. 【纪录】Proxychain4 使用部署以及利用 ss 加速下载操作

    我觉得这个方案用来解决 linux 机器上面 apt-get 和 yum 国外源拉去取过慢的问题还是还不错的. 参看下面两个教程. Reference: https://brickyang.githu ...

  3. Jmeter安装与使用(压测)

    一.介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态和动态资源的性能,例如:静态文件,Java Se ...

  4. Google浏览器解决编码乱码问题

    新版google浏览器编码乱码没有设置的入口,怎么办呢?. 步骤一: 可以下载goole的插件,名为charset,下载后的文件名为Charset_v0.4.1 步骤二: google右上角-> ...

  5. 四、docker compose

    docker compose可以方便我们快捷高效地管理容器的启动.停止以及重启等操作,和批量管理容器,它类似于linux下的shell脚本,基于yaml语法,在该文件里我们可以描述应用的架构,比如用什 ...

  6. vue axios 封装(二)

    封装二: http.js import axios from 'axios' import storeHelper from './localstorageHelper' // 全局设置 const ...

  7. ASP.NET MVC和Web API中的Angular2 - 第1部分

    下载源码 - 903.5 KB 内容 第1部分:Visual Studio 2017中的Angular2设置,基本CRUD应用程序,第三方模态弹出控件 第2部分:使用Angular2管道进行过滤/搜索 ...

  8. notepad++上直接运行python文件

    一.打开notepad++,点击语言,选择python,这样就写的是python文件了 二.点击运行>运行:输入cmd /k python "$(FULL_CURRENT_PATH)& ...

  9. 创建iview框架的项目

    http://www.cnblogs.com/jf-67/p/8479176.html 在使用‘vue init webpack my-project’创建项目时,出现了错误 npm ERR! cod ...

  10. Flask 构建微电影视频网站(二)

    搭建前台页面 前台布局搭建 将static中的文件拷贝到项目的static目录下 在app/templates/home下新建home.html,当作基础模板,并修改静态资源链接 <!docty ...