MongoDB在$ group上缺乏表现

时间:2019-04-02 21:30:09

标签: node.js mongodb mongoose mongodb-query

我正在研究一个项目,在该项目中,用户可以对一场比赛下注,如果他为获胜的团队下注,则可以赚取积分。

我正在建立一个排行榜,需要从平台中选择50个最佳玩家(得分最高的玩家)。

处理点动态地迫使我查看聚合方法,以便根据这些Models计算用户点:

const UserSchema = new mongoose.Schema({
  admin: { type: Boolean, default: true },
  username: String,
  password: String,
  pronos: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Prono', default: [] }],
});

const PronoSchema = new mongoose.Schema({
  match: { type: mongoose.Schema.Types.ObjectId, ref: 'Match' },
  local: Number,
  guest: Number,
  coeff: Number,
});

const StepSchema = new mongoose.Schema({
  matchs: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Match', default: [] }],
  name: String,
});

const CompetitionSchema = new mongoose.Schema({
  steps: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Step', default: [] }],
  start: { type: mongoose.Schema.Types.Date },
  name: String,
});

const MatchSchema = new mongoose.Schema({
  local: { type: mongoose.Schema.Types.ObjectId, ref: 'Team' },
  guest: { type: mongoose.Schema.Types.ObjectId, ref: 'Team' },
  localScore: { type: Number, default: -1 },
  guestScore: { type: Number, default: -1 },
  date: { type: mongoose.Schema.Types.Date },
});

总结所有这些代码:

玩家在pronos上的Matches上下注,称为Steps,下注在Competition内。因此,比赛的每一步都有自己的比赛。

我一直在制作这个游戏来计算玩家的积分,我想知道我是否朝着正确的方向前进

  const users = await User.aggregate([
    { $unwind: '$pronos' },
    {
      $lookup: {
        from: 'pronos',
        localField: 'pronos',
        foreignField: '_id',
        as: 'pronoObjects',
      }
    },
    { $unwind: '$pronoObjects' },
    {
      $lookup: {
        from: 'matches',
        localField: 'pronoObjects.match',
        foreignField: '_id',
        as: 'matches',
      }
    },
    { $unwind: '$matches' },
    {
      $addFields: {
        pointsEarned: {
          $switch: {
            branches: [
              {
                case: {
                  $and: [
                    { $eq: ['$pronoObjects.local', '$matches.localScore'] },
                    { $eq: ['$pronoObjects.guest', '$matches.guestScore'] },
                  ],
                },
                then: 3,
              },
              {
                case: {
                  $and: [
                    { $lt: [{ $subtract: ['$pronoObjects.local', '$pronoObjects.guest'] }, 0] },
                    { $lt: [{ $subtract: ['$matches.localScore', '$matches.guestScore'] }, 0] }
                  ]
                },
                then: 1,
              },
              {
                case: {
                  $and: [
                    { $gt: [{ $subtract: ['$pronoObjects.local', '$pronoObjects.guest'] }, 0] },
                    { $gt: [{ $subtract: ['$matches.localScore', '$matches.guestScore'] }, 0] }
                  ]
                },
                then: 1,
              },
            ],
            default: 0,
          }
        }
      }
    },
    {
      $group: {
        _id: '$_id',
        points: { $sum: '$pointsEarned' }
      }
    },
    {
      $lookup: {
        from: 'users',
        localField: '_id',
        foreignField: '_id',
        as: 'user',
      }
    }
  ]);

由于它按照我想要的方式工作,因此我计划通过Competition建立一个排名,用户可以在其中选择比赛ID并查看其排名。

当试图实现这一目标时,我一直在使用许多放松方法,以至于在分组之前我的响应时间为1万行。因此,我想知道是否有人可以向我暗示实现此目标的正确方法。

我不是要寻找完整的答案,我是mongo的新手,并且想了解良好做法或学习新的汇总方法。

谢谢。

编辑:

通过这种汇总,我设法为每个用户获取积分。问题在于,只有2位用户才能进行400次比赛,而这需要10秒钟以上的时间。 我想念什么吗?

这是我使用的请求:

  return await Competition.aggregate([
    {
      $match: {
        $expr: {
          $eq: ['$_id', { $toObjectId: compId }],
        }
      }
    },
    { $unwind: '$steps' },
    {
      $lookup: {
        from: 'steps',
        as: 'stepsObject',
        localField: 'steps',
        foreignField: '_id',
      }
    },
    { $unwind: '$stepsObject' },
    { $unwind: '$stepsObject.matchs' },
    {
      $project: {
        _id: 1,
        stepsObject: 1,
      }
    },
    {
      $lookup: {
        from: 'matches',
        as: 'matchesObject',
        let: { otherid: '$stepsObject.matchs' },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  { $eq: ['$$otherid', '$_id'] },
                  { $ne: ['$localScore', -1] },
                ]
              }
            }
          }
        ],
      },
    },
    { $unwind: '$matchesObject' },
    {
      $lookup: {
        from: 'users',
        as: 'users',
        pipeline: [
          {
            $project: {
              _id: 1,
              pronos: 1,
            }
          }
        ],
      }
    },
    { $unwind: '$users' },
    { $unwind: '$users.pronos' },
    {
      $lookup: {
        from: 'pronos',
        as: 'pronosObject',
        let: { matchid: '$matchesObject._id', knownpronos: '$users.pronos' },
        pipeline: [
          {
            $match: {
              $expr:
              {
                $and: [
                  { $eq: ['$$matchid', '$match'], },
                  { $eq: ['$$knownpronos', '$_id'] },
                ]
              }
            }
          }
        ]
        // localField: 'users.pronos',
        // foreignField: '_id',
      }
    },
    { $unwind: '$pronosObject' },
    {
      $addFields: {
        pointsEarned: {
          $switch: {
            branches: [
              {
                case: {
                  $and: [
                    { $eq: ['$pronosObject.local', '$matchesObject.localScore'] },
                    { $eq: ['$pronosObject.guest', '$matchesObject.guestScore'] },
                  ],
                },
                then: { $multiply: [3, '$pronosObject.coeff'] },
              },
              {
                case: {
                  $and: [
                    { $lt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
                    { $lt: [{ $subtract: ['$matchesObject.localScore', '$matchesObject.guestScore'] }, 0] }
                  ]
                },
                then: { $multiply: [1, '$pronosObject.coeff'] },
              },
              {
                case: {
                  $and: [
                    { $gt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
                    { $gt: [{ $subtract: ['$matchesObject.localScore', '$matchesObject.guestScore'] }, 0] }
                  ]
                },
                then: { $multiply: [1, '$pronosObject.coeff'] },
              },
            ],
            default: 0,
          }
        }
      }
    },
    {
      $group: {
        _id: '$users._id',
        pointsEarned: { $sum: '$pointsEarned' },
      }
    }
  ]);

编辑2:

我用另一种方法更快地重写了整个管道。我仍然有一个小问题。如果我摆脱了最后一组,则该请求只需要4毫秒即可运行,但是通过id对点进行分组的最后一组则需要花费550ms的时间。我不知道该如何优化,因为这是每个用户所有积分的最终加法。

这是我随附的:

  return await Competition.aggregate([
    {
      $match: {
        $expr: {
          $eq: ['$_id', { $toObjectId: compId }],
        }
      }
    },
    { $unwind: '$steps' },
    {
      $lookup: {
        from: 'steps',
        as: 'stepsObject',
        localField: 'steps',
        foreignField: '_id',
      }
    },
    { $unwind: '$stepsObject' },
    {
      $project: {
        _id: 1,
        stepsObject: 1,
      }
    },
    {
      $lookup: {
        from: 'matches',
        as: 'matchesObject',
        let: { otherid: '$stepsObject.matchs' },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  { $in: ['$_id', '$$otherid'] },
                  { $ne: ['$localScore', -1] },
                ]
              }
            }
          }
        ],
      },
    },
    {
      $lookup: {
        from: 'users',
        as: 'users',
        pipeline: [
          {
            $project: {
              _id: 1,
              pronos: 1,
            }
          }
        ],
      }
    },
    { $unwind: '$users' },
    { $unwind: '$users.pronos' },
    {
      $lookup: {
        from: 'pronos',
        as: 'pronosObject',
        localField: 'users.pronos',
        foreignField: '_id',
      }
    },
    { $unwind: '$pronosObject' },
    {
      $project: {
        user_id: '$users._id',
        pronosObject: 1,
        matchesObjects: {
          $arrayElemAt: [
            {
              $filter: {
                input: '$matchesObject',
                as: 'matchesObjects',
                cond: { $eq: ['$$matchesObjects._id', '$pronosObject.match'] }
              }
            }, 0
          ]
        },
      }
    },
    {
      $addFields: {
        pointsEarned: {
          $switch: {
            branches: [
              {
                case: {
                  $and: [
                    { $eq: ['$pronosObject.local', '$matchesObjects.localScore'] },
                    { $eq: ['$pronosObject.guest', '$matchesObjects.guestScore'] },
                  ],
                },
                then: { $multiply: [3, '$pronosObject.coeff'] },
              },
              {
                case: {
                  $and: [
                    { $lt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
                    { $lt: [{ $subtract: ['$matchesObjects.localScore', '$matchesObjects.guestScore'] }, 0] }
                  ]
                },
                then: { $multiply: [1, '$pronosObject.coeff'] },
              },
              {
                case: {
                  $and: [
                    { $gt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
                    { $gt: [{ $subtract: ['$matchesObjects.localScore', '$matchesObjects.guestScore'] }, 0] }
                  ]
                },
                then: { $multiply: [1, '$pronosObject.coeff'] },
              },
            ],
            default: 0,
          }
        }
      }
    },
    {
      $group: { // This is causing me trouble
        _id: '$user_id',
        pointsEarned: { $sum: '$pointsEarned' },
      }
    }
  ]);

0 个答案:

没有答案