MongoDB中$match搭配$gte/$lte查数组失效及聚合数组长度问题
问题1:$match结合$gte/$lte查询数组无结果
你提到用$match阶段结合$gte和$lte操作符查询数组时没效果,这其实是因为MongoDB对数组字段直接用范围操作符的逻辑和你预期的不一样。
当你直接在$match里写{endorsedBy: {$gte: X, $lte: Y}},MongoDB会把整个数组按字典序和X、Y比较,而不是检查数组里是否存在某个元素落在[X,Y]区间里。比如数组[5, 15]和$gte:10比较时,MongoDB会先拿第一个元素5和10比,5<10,所以整个数组会被判定为小于10,不会匹配,哪怕数组里有15符合条件。
解决方案
用$elemMatch操作符来指定数组元素的范围条件,它会专门检查数组中是否至少有一个元素满足所有指定的条件:
db.users.aggregate([ { $match: { endorsedBy: { $elemMatch: { $gte: 10, // 替换成你的下限值 $lte: 20 // 替换成你的上限值 } } } } ])
如果你的数组存储的是字符串或者其他类型,这个逻辑同样适用,只要范围操作符对该类型有效就行。
问题2:聚合中查询数组长度大于1失效,但find()正常
你说在find()里能正常查询数组长度大于1的文档,但聚合里不行,大概率是你在聚合的$match阶段用了错误的语法——直接把$size和$gt组合起来用了,比如{followings: {$size: {$gt: 1}}},这在MongoDB里是不允许的,因为$size只能接受一个固定的数值参数,不能和比较操作符搭配。
而find()里你可能用了$where或者$expr来实现(比如db.users.find({$expr: {$gt: [{$size: "$followings"}, 1]}})),所以能正常工作,但聚合里如果没正确使用$expr就会失效。
解决方案1(推荐,MongoDB 3.6+支持)
在$match阶段用$expr来嵌入聚合表达式,这样就能先计算数组长度再做比较:
db.users.aggregate([ { $match: { $expr: { $gt: [{$size: "$followings"}, 1] } } } ])
解决方案2(兼容旧版本MongoDB)
如果你的MongoDB版本低于3.6,不支持$expr,可以先通过$addFields阶段计算数组的长度,再用$match过滤:
db.users.aggregate([ { $addFields: { followingsCount: {$size: "$followings"} } }, { $match: { followingsCount: {$gt: 1} } } ])
这样就能正确筛选出followings数组长度大于1的文档了,比如如果有一个文档的followings是["user1", "user2"],就会被匹配到。
内容的提问来源于stack exchange,提问作者Ashish Choudhary

