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:修正原聚合管道逻辑
原代码问题在于:
- 使用Python的
re.compile对象传入MongoDB的$regex存在兼容性风险 - 多余的
$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
相关产品推荐
相关产品推荐

