如何用MongoDB Query查询特定字段值变化的文档列表?
如何用MongoDB查询特定字段值与前序文档不同的文档
可以直接用MongoDB的聚合管道实现,不需要通过脚本迭代。针对你给出的示例场景,以下是具体解决方案:
前提说明
必须给文档指定明确的排序规则(比如按_id、创建时间字段等),因为"前序文档"的定义依赖固定顺序,示例中的#1到#5默认按_id升序排列。
方案一:使用MongoDB 5.0+的$setWindowFields(推荐,高效简洁)
MongoDB 5.0及以上版本支持窗口函数,用$lag操作符可直接获取前一个文档的字段值,再对比筛选:
db.collection.aggregate([ // 1. 按固定顺序排序(这里用_id升序对应示例的#1到#5) { $sort: { _id: 1 } }, // 2. 用窗口函数获取前一个文档的name字段 { $setWindowFields: { partitionBy: null, // 不分区,整个集合作为一个窗口 sortBy: { _id: 1 }, output: { prevName: { $lag: "$name", // 取前一个文档的name值 offset: 1, default: null // 第一个文档没有前序,设为null } } } }, // 3. 筛选符合要求的文档 { $match: { $or: [ // 匹配第二个及以后、且与前序name相同的文档(对应示例#2) { $and: [{ prevName: "$name" }, { prevName: { $ne: null } }] }, // 匹配与前序name不同的文档(对应示例#3、#4) { $expr: { $ne: ["$name", "$prevName"] } } ] } }, // 移除辅助字段,返回原始文档结构 { $unset: "prevName" } ])
逻辑解释
$sort确保文档按固定顺序排列,这是确定"前序文档"的基础$setWindowFields的$lag操作符会为每个文档添加prevName字段,存储前一个文档的name值$match的条件覆盖两种情况:- 非第一个文档,且当前
name和前序相同(对应示例中的#2) - 当前
name和前序不同(对应示例中的#3、#4)
- 非第一个文档,且当前
- 最后用
$unset移除辅助字段prevName,返回和原始文档一致的结构
方案二:兼容MongoDB 5.0以下版本
如果你的MongoDB版本低于5.0,可以用聚合管道的$group+$unwind组合实现:
db.collection.aggregate([ // 1. 按固定顺序排序 { $sort: { _id: 1 } }, // 2. 将所有文档存入一个数组 { $group: { _id: null, docs: { $push: "$$ROOT" } } }, // 3. 展开数组并记录每个文档的位置索引 { $unwind: { path: "$docs", includeArrayIndex: "position" } }, // 4. 根据位置索引获取前一个文档 { $addFields: { prevDoc: { $arrayElemAt: ["$docs", { $subtract: ["$position", 1] }] } } }, // 5. 筛选符合要求的文档 { $match: { $or: [ { $and: [{ "prevDoc.name": "$docs.name" }, { position: { $gt: 0 } }] }, { $expr: { $ne: ["$docs.name", "$prevDoc.name"] } } ] } }, // 6. 恢复原始文档结构 { $replaceRoot: { newRoot: "$docs" } } ])
逻辑解释
- 先把排序后的所有文档存入一个数组,再展开每个文档并记录位置
- 通过位置索引计算出前一个文档的位置,用
$arrayElemAt取出前序文档 - 后续的筛选逻辑和方案一一致,最后用
$replaceRoot恢复原始文档结构
内容的提问来源于stack exchange,提问作者Hesoyam
相关产品推荐
相关产品推荐

