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 }
View Code

 

查询条件是 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 }
View Code

 

但大多是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 }
View Code

 

$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 }
View Code

 

终于得到我想要的结果了 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 }
View Code

 

明明是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/

  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/ 
    修改输入文档的结构

执行命令:

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 }
View Code

 

这样的一个结果就是我们想要的。感兴趣的同学可以分别执行下这个三个操作(比较看看三个结果有什么不同),你就能理解 $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}}])
View Code

 

总结

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

 

posted @ 2019-05-28 15:01  php、凯  阅读(1754)  评论(0编辑  收藏  举报