You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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函数。
  • 递归函数会逐层遍历文档的所有属性:
    1. 先检查当前对象是不是空对象(通过Object.keys(obj).length === 0和constructor判断,避免把数组[]或null误判为空对象)。
    2. 如果属性值是嵌套文档,就递归进去继续检查。
  • 只要发现任何层级存在空对象,$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:28:18