【问题标题】:MongoDB Aggregation Limit LookupMongoDB 聚合限制查找
【发布时间】:2017-11-08 20:48:35
【问题描述】:

我在 PyMongo 中使用 $lookup 成功地“加入”了两个集合(这有效)。我有一个问题,我加入的第二个集合在返回所有记录时可能超过 BSON 文档大小。

我希望使用 $limit 来限制允许在“match_docs”下加入的记录数,例如:每个 obj_id 的“cmets”最多 100 条记录:

db.indicators.aggregate([
  {
    "$lookup": {
      "from": "comments",
      "localField": "_id",
      "foreignField": "obj_id",
      "as": "match_docs"
    }
  }
])

我尝试了各种类型的 $limit,它似乎只限制了整体结果的总数,而不仅仅是连接。

【问题讨论】:

  • 你不能在 $lookup 中设置一个限制,但是你不需要这个来使查询工作。将{allowDiskUse: true} 添加到您的聚合选项应该可以解决问题
  • @felix OP 谈论的是 BSON 文档大小限制,而不是内存限制。
  • 您能否对从$lookup 返回的内容进行$match 以减少结果?当流水线阶段为$lookup -> $unwind -> $match 并且$match (连续)和$match 中的条件引用来自$lookup 的数组时,有一种特殊情况。舞台被“提升”到$lookup。这是减少可能返回的条目的优化。
  • @NeilLunn 你知道这在查询表单中会是什么样子吗?
  • 是的,$unwind immediately 位于$lookup 之后,理想情况下$match imdediately 位于$unwind 之后。如果这比我已经说过的更清楚。也许您应该展示您的整个“预期”聚合管道,然后我们可以建议这是否适合您的目的。

标签: mongodb join aggregation-framework pymongo


【解决方案1】:

如果您在$lookup 之后立即执行$unwind,则管道将得到优化,基本上结合两个阶段有助于绕过$lookup 返回大量文档可能导致的 16MB 限制。

请记住,如果外部集合中的单个文档加上本地集合中的文档大小超过 16 MB,则此优化无济于事。

【讨论】:

  • 还要注意,如果$unwind 后跟$match 寻址外部集合,那么查询条件也将与$unwind 一起“提升”到$lookup 中。您可以使用 { "explain": true } 作为聚合参数来查看这一点。
  • @NeilLunn 对此有一些警告:jira.mongodb.org/browse/SERVER-21612。此外,要为后续的$match 使用索引,您需要有一个复合索引,以用于$lookup 的字段开头,后跟$match 中的字段。
  • 索引与我在这里所说的无关,这是一个单独的问题。您在此处所做的两个回复看起来都是由某人在 google 中搜索字词完成的,并且可以通过示例说明整个故事。
  • @NeilLunn,我不确定我是否明白这是一个单独的问题。您说在$lookup$unwind 之后使用$match 将得到优化。我只是补充说,要让$match 使用索引,索引必须采用特定的形式。否则,即使经过查询引擎优化,它也可能导致扫描大量文档以满足 $match 生成的 $lookup 结果集的要求。我提供了 SERVER 票证链接来说明优化此管道时存在的注意事项。
【解决方案2】:

我想通了。

$lookup -> $match -> $project

db.indicators.aggregate([{
    "$lookup": {
        "from": "comments"
        , "localField": "_id"
        , "foreignField": "obj_id"
        , "as": "match_docs"
    }
}, {
    "$match": {
        "match_docs": {
            "$exists": True
        }
    }
}, {
    "$project": {
        "match_docs_agg": {
            "$slice": ["$match_docs", 3]
        }
    }
}])

【讨论】:

  • 讨厌在那里破灭你的泡沫,但这实际上并没有做任何事情来避免 BSON 限制被打破。测试数组字段的存在也是“给定的”,因为它“总是”在$lookup 的输出中创建。 $slice 只会在“已经”拉入文档之后减小结果的大小,因此仍然可以打破 BSON 限制。这对你有用的唯一原因是因为拉入的文档实际上并没有打破这个限制。无论如何,16MB 是相当多的
  • 你是 100% 正确的,它正在做我想要的,因为它只返回了加入列表的“3”,但它仍然在管道期间中断。
  • 您能否在问题中显示您打算进行的聚合。您的“意图”是指$lookup 操作,以及您想要对接下来的信息进行的聚合操作,即使它只是$slice。然后我可以真正告诉你该怎么做。
【解决方案3】:

从 MongoDB 3.6 开始,您可以使用不相关的子查询来限制查找:

db.indicators.aggregate([
{ $lookup: {
  from: 'comments',
  as: 'match_docs',
  let: { indicator_id: '$_id' },
  pipeline: [
    { $match: {
      $expr: { $eq: [ '$obj_id', '$$indicator_id' ] }
    } },
    // { $sort: { createdAt: 1 } }, // add sort if needed (for example, if you want first 100 comments by creation date)
    { $limit: 100 }
  ]
} }
])

【讨论】:

  • $match 后的 $limit,正是我所期待的
  • 只有我这不起作用吗?不知何故,它不是限制“cmets”集合的结果,而是限制我正在查找的父集合的结果
【解决方案4】:

这样我们可以使用限制聚合过滤记录。根据我们的要求,我们可以通过限制值。这里有一个有用的链接Docs link

   db.getCollection('botAnalytics').aggregate([{
                    $match: { applicationId: "15077a8c38657a61b844e6a" },
              },
                 { $limit : 5 },
        
                {
                    $lookup: {
                        from: "movie",
                        localField: "botKey",
                        foreignField: "key",
                        as: "botDetails",
                    },
                }])

【讨论】:

    猜你喜欢
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-11
    • 2017-05-05
    • 2020-06-29
    • 1970-01-01
    相关资源
    最近更新 更多