MongoDB查询内嵌数组(限定返回符合条件的数组中的数据)(1)
原文地址:https://blog.csdn.net/bicheng4769/article/details/79579830
项目背景
最近在项目中使用mongdb来保存压测结果中的监控数据,那么在获取监控数据时,遇到这样一个问题: 一个doucument中包含一个内嵌数组,其中内嵌数组也是分成好几类的数组(可以通过标识判断),那么我只需要返回特定的数组,而不是返回内嵌数组的所有数据。
原始数据:
1 { 2 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 3 "addTime" : ISODate("2018-03-16T03:05:04.363Z"), 4 "tag" : "test", 5 "userInfo" : [ 6 { 7 "name" : "cj", 8 "address" : "江苏", 9 "age" : 24.0, 10 "userTag" : "stu" 11 }, 12 { 13 "name" : "hj", 14 "address" : "江苏", 15 "age" : 26.0, 16 "userTag" : "stu" 17 }, 18 { 19 "name" : "lbl", 20 "address" : "美国", 21 "age" : 22.0, 22 "userTag" : "teach" 23 } 24 ] 25 }
查询条件是 tag =“test” userTag=”teach” 的学生的信息。期望返回的结果如下所示:
1 { 2 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 3 "userInfo" : [ 4 { 5 "name" : "lbl", 6 "address" : "美国", 7 "age" : 22.0, 8 "userTag" : "teach" 9 } 10 ] 11 }
但大多是find 的结果 是这样的
1 db.test.find({"userInfo.userTag":"teach","tag":"test"}) 2 { 3 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 4 "addTime" : ISODate("2018-03-16T03:05:04.363Z"), 5 "tag" : "test", 6 "userInfo" : [ 7 { 8 "name" : "cj", 9 "address" : "江苏", 10 "age" : 24.0, 11 "userTag" : "stu" 12 }, 13 { 14 "name" : "hj", 15 "address" : "江苏", 16 "age" : 26.0, 17 "userTag" : "stu" 18 }, 19 { 20 "name" : "lbl", 21 "address" : "美国", 22 "age" : 22.0, 23 "userTag" : "teach" 24 } 25 ] 26 }
$elemMatch 介绍
其实我们在学习一个新的东西的时候,我建议是去官方文档查看一下,毕竟官方的才是最权威的。官方地址:
https://docs.mongodb.com/manual/reference/operator/projection/elemMatch/#proj._S_elemMatch.
我们可以看到 $elemMatch 是在projections方法下面,projections代表是限制返回字段的方法。
修改后的方法:
1 db.test.find({"userInfo.userTag":"teach","tag":"test"},{"userInfo":{$elemMatch{"userTag":"teach"}}})
执行结果
1 { 2 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 3 "userInfo" : [ 4 { 5 "name" : "lbl", 6 "address" : "美国", 7 "age" : 22.0, 8 "userTag" : "teach" 9 } 10 ] 11 }
终于得到我想要的结果了 hhhhhhhhhhhhhhhhhhhhh。
然后我又想获取userInfo.userTag = “stu” 的数据,很简单啊
1 db.test.find({"userInfo.userTag":"teach","tag":"test"},{"userInfo":{$elemMatch:{"userTag":"stu"}}})
但是结果出人意料:
1 { 2 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 3 "userInfo" : [ 4 { 5 "name" : "cj", 6 "address" : "江苏", 7 "age" : 24.0, 8 "userTag" : "stu" 9 } 10 ] 11 }
明明是2条stu的结果,为什么至返回一条呢? 其实$elemMatch 的定义 在官网中已经说过了,这是原话:
1 The $elemMatch operator limits the contents of an <array> field from the query results to contain only the first element matching the $elemMatch condition.
注意 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/
修改输入文档的结构
执行命令:
1 db.test.aggregate([{"$unwind":"$userInfo"}, 2 {"$match":{"userInfo.userTag":"stu","tag":"test"}}, 3 {"$project":{"userInfo":1}}])
结果
1 /* 1 */ 2 { 3 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 4 "userInfo" : { 5 "name" : "cj", 6 "address" : "江苏", 7 "age" : 24.0, 8 "userTag" : "stu" 9 } 10 } 11 12 /* 2 */ 13 { 14 "_id" : ObjectId("5aab3460353df3bd352e0e15"), 15 "userInfo" : { 16 "name" : "hj", 17 "address" : "江苏", 18 "age" : 26.0, 19 "userTag" : "stu" 20 } 21 }
这样的一个结果就是我们想要的。感兴趣的同学可以分别执行下这个三个操作(比较看看三个结果有什么不同),你就能理解 $unwind、$match、$project 三个方法的作用
1 db.test.aggregate([{"$unwind":"$userInfo"}]) 2 db.test.aggregate([{"$unwind":"$userInfo"},{"$match":{"userInfo.userTag":"stu","tag":"test"}}]) 3 db.test.aggregate([{"$unwind":"$userInfo"},{"$match":{"userInfo.userTag":"stu","tag":"test"}},{"$project":{"userInfo":1}}])
总结
- 之前查询内嵌数组时,采用的方法是将整条document查询出来之后,在对内嵌数组进行代码过滤。只是觉得这种查询方式并没有用到mongodb的其他的一些方法,还是需求驱动学习。
- 学习一个新的东西建议从官方文档开始学习。