【问题标题】:Using results from $project gives exceeding maximum document size使用 $project 的结果会超出最大文档大小
【发布时间】:2017-12-26 07:36:56
【问题描述】:

我有 2 个集合,比如 A 和 B。

示例 A:

[
    {"Account": "99", "Cat_1": "Losses",  "Cat_2": "Marketing"},
    {"Account": "89", "Cat_1": "Losses",  "Cat_2": "Consultancy"},
    {"Account": "79", "Cat_1": "Losses",  "Cat_2": "Marketing"},
    {"Account": "69", "Cat_1": "Losses",  "Cat_2": "Consultancy"},
    {"Account": "59", "Cat_1": "Profits", "Cat_2": "Marketing"},
    {"Account": "49", "Cat_1": "Profits", "Cat_2": "Consultancy"},
    {"Account": "29", "Cat_1": "Profits", "Cat_2": "Marketing"},
    {"Account": "00", "Cat_1": "Profits", "Cat_2": "Consultancy"}
...
]

示例 B:

[
    {"Name": "Example A", "Year": 2014, "Account": "99", "Amount": -5000},
    {"Name": "Example A", "Year": 2015, "Account": "99", "Amount": -5000},
    {"Name": "Example A", "Year": 2014, "Account": "89", "Amount": -2000},
    {"Name": "Example A", "Year": 2015, "Account": "79", "Amount": -3000},
    {"Name": "Example A", "Year": 2014, "Account": "69", "Amount": 0},
    {"Name": "Example A", "Year": 2015, "Account": "59", "Amount": 100},
    {"Name": "Example A", "Year": 2016, "Account": "49", "Amount": 5000},
    {"Name": "Example A", "Year": 2014, "Account": "29", "Amount": 4000},
    {"Name": "Example A", "Year": 2015, "Account": "00", "Amount": 900},
    {"Name": "Example B", "Year": 2013, "Account": "99", "Amount": -500},
    {"Name": "Example B", "Year": 2011, "Account": "89", "Amount": -10000},
    ...
]

例如,现在我想获取所有“Cat_1”帐户及其类型,最终得到以下结果:

[
    {"cat": "Losses",  "Accounts": ["99", "89", "79", "69"]},
    {"cat": "Profits", "Accounts": ["59", "49", "29", "00"]}
]

或者,我会为某个类别获取 Cat_n 并获得同样的结果。

接下来我展开 Accounts 并对集合 B 执行查找。这是出现问题的地方,并且超出了最大文档大小。我应该提一下,我一次只对 1 个用户感兴趣,所以我的查找现在看起来像这样:

...
{
  "$lookup": {
    "from": "collection_B",
    "localField": "Account",
    "foreignField": "Account",
    "as": "results"
  }
},
{
  "$addFields": {
    "results": {
      "$filter": {
        "input": "$results",
        "as": "comp",
        "cond": {
          "$eq": [
            "$$results.Name", "Example A"
          ]
        }
      }
    }
  }
},
...

查找后,我使用 $addFields 覆盖原始结果字段,因为我不想要其中的大多数,因为我只对特定的用户感兴趣。

第二个集合中有大约 1000 万个文档,每个用户大约 300k。因此,在此查找之后,结果中永远不会超过 300k。当请求cat_1 类别时,结果将是两个数组“Losses”和“Profits”,它们都包含大约 800 个帐户。

我使用$project 减小了文档大小,只包含我真正想要的字段。此外,我还尽可能早地使用$match 以从聚合中消除不需要的文档。

但这一切都无济于事,文档不断增长,超过了 16MB BSON 的限制。只有当$limit 使用相当低的±300 值时,才会返回结果并且缺少信息。

我最终感兴趣的是为给定的用户和Cat_n生成一个包含类似内容的文档

{
  "Name": "Example A",
  "Losses": [
    {"Year": 2014, "Amount": ...},
    {"Year": 2015, "Amount": ...},
    {"Year": 2016, "Amount": ...}
  ],
  "Profits": [
    {"Year": 2014, "Amount": ...},
    {"Year": 2015, "Amount": ...},
    {"Year": 2016, "Amount": ...}
  ],
}

我一直在考虑只创建两个单独的聚合,一个用于获取类别,另一个用于聚合集合 B 的结果。但是我必须检查每个文档以找出它属于哪个类别,这并不看起来效率不高。 或者,我可以创建第三个集合,在其中合并两个集合中的文档并在那里进行聚合,但如果可能的话,我宁愿避免这样做,因为它会在以后维护或查看这些数据时增加额外的复杂性。

【问题讨论】:

  • 您看过这个“2.6 版更改:db.collection.aggregate() 方法返回一个游标,可以返回任何大小的结果集。以前的版本在单个文档中返回所有结果,并且结果集的大小限制为 16 MB"docs.mongodb.com/manual/reference/method/…
  • @DanieleTassone,有趣的功能!虽然它现在没有解决我的问题,但我觉得我可能会在以后的某个地方使用它

标签: mongodb pymongo


【解决方案1】:

我发现 $lookup 返回的匹配数组超出了 16MB 的 BSON 限制。因此,无法以任何方式检索结果。

设法通过反转逻辑来解决它,我现在正在从 A 中的集合 B 执行查找。这实际上更有意义,因为我现在可以在执行查找之前过滤掉 94% 的文档,而不是之后再做。

【讨论】:

  • 听起来更像是“解决方法”而不是解决方案。正确的做法是在$unwind$match 之后直接跟随$lookup。原因是这两个流水线阶段实际上成为了$lookup 阶段的一部分。结果是您只能取回您的条件要求的数据,并且由于它“展开”,因此不可能突破 16MB 的限制。您需要查看“解释”输出以查看对管道的实际效果。我在stackoverflow.com/a/44960412/2313887 上提供了更多详细信息。所以你可以反过来做同样的事情。
猜你喜欢
  • 2020-11-19
  • 2015-08-02
  • 1970-01-01
  • 2014-12-03
  • 2018-01-25
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
相关资源
最近更新 更多