【问题标题】:MongoDB: slow performance pipeline lookup compared to basic lookupMongoDB:与基本查找相比,性能管道查找速度慢
【发布时间】:2020-11-07 03:27:54
【问题描述】:

我有两个收藏:

matches:

[{
    date: "2020-02-15T17:00:00Z",    
    players: [
      {_id: "5efd9485aba4e3d01942a2ce"}, 
      {_id: "5efd9485aba4e3d01942a2cf"}
    ]        
},
{...}] 

players:

 [{    
     _id: "5efd9485aba4e3d01942a2ce", 
     name: "Rafa Nadal"   
 },
 {    
     _id: "5efd9485aba4e3d01942a2ce", 
     name: "Roger Federer"   
 },
 {...}]  

我需要使用查找管道,因为我正在构建一个带有递归函数的 graphql 解析器,并且我需要嵌套查找。我已经按照这个例子https://docs.mongodb.com/datalake/reference/pipeline/lookup-stage#nested-example

我的问题是管道查找需要 11 秒,但基本查找只需 0.67 秒。而且我的测试数据库很短!大约 1300 名玩家和 700 场比赛。

这是我的管道查找(11 秒解决)

db.collection('matches').aggregate([{
   $lookup: {
      from: 'players',
      let: { ids: '$players' },            
      pipeline: [{ $match: { $expr: { $in: ['$_id', '$$ids' ] } } }],
      as: 'players'
   }
}]);

这是我的基本查找(0.67 秒解决)

db.collection('matches').aggregate([{
   $lookup: {
     from: "players",
     localField: "players",
     foreignField: "_id",
     as: "players"
   }
}]);

为什么差别这么大?我可以通过什么方式进行更快的管道查找?

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    作为@namar sood cmets,有几张票证提到了这个问题:

    https://jira.mongodb.org/browse/SERVER-37470

    https://jira.mongodb.org/browse/SERVER-32549

    同时解决方案可以是(也可以嵌套):

    db.collection('matches').aggregate([
      { $unwind: '$players' },
      {
       $lookup: {
          from: 'players',
          let: { id: '$players' },            
          pipeline: [{ $match: { $expr: { $eq: ['$_id', '$$id' ] } } }],
          as: 'players'
      },
      { $unwind: '$players' },
      {
        $group: {
           "_id": "$_id",        
           "data": { "$first": "$$ROOT" },
           "players": {$push: "$players"}        
        }
      },
      { $addFields: {"data.players": "$players"} },
      { $replaceRoot: { newRoot: "$data" }}
    ]);
    

    【讨论】:

      【解决方案2】:

      问题是,当您使用带有匹配阶段的pipeline 执行lookup 时,索引将仅用于与$eq operator 匹配的字段,而其余索引将不被使用。

      您使用管道指定的示例将像这样工作(此处不再使用索引,因为它不是 $eq

      db.matches.aggregate([
        {
          $lookup: {
            from: "players",
            let: {
              ids: {
                $map: {
                  input: "$players",
                  in: "$$this._id"
                }
              }
            },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $in: [
                      "$_id",
                      "$$ids"
                    ]
                  }
                }
              }
            ],
            as: "players"
          }
        }
      ])
      

      因为 player 是一个对象数组,所以需要先映射到 ids 数组

      MongoDB Playground

      【讨论】:

      • 我删除了之前的答案,因为我认为 $eq 与数组一起使用,但想法是这只是管道中的索引仅适用于 $eq 运算符
      • 那么,就像这样:pipeline: [{ '$match': { '$expr': { $or: [{ '$eq': ['$_id', '$$ids.0'] },{ '$eq': ['$_id', '$$ids.1'] }] } } } ]
      • jira.mongodb.org/browse/SERVER-32549 ,这是我为同一问题找到的 jira toicket
      • 我不知道使用 $or 是否有帮助(我们可以尝试运行它),但我们可以使用的另一个解决方案是,首先,我们展开玩家的 id 和使用$eq 运算符示例:mongoplayground.net/p/FGAY-jFBG1_
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-27
      相关资源
      最近更新 更多