MongoDB中如何查询数组内banana在apple之前的文档?
MongoDB数组元素顺序查询方案
你的集合中文档的fruits数组结构示例如下:
// 符合条件的文档 "fruits": [ {"fruit1": "banana"}, {"fruit2": "apple"}, {"fruit3": "pear"}, {"fruit4": "orange"} ] // 不符合条件的文档1 "fruits": [ {"fruit2": "apple"}, {"fruit4": "orange"}, {"fruit1": "banana"}, {"fruit3": "pear"} ] // 不符合条件的文档2 "fruits": [ {"fruit3": "pear"}, {"fruit2": "apple"}, {"fruit4": "orange"}, {"fruit1": "banana"} ]
需求为找出fruits数组内banana出现在apple之前的文档,MongoDB不支持直接用类似if (fruits.indexOf('banana') < fruits.indexOf('apple')) return true;的语法,但可以通过以下两种方法实现需求:
方法一:聚合管道结合$indexOfArray
MongoDB 3.4及以上版本提供了$indexOfArray操作符,可获取元素在数组中的索引。由于你的数组元素是嵌套对象,需要先提取所有水果名称组成新数组,再比较索引:
db.collection.aggregate([ // 提取fruits数组中的所有水果名称,生成临时数组fruitNames { $addFields: { fruitNames: { $map: { input: "$fruits", as: "item", in: { $arrayElemAt: [{ $objectToArray: "$$item" }, 1] } } } } }, // 筛选banana索引小于apple索引的文档 { $match: { $expr: { $lt: [ { $indexOfArray: ["$fruitNames", "banana"] }, { $indexOfArray: ["$fruitNames", "apple"] } ] } } }, // 移除临时字段(可选) { $project: { fruitNames: 0 } } ])
方法二:使用$expr+$reduce遍历数组
如果需要处理更复杂的逻辑,可通过$reduce遍历数组,标记是否已遇到banana,再判断apple出现的时机:
db.collection.find({ $expr: { $reduce: { input: "$fruits", initialValue: { seenBanana: false, valid: true }, in: { $cond: [ // 如果已经见过banana后又遇到apple,标记为无效 { $and: ["$$value.seenBanana", { $in: ["apple", { $objectToArray: "$$this" }] }] }, { seenBanana: true, valid: false }, { $cond: [ // 遇到banana,标记已见过 { $in: ["banana", { $objectToArray: "$$this" }] }, { seenBanana: true, valid: "$$value.valid" }, // 其他元素,保持当前状态 "$$value" ] } ] } }.valid } })
注意事项
- 以上方法均要求MongoDB版本在3.4及以上;
- 若数组中不存在banana或apple,
$indexOfArray会返回-1,可根据业务需求添加额外判断逻辑。
内容的提问来源于stack exchange,提问作者Александр Сосо
相关产品推荐
相关产品推荐

