如何在MongoDB集合中查找含空对象属性(含子文档且属性名未知)的文档?
在MongoDB中查找含空对象{}的文档(属性名未知)
要找出集合中**自身或任意子文档里存在值为空对象{}**的文档,而且属性名完全未知,我们可以根据MongoDB版本选择不同的实现方式,下面给你详细说明:
方法一:使用递归函数(MongoDB 4.4+ 推荐)
如果你的MongoDB版本是4.4及以上,用$function自定义递归函数是最直观的方案——它可以遍历文档的所有层级,只要发现任何属性的值是空对象就返回该文档。
查询代码
db.yourCollectionName.aggregate([ { $match: { $expr: { $function: { body: function(doc) { // 递归检查函数:遍历对象所有属性,判断是否存在空对象 function hasEmptyObject(obj) { // 先判断当前对象本身是不是空对象(排除数组、null) if (Object.keys(obj).length === 0 && obj.constructor === Object) { return true; } // 遍历每个属性值,递归检查嵌套文档 for (const key in obj) { const value = obj[key]; // 只处理非数组、非null的对象类型 if (typeof value === 'object' && value !== null && !Array.isArray(value)) { if (hasEmptyObject(value)) { return true; } } } return false; } return hasEmptyObject(doc); }, args: ["$$ROOT"], // 传入整个文档 lang: "js" } } } } ])
代码说明
$$ROOT代表当前整个文档,我们把它传给自定义的hasEmptyObject函数。- 递归函数会逐层遍历文档的所有属性:
- 先检查当前对象是不是空对象(通过
Object.keys(obj).length === 0和constructor判断,避免把数组[]或null误判为空对象)。 - 如果属性值是嵌套文档,就递归进去继续检查。
- 先检查当前对象是不是空对象(通过
- 只要发现任何层级存在空对象,
$match就会把该文档筛选出来。
方法二:聚合管道组合操作(MongoDB 4.4以下版本)
如果你的MongoDB版本不支持$function,可以用原生聚合操作符组合实现,但这个方法仅能处理两层嵌套(顶层+一级子文档),更深的嵌套需要额外扩展逻辑。
查询代码
db.yourCollectionName.aggregate([ // 把顶层文档转成键值对数组,方便遍历 { $addFields: { topLevelEntries: { $objectToArray: "$$ROOT" } } }, // 展开顶层的每个键值对 { $unwind: "$topLevelEntries" }, // 匹配两种情况:顶层值是空对象,或者子文档里有空对象 { $match: { $or: [ { "topLevelEntries.v": {} }, { $expr: { $gt: [ { $size: { $filter: { input: { $objectToArray: "$topLevelEntries.v" }, cond: { $eq: ["$$this.v", {}] } } } }, 0 ] } } ] } }, // 去重:避免同一个文档因多个符合条件的键被重复返回 { $group: { _id: "$_id", originalDoc: { $first: "$$ROOT" } } }, // 恢复原始文档结构 { $replaceRoot: { newRoot: "$originalDoc" } }, // 移除临时添加的字段 { $project: { topLevelEntries: 0 } } ])
测试示例
假设你的集合里有这些文档:
// 符合条件的文档 { "_id": 1, "data": { "comment": {} } } { "_id": 2, "emptyField": {} } { "_id": 3, "a": { "b": { "c": {} } } } // 不符合条件的文档 { "_id": 4, "data": { "comment": "test" } } { "_id": 5, "emptyArray": [] } { "_id": 6, "nullValue": null }
用方法一的查询会返回_id为1、2、3的文档,完全符合你的需求。
注意事项
- 性能方面:
$function是JS函数,在数据量极大的情况下,性能会比原生操作符差一些。如果是超大集合,可能需要考虑提前规划字段结构,或者用其他方式优化。 - 空对象判断:一定要排除数组和null,因为
typeof []和typeof null都会返回object,所以递归函数里加了专门的判断逻辑。
内容的提问来源于stack exchange,提问作者Alexander Zeitler
相关产品推荐
相关产品推荐

