Монгробная структура запросов агрегации - PullRequest
0 голосов
/ 03 апреля 2020

Я использую структуру агрегации MongoDB, чтобы сделать несколько запросов, чтобы получить статистику о некоторых сыгранных матчах. Это прекрасно работало, но теперь, когда в моей базе данных более 100 тыс. Документов, запросы выполняются очень медленно.

Каждый запрос занимает от 1300 до 3900 мс. Поэтому вся моя страница занимает около 12 секунд.

Вот код запросов:

class MatchRepository {
  static get inject() {
    return ['App/Models/Match']
  }

  constructor(Match) {
    this.Match = Match
  }

  /**
   * Basic matchParams used in a lot of requests
   * @param puuid of the summoner
   */
  _matchParams(puuid) {
    return {
      summoner_puuid: puuid,
      result: { $not: { $eq: 'Remake' } },
      gamemode: { $nin: [800, 810, 820, 830, 840, 850] },
      season: this.season ? this.season : { $exists: true }
    }
  }

  /**
   * Build the aggregate mongo query
   * @param {Number} puuid 
   * @param {Object} matchParams 
   * @param {Array} intermediateSteps 
   * @param {*} groupId 
   * @param {Object} groupParams 
   * @param {Array} finalSteps 
   */
  _aggregate(puuid, matchParams, intermediateSteps, groupId, groupParams, finalSteps) {
    return this.Match.query().aggregate([
      {
        $match: {
          ...this._matchParams(puuid),
          ...matchParams
        }
      },
      ...intermediateSteps,
      {
        $group: {
          _id: groupId,
          count: { $sum: 1 },
          wins: {
            $sum: {
              $cond: [{ $eq: ['$result', 'Win'] }, 1, 0]
            }
          },
          losses: {
            $sum: {
              $cond: [{ $eq: ['$result', 'Fail'] }, 1, 0]
            }
          },
          ...groupParams
        },
      },
      ...finalSteps
    ])
  }

  /**
   * Get Summoner's statistics for the N most played champions
   * @param puuid of the summoner
   * @param limit number of champions to fetch
   */
  championStats(puuid, limit = 5) {
    const groupParams = {
      champion: { $first: '$champion' },
      kills: { $sum: '$stats.kills' },
      deaths: { $sum: '$stats.deaths' },
      assists: { $sum: '$stats.assists' },
    }
    const finalSteps = [
      { $sort: { 'count': -1, 'champion.name': 1 } },
      { $limit: limit }
    ]
    return this._aggregate(puuid, {}, [], '$champion.id', groupParams, finalSteps)
  }

  /**
   * Get Summoner's statistics for all played champion classes
   * @param puuid of the summoner
   */
  championClassStats(puuid) {
    const groupId = { '$arrayElemAt': ['$champion.roles', 0] }
    return this._aggregate(puuid, {}, [], groupId, {}, [])
  }

  /**
   * Get Summoner's statistics for all played modes
   * @param puuid of the summoner
   */
  gamemodeStats(puuid) {
    return this._aggregate(puuid, {}, [], '$gamemode', {}, [])
  }

  /**
   * Get global Summoner's statistics
   * @param puuid of the summoner
   */
  globalStats(puuid) {
    const groupParams = {
      time: { $sum: '$time' },
      kills: { $sum: '$stats.kills' },
      deaths: { $sum: '$stats.deaths' },
      assists: { $sum: '$stats.assists' },
      minions: { $sum: '$stats.minions' },
      vision: { $sum: '$stats.vision' },
      kp: { $avg: '$stats.kp' },
    }
    return this._aggregate(puuid, {}, [], null, groupParams, [])
  }

  /**
   * Get Summoner's statistics for the 5 differnt roles
   * @param puuid of the summoner
   */
  roleStats(puuid) {
    const matchParams = {
      role: { $not: { $eq: 'NONE' } }
    }
    const finalSteps = [
      {
        $project: {
          role: '$_id',
          count: '$count',
          wins: '$wins',
          losses: '$losses',
        }
      }
    ]
    return this._aggregate(puuid, matchParams, [], '$role', {}, finalSteps)
  }
  /**
   * Get Summoner's played seasons
   * @param puuid of the summoner
   */
  seasons(puuid) {
    this.season = null

    return this.Match.query().aggregate([
      {
        $match: {
          ...this._matchParams(puuid),
        }
      },
      {
        $group: { _id: '$season' }
      },
    ])
  }

  /**
   * Get Summoner's mates list
   * @param puuid of the summoner
   */
  mates(puuid) {
    const intermediateSteps = [
      { $sort: { 'gameId': -1 } },
      { $unwind: '$allyTeam' },
    ]
    const groupParams = {
      account_id: { $first: '$account_id' },
      name: { $first: '$allyTeam.name' },
      mateId: { $first: '$allyTeam.account_id' },
    }
    const finalSteps = [
      {
        '$addFields': {
          'idEq': { '$eq': ['$mateId', '$account_id'] }
        }
      },
      {
        $match: {
          'idEq': false,
          'count': { $gte: 2 }
        },
      },
      { $sort: { 'count': -1, 'name': 1 } },
      { $limit: 15 },
    ]
    return this._aggregate(puuid, {}, intermediateSteps, '$allyTeam.account_id', groupParams, finalSteps)
  }
}

module.exports = MatchRepository

И вот как я его использую:

async getSummonerStats(account, season) {
  this.matchRepository.season = season
  const globalStats = await this.matchRepository.globalStats(account.puuid)
  const gamemodeStats = await this.matchRepository.gamemodeStats(account.puuid)
  const roleStats = await this.matchRepository.roleStats(account.puuid)
  // Check if all roles are in the array
  const roles = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
  for (const role of roles) {
    if (!roleStats.find(r => r.role === role)) {
      roleStats.push({
        count: 0,
        losses: 0,
        role,
        wins: 0
      })
    }
  }
  const championStats = await this.matchRepository.championStats(account.puuid, 5)
  const championClassStats = await this.matchRepository.championClassStats(account.puuid)
  const mates = await this.matchRepository.mates(account.puuid)

  return {
    global: globalStats[0],
    league: gamemodeStats,
    role: roleStats.sort(Helpers.sortTeamByRole),
    class: championClassStats,
    mates,
    champion: championStats,
  }
}

Как это может быть так плохо? Я использую 5 $ VPS, но это было очень быстро с 30 тыс. Документов в базе данных. Я не понимаю, как это оказалось так медленно с «только» 70 тысяч документов.

...