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

MongoDB聚合查询嵌套数组过滤问题及数据结构优化咨询

MongoDB嵌套数组过滤与数据结构优化问题

问题背景

已实现过滤sales数组中label=1的对象,但无法进一步过滤sales下嵌套的codes(仅保留active=True)和prices(仅保留valid_from大于今日)数组,尝试的查询返回空结果;同时咨询当前数据结构是否最优,是否需要将sales拆分到单独集合通过关联实现。

数据结构

data = {
    "_id": "abc",
    "field": "value",
    "misc": {
        "height": 1.1,
    },
    "sales": [
        {
            "label": 1,
            "codes": [
                {
                    "code": 123456,
                    "active": True
                },
                {
                    "code": 234567,
                    "active": False
                },
                {
                    "code": 345678,
                    "active": False
                },
            ],
            "prices": [
                {
                    "price": 2.99,
                    "valid_from": datetime(year=2023, month=3, day=1)
                },
                {
                    "price": 3.99,
                    "valid_from": datetime(year=2023, month=4, day=1)
                },
                {
                    "price": 4.99,
                    "valid_from": datetime(year=2023, month=5, day=1)
                },
                {
                    "price": 5.99,
                    "valid_from": datetime(year=2023, month=6, day=1)
                },
            ]
        },
        {
            "label": 2,
            "codes": [
                {
                    "code": 987654,
                    "active": True
                },
                {
                    "code": 876543,
                    "active": False
                },
                {
                    "code": 765432,
                    "active": False
                },
            ],
            "prices": [
                {
                    "price": 2.99,
                    "valid_from": datetime(year=2023, month=3, day=1)
                },
                {
                    "price": 3.99,
                    "valid_from": datetime(year=2023, month=4, day=1)
                },
                {
                    "price": 4.99,
                    "valid_from": datetime(year=2023, month=5, day=1)
                },
                {
                    "price": 6.99,
                    "valid_from": datetime(year=2023, month=6, day=1)
                },
            ]
        }
    ]
}

已生效的聚合查询

db.MasterData.aggregate([
    
    { "$match": { "_id": "abc" } },

    {
        "$project": {
            "field": 1,
            "sales": {
                "$filter": {
                    "input": "$sales",
                    "as": "item",
                    "cond": {
                        "$eq": ["$$item.label", 1]
                    }
                }
            }
        }
    }
    
])

尝试失败的聚合查询

db.MasterData.aggregate([
    
    { "$match": { "_id": "abc" } },

    {
        "$project": {
            "field": 1,
            "sales.codes": {
                "$filter": {
                    "input": "$sales.codes",
                    "as": "item",
                    "cond": {
                        "$eq": ["$$item.active", True]
                    }
                }
            }
        }
    }
    
])

解决方案

一、正确的嵌套数组过滤聚合查询

失败原因是直接对sales.codes做$filter时,$sales.codes是二维数组(每个sales元素都包含独立的codes数组),无法直接用$filter处理。需先过滤sales数组,再对每个sales元素内的codes和prices分别做过滤,推荐用$map配合$filter实现:

db.MasterData.aggregate([
    { "$match": { "_id": "abc" } },
    {
        "$project": {
            "field": 1,
            "sales": {
                "$map": {
                    "input": {
                        "$filter": {
                            "input": "$sales",
                            "as": "saleItem",
                            "cond": { "$eq": ["$$saleItem.label", 1] }
                        }
                    },
                    "as": "filteredSale",
                    "in": {
                        "label": "$$filteredSale.label",
                        "codes": {
                            "$filter": {
                                "input": "$$filteredSale.codes",
                                "as": "codeItem",
                                "cond": { "$eq": ["$$codeItem.active", true] }
                            }
                        },
                        "prices": {
                            "$filter": {
                                "input": "$$filteredSale.prices",
                                "as": "priceItem",
                                "cond": { "$gt": ["$$priceItem.valid_from", new Date()] }
                            }
                        }
                    }
                }
            }
        }
    }
])

逻辑说明

  1. 先用$filter筛选出sales中label=1的元素;
  2. 用$map遍历每个筛选后的sales元素,对内部的codes和prices分别做$filter:
    • codes只保留active=true的项;
    • prices只保留valid_from大于当前日期的项。

若需保留sales中的其他字段,可在in对象内添加"$$filteredSale.目标字段名"。

二、数据结构优化建议

是否拆分sales到单独集合,需结合业务场景判断:

适合保留当前嵌入式结构的场景

  • 查询时总是需要将sales与主文档(field、misc等)一同返回;
  • sales数组长度不会无限增长(比如每个主文档的sales项数固定或较少);
  • 对sales的更新操作较少,或无需频繁单独修改某个sales项。

适合拆分到单独集合的场景

  • sales数组持续增长,可能导致主文档体积超过MongoDB单文档最大16MB限制;
  • 经常需要单独查询、更新某个sales项,无需加载整个主文档;
  • sales项数量远多于主文档数量,需为sales的label、valid_from等字段单独建索引以提升查询效率。

若拆分,推荐sales集合结构如下:

// Sales集合文档示例
{
  "_id": ObjectId("..."),
  "master_id": "abc", // 关联主文档的_id
  "label": 1,
  "codes": [...],
  "prices": [...]
}

查询时可通过$lookup关联主文档,或根据业务需求分别查询两个集合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:48:22