MongoDB二级Unwind后Match不生效问题及正确实现咨询
MongoDB聚合查询中二级Unwind后的Match条件失效解决办法
你正在开发一款基于MongoDB的软件,现有如下集合文档示例(单条):
{ "_id" : ObjectId("5aef51e0af42ea1b70d0c4dc"), "EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83", "DateTime" : ISODate("2018-05-06T19:05:04.574Z"), "Url" : "test", "Tags" : [ { "Uid" : "E2:02:00:18:DA:40", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:04.574Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-98") }, { "Type" : 2, "Value" : NumberDecimal("-65") } ] }, { "Uid" : "12:3B:6A:1A:B7:F9", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:04.574Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-95") }, { "Type" : 2, "Value" : NumberDecimal("-59") }, { "Type" : 3, "Value" : NumberDecimal("12.939770381907275") } ] } ] }
你执行了如下聚合查询,但发现二级$unwind后的$match条件(过滤Tags.Sensors.Type)并未生效:
db.myCollection.aggregate([ { $unwind: "$Tags" }, { $match: { $and: [ { "Tags.DateTime": { $gte: ISODate("2018-05-06T19:05:02Z"), $lte: ISODate("2018-05-06T19:05:09Z") } }, { "Tags.Uid": { $in: ["C1:3D:CA:D4:45:11"] } } ] } }, { $unwind: "$Tags.Sensors" }, { $match: { "$Tags.Sensors.Type": { $in: [1, 2] } } }, // 这里的写法有问题 { $project: { _id: 0, EndpointId: "$EndpointId", TagId: "$Tags.Uid", Url: "$Url", TagType: "$Tags.Type", Date: "$Tags.DateTime", SensorType: "$Tags.Sensors.Type", Value: "$Tags.Sensors.Value" } } ])
问题原因
你在第二个$match阶段的字段路径前面多了一个$符号!在MongoDB的$match阶段中,指定字段路径时不需要加$——$符号通常用于在$project、$addFields这类阶段中引用字段的值,但$match是直接匹配文档的字段路径,所以这里的"$Tags.Sensors.Type"写法错误,导致MongoDB无法识别目标字段,过滤自然失效。
修正后的聚合查询
只需要把第二个$match的字段路径去掉前缀$即可:
db.myCollection.aggregate([ { $unwind: "$Tags" }, { $match: { $and: [ { "Tags.DateTime": { $gte: ISODate("2018-05-06T19:05:02Z"), $lte: ISODate("2018-05-06T19:05:09Z") } }, { "Tags.Uid": { $in: ["C1:3D:CA:D4:45:11"] } } ] } }, { $unwind: "$Tags.Sensors" }, { $match: { "Tags.Sensors.Type": { $in: [1, 2] } } }, // 去掉前缀$ { $project: { _id: 0, EndpointId: "$EndpointId", TagId: "$Tags.Uid", Url: "$Url", TagType: "$Tags.Type", Date: "$Tags.DateTime", SensorType: "$Tags.Sensors.Type", Value: "$Tags.Sensors.Value" } } ])
更高效的优化方案
从性能角度考虑,我们可以先过滤Sensors数组再执行$unwind,这样能减少$unwind处理的数据量,提升查询效率。具体做法是在第一个$unwind之后,用$addFields配合$filter先把每个Tag下符合条件的Sensors筛选出来,再进行后续操作:
db.myCollection.aggregate([ { $unwind: "$Tags" }, { $match: { $and: [ { "Tags.DateTime": { $gte: ISODate("2018-05-06T19:05:02Z"), $lte: ISODate("2018-05-06T19:05:09Z") } }, { "Tags.Uid": { $in: ["C1:3D:CA:D4:45:11"] } } ] } }, // 先过滤Sensors数组,只保留Type为1或2的元素 { $addFields: { "Tags.Sensors": { $filter: { input: "$Tags.Sensors", cond: { $in: ["$$this.Type", [1, 2]] } } } } }, // 只对非空的Sensors数组执行unwind { $unwind: "$Tags.Sensors" }, { $project: { _id: 0, EndpointId: "$EndpointId", TagId: "$Tags.Uid", Url: "$Url", TagType: "$Tags.Type", Date: "$Tags.DateTime", SensorType: "$Tags.Sensors.Type", Value: "$Tags.Sensors.Value" } } ])
这个方案的优势在于避免了对所有Sensors元素执行$unwind,先过滤再展开能显著减少后续阶段需要处理的数据量,尤其当集合数据量较大时,性能提升会很明显。
内容的提问来源于stack exchange,提问作者Mhmd
相关产品推荐
相关产品推荐

