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

MongoDB嵌套数组查询:如何返回所有匹配对象及父文档?

问题:MongoDB聚合管道仅返回单个匹配的嵌套数组元素

集合数据片段

[
  {
    "_id": "57582b6b",
    "source": "integration",
    "url": "https://example.com/images/51/landscapes-polar.xml",
    "pictures": [
      {
        "name": "pines",
        "version": "2"
      },
      {
        "name": "penguins",
        "version": "1"
      },
      {
        "name": "pineapple",
        "version": "7"
      }
    ]
  },
  {
    "_id": "57582b6d",
    "source": "customer",
    "url": "https://example.com/images/15/nature.xml",
    "pictures": [
      {
        "name": "mountains",
        "version": "2"
      },
      {
        "name": "pines",
        "version": "1"
      }
    ]
  },
  {
    "_id": "57582b6c",
    "source": "qa",
    "url": "https://example.com/image/32/landscapes.xml",
    "pictures": [
      {
        "name": "alps",
        "version": "1"
      },
      {
        "name": "pineapple",
        "version": "7"
      },
      {
        "name": "pines",
        "version": "3"
      }
    ]
  }
]

需求

从嵌套的pictures数组中,查找名称匹配指定查询字符串的对象,返回包含所有匹配对象的父文档。

现有PyMongo代码

import re
from flask import Flask, jsonify

from controller.database import client, database_name, temp_collection


app = Flask(__name__)
db = client[database_name]
collection = db[temp_collection]


@app.route('/component/find/<picture_name>', methods=['GET'])
def get_component(picture_name):
    pattern = re.compile(picture_name, re.IGNORECASE)

    pipeline = [
        {"$unwind": "$pictures"},
        {"$match": {"pictures.name": {"$regex": pattern}}},
        {"$group": {
            "_id": "$_id",
            "url": {"$first": "$url"},
            "source": {"$first": "$source"},
            "pictures": {"$addToSet": "$pictures"},
            "root": {"$first": "$$ROOT"}
        }},
        {"$replaceRoot": {
            "newRoot": {
                "$mergeObjects": ["$root", {"pictures": "$pictures"}]
            }
        }},
        {"$project": {
            "_id": {"$toString": "$_id"},
            "url": 1,
            "source": 1,
            "pictures": 1
        }}
    ]

    result = list(collection.aggregate(pipeline))

    if result:
        return jsonify(result)
    else:
        return jsonify({"message": "Component with picture '{}' not found.".format(picture_name)}), 404


if __name__ == "__main__":
    app.run(debug=True)

问题现象

当前返回的每个父文档中,pictures数组仅包含一个匹配对象,而非所有匹配对象。

期望结果

[
  {
    "_id": "57582b6b",
    "source": "integration",
    "url": "https://example.com/51/landscapes-polar.xml",
    "pictures": [
      {
        "name": "pines",
        "version": "2"
      },
      {
        "name": "pineapple",
        "version": "7"
      }
    ]
  },
  {
    "_id": "57582b6d",
    "source": "customer",
    "url": "https://example.com/15/nature.xml",
    "pictures": [
      {
        "name": "pines",
        "version": "1"
      }
    ]
  },
  {
    "_id": "57582b6c",
    "source": "qa",
    "url": "https://example.com/image/32/landscapes.xml",
    "pictures": [
      {
        "name": "pineapple",
        "version": "7"
      },
      {
        "name": "pines",
        "version": "3"
      }
    ]
  }
]

实际结果

[
  {
    "_id": "57582b6b",
    "source": "integration",
    "url": "https://example.com/51/landscapes-polar.xml",
    "pictures": [
      {
        "name": "pines",
        "version": "2"
      }
    ]
  },
  {
    "_id": "57582b6d",
    "source": "customer",
    "url": "https://example.com/15/nature.xml",
    "pictures": [
      {
        "name": "pines",
        "version": "1"
      }
    ]
  },
  {
    "_id": "57582b6c",
    "source": "qa",
    "url": "https://example.com/image/32/landscapes.xml",
    "pictures": [
      {
        "name": "pineapple",
        "version": "7"
      }
    ]
  }
]

解决方案

方案1:修正原聚合管道逻辑

原代码问题在于:

  1. 使用Python的re.compile对象传入MongoDB的$regex存在兼容性风险
  2. 多余的$replaceRoot和$$ROOT操作导致逻辑冲突,$$ROOT在$unwind后仅代表单元素拆分文档,会干扰最终结果

修改后的代码:

@app.route('/component/find/<picture_name>', methods=['GET'])
def get_component(picture_name):
    pipeline = [
        {"$unwind": "$pictures"},
        # 直接使用MongoDB原生正则参数,避免Python正则对象的兼容性问题
        {"$match": {"pictures.name": {"$regex": picture_name, "$options": "i"}}},
        {"$group": {
            "_id": "$_id",
            "url": {"$first": "$url"},
            "source": {"$first": "$source"},
            # 用$push收集所有匹配项,无需$addToSet(无重复场景下效果一致)
            "pictures": {"$push": "$pictures"}
        }},
        {"$project": {
            "_id": {"$toString": "$_id"},
            "url": 1,
            "source": 1,
            "pictures": 1
        }}
    ]

    result = list(collection.aggregate(pipeline))

    if result:
        return jsonify(result)
    else:
        return jsonify({"message": f"Component with picture '{picture_name}' not found."}), 404

方案2:更高效的数组过滤方式(推荐)

无需拆分数组,直接用$filter过滤嵌套数组,性能更优:

@app.route('/component/find/<picture_name>', methods=['GET'])
def get_component(picture_name):
    pipeline = [
        # 直接过滤pictures数组,保留匹配的元素
        {"$addFields": {
            "pictures": {
                "$filter": {
                    "input": "$pictures",
                    "as": "pic",
                    "cond": {"$regexMatch": {"input": "$$pic.name", "regex": picture_name, "options": "i"}}
                }
            }
        }},
        # 过滤掉没有匹配图片的文档
        {"$match": {"pictures.0": {"$exists": True}}},
        {"$project": {
            "_id": {"$toString": "$_id"},
            "url": 1,
            "source": 1,
            "pictures": 1
        }}
    ]

    result = list(collection.aggregate(pipeline))

    if result:
        return jsonify(result)
    else:
        return jsonify({"message": f"Component with picture '{picture_name}' not found."}), 404

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 10:44:55