MongoDB查询内嵌数组(限定返回符合条件的数组中的数据)(1)
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/
-
$unwind:
https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#pipe._S_unwind
大概意思就是将数组中的每一个元素转为每一条文档 -
$match:
https://docs.mongodb.com/manual/reference/operator/aggregation/match/
简单的过滤文档,条件查询。 - $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
总结
- 之前查询内嵌数组时,采用的方法是将整条document查询出来之后,在对内嵌数组进行代码过滤。只是觉得这种查询方式并没有用到mongodb的其他的一些方法,还是需求驱动学习。
- 学习一个新的东西建议从官方文档开始学习。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?