MongoDB查询集合时过滤排除未知键的未发布嵌入文档
问题背景
现有MongoDB集合单文档结构如下:
{ _id: "...", name: "myName", versions: { "1.0.0": { status: 'unpublished' }, "1.0.1": { status: 'published' }, // 其余动态版本号键 "2.2.0": { status: 'unpublished' }, "14.3023.1": { status: 'published' } // ... } }
需求为查询name等于指定值(示例为myName)的文档,返回结果中剔除versions下所有status为unpublished的版本子文档。由于版本号为动态生成,无法提前枚举versions下的所有键名。
原有尝试写法未生效,核心问题是:普通find方法的基础投影语法不支持动态遍历未知键的对象做条件过滤,且写法中使用的$是数组专属位置操作符,无法作用于对象类型的versions字段。
正确实现方案
使用聚合管道实现,核心思路是将动态键对象转为数组结构完成过滤,再转回对象结构,兼容MongoDB 3.4.4及以上所有版本,代码如下:
db.你的集合名.aggregate([ // 匹配name符合条件的文档,和find查询条件逻辑一致 { $match: { name: "myName" } }, // 重写versions字段,过滤未发布版本 { $set: { versions: { // 把过滤后的k-v数组转回原对象结构 $arrayToObject: { // 过滤数组,仅保留status不为unpublished的版本 $filter: { input: { $objectToArray: "$versions" }, // 将versions对象转为{k:版本号, v:版本内容}的数组 cond: { $ne: ["$$this.v.status", "unpublished"] } } } } } }, // 取第一条结果,和findOne效果一致 { $limit: 1 } ]).toArray()[0]
如果使用MongoDB 4.4及以上版本,也可以直接在find方法中使用聚合投影语法,写法更简洁:
db.你的集合名.findOne( { name: "myName" }, { $set: { versions: { $arrayToObject: { $filter: { input: { $objectToArray: "$versions" }, cond: { $ne: ["$$this.v.status", "unpublished"] } } } } } } )
- 上述两种写法返回的文档结构和原结构完全一致,仅会剔除
versions下状态为unpublished的版本键,其余字段(_id、name等)均正常保留 - 不需要提前知道任何具体版本号,自动适配所有动态生成的版本键
内容的提问来源于stack exchange,提问作者johann1301s
相关产品推荐
相关产品推荐

