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

MongoDB嵌套数组指定对象的值获取:寻求更简洁高效实现方案

更简洁高效的MongoDB查询方案:获取嵌套数组中的指定值

请看以下示例数据。V1、V2和V3是values数组中对象的唯一标识符,这些标识符只会使用一次(即集合中即使属于不同数组的values对象,也不会有相同的id)。

db.experiment.deleteMany({});

db.experiment.insertMany([
  {
    'id': 'Q1',
    'statements': [
      {
        'property': 'P1',
        'values': [
          {'id': 'V1', 'value': 'Q1 foo'},
          {'id': 'V2', 'value': 'Q1 bar'}
        ]
      },
      {
        'property': 'P2',
        'values': [
          {'id': 'V3', 'value': 'Q1 baz'},
          {'id': 'V4', 'value': 'Q1 qux'}
        ]
      }
    ]
  },
  {
    'id': 'Q2',
    'statements': [
      {
        'property': 'P1',
        'values': [
          {'id': 'V5', 'value': 'Q2 foo'},
          {'id': 'V6', 'value': 'Q2 bar'}
        ]
      },
      {
        'property': 'P2',
        'values': [
          {'id': 'V7', 'value': 'Q2 baz'},
          {'id': 'V8', 'value': 'Q2 qux'}
        ]
      }
    ]
  }
]);

需求与预期结果

需要获取顶层id为Q1的文档中,id为V3的对象的value字段,预期结果如下:

{
  _id: ObjectId(<object-id>),
  value: 'Q1 baz'
}

原始实现方案

你已通过以下聚合管道得到了预期结果:

printjson(db.experiment.aggregate([
  {
    '$match': {
      'id': 'Q1'
    }
  },
  // I used $unwind because I did not know how to use $filter in an
  // array that is inside another array.
  {
    '$unwind': '$statements'
  },
  // I used $project in order to use $filter
  {
    '$project': {
      'projectResult': {
        $filter: {
          input: '$statements.values',
          as: 'value',
          cond: { $eq: ["$$value.id", "V3"] }
        }
      }
    }
  },
  // I used $match to filter out empty arrays.
  {
    '$match': {
      'projectResult': {$ne: []}
    }
  },
  // At this point, the key projectResult is an array, so I needed to
  // unwind it in order to $project the key "value"
  {
    '$unwind': '$projectResult'
  },
  {
    '$project': {
      'value': '$projectResult.value'
    }
  }
]))

执行结果:

[
  {
    _id: ObjectId('66e0a3f4370082960c2202d8'),
    value: 'Q1 baz'
  }
]

优化后的实现方案

方法一:聚合管道简化版(减少阶段,避免多次$unwind)

利用$reduce遍历外层数组,结合$filter筛选内层数组,再通过$arrayElemAt和$replaceRoot直接提取目标值,无需多次展开数组:

printjson(db.experiment.aggregate([
  { $match: { id: "Q1" } },
  {
    $project: {
      targetValue: {
        $arrayElemAt: [
          {
            $reduce: {
              input: "$statements",
              initialValue: [],
              in: {
                $concatArrays: [
                  "$$value",
                  { $filter: { input: "$$this.values", as: "v", cond: { $eq: ["$$v.id", "V3"] } } }
                ]
              }
            }
          },
          0
        ]
      }
    }
  },
  { $replaceRoot: { newRoot: { _id: "$_id", value: "$targetValue.value" } } },
  { $match: { value: { $exists: true } } }
]))

方法二:find查询配合投影(更简洁的写法)

直接用find定位文档,通过投影中的数组运算符提取目标值,最后用map整理结果格式:

printjson(db.experiment.find(
  { id: "Q1", "statements.values.id": "V3" },
  {
    _id: 1,
    targetValue: {
      $arrayElemAt: [
        {
          $reduce: {
            input: "$statements",
            initialValue: [],
            in: {
              $concatArrays: [
                "$$value",
                { $filter: { input: "$$this.values", as: "v", cond: { $eq: ["$$v.id", "V3"] } } }
              ]
            }
          }
        },
        0
      ]
    }
  }
).map(doc => ({ _id: doc._id, value: doc.targetValue.value })))

方法三:MongoDB 4.4+版本简化方案

利用$first和$getField进一步精简代码:

printjson(db.experiment.aggregate([
  { $match: { id: "Q1", "statements.values.id": "V3" } },
  {
    $project: {
      value: {
        $getField: {
          field: "value",
          input: {
            $first: {
              $reduce: {
                input: "$statements",
                initialValue: [],
                in: { $concatArrays: ["$$value", { $filter: { input: "$$this.values", as: "v", cond: { $eq: ["$$v.id", "V3"] } } }] }
              }
            }
          }
        }
      }
    }
  }
]))

优化优势

这些方案避免了多次$unwind和$match操作:

  • $unwind会将数组展开为多个文档,增加后续处理的文档数量,降低效率
  • 用$reduce+$filter可以在单个阶段完成嵌套数组的筛选,减少管道阶段,提升查询性能
  • 代码更简洁,逻辑更集中

内容的提问来源于stack exchange,提问作者Rodrigo Morales

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:07:05