diff --git a/app/Env.scala b/app/Env.scala index 983e89be8f..3469288d4d 100644 --- a/app/Env.scala +++ b/app/Env.scala @@ -29,6 +29,7 @@ final class Env( bookmarkApi = Env.bookmark.api, eloCalculator = Env.round.eloCalculator, relationApi = Env.relation.api, + gameCached = Env.game.cached, postApi = Env.forum.postApi, getRank = Env.user.ranking.get) _ diff --git a/app/controllers/Tv.scala b/app/controllers/Tv.scala index f120b22006..82fcaf1c5b 100644 --- a/app/controllers/Tv.scala +++ b/app/controllers/Tv.scala @@ -36,7 +36,7 @@ object Tv extends LilaController { private def confrontation(game: GameModel): Fu[Option[Confrontation]] = ~{ (game.creator.userId |@| game.invited.userId) apply { case (id1, id2) ⇒ (UserRepo byId id1) zip (UserRepo byId id2) flatMap { - case (Some(user1), Some(user2)) ⇒ GameRepo.confrontation(user1, user2) map (_.some) + case (Some(user1), Some(user2)) ⇒ Env.game.cached.confrontation(user1, user2) map (_.some) case _ ⇒ fuccess(none) } } diff --git a/app/mashup/UserInfo.scala b/app/mashup/UserInfo.scala index 6b8af6c953..26e072ab26 100644 --- a/app/mashup/UserInfo.scala +++ b/app/mashup/UserInfo.scala @@ -36,16 +36,17 @@ object UserInfo { bookmarkApi: BookmarkApi, eloCalculator: EloCalculator, relationApi: RelationApi, + gameCached: lila.game.Cached, postApi: PostApi, getRank: String ⇒ Fu[Option[Int]])(user: User, ctx: Context): Fu[UserInfo] = (getRank(user.id) flatMap { _ ?? { rank ⇒ countUsers() map { nb ⇒ (rank -> nb).some } } }) zip ((ctx is user) ?? { - GameRepo count (_ notFinished user.id) map (_.some) + gameCached nbPlaying user.id map (_.some) }) zip (ctx.me.filter(user!=) ?? { me ⇒ - GameRepo.confrontation(me, user) map (_.some filterNot (_.empty)) + gameCached.confrontation(me, user) map (_.some filterNot (_.empty)) }) zip (bookmarkApi countByUser user) zip EloChart(user) zip diff --git a/app/views/user/confrontation.scala.html b/app/views/user/confrontation.scala.html index 6b4aa2c93b..8de55e1dda 100644 --- a/app/views/user/confrontation.scala.html +++ b/app/views/user/confrontation.scala.html @@ -1,7 +1,7 @@ @(c: lila.user.Confrontation)(implicit ctx: Context)
-
@userLink(c.user1, withElo = false, withOnline = false) vs @userLink(c.user2, withElo = false, withOnline = false)
+
@userIdLink(c.user1.some, withOnline = false) vs @userIdLink(c.user2.some, withOnline = false)
@trans.nbWins(""+c.wins+""), @trans.nbDraws(""+c.draws+""), @trans.nbLosses(""+c.losses+"") diff --git a/modules/game/src/main/Cached.scala b/modules/game/src/main/Cached.scala index 1bfb233d8e..f7a2bb09d1 100644 --- a/modules/game/src/main/Cached.scala +++ b/modules/game/src/main/Cached.scala @@ -9,15 +9,23 @@ import lila.memo.AsyncCache import lila.user.{ User, Confrontation } import tube.gameTube -private[game] final class Cached(ttl: Duration) { +final class Cached(ttl: Duration) { def nbGames: Fu[Int] = count(Query.all) def nbMates: Fu[Int] = count(Query.mate) def nbPopular: Fu[Int] = count(Query.popular) def nbImported: Fu[Int] = count(Query.imported) - def confrontation(user1: User, user2: User): Fu[Confrontation] = - confrontationCache(List(user1, user2).sortBy(_.count.game).map(_.id)) + def nbPlaying(userId: String): Fu[Int] = count(Query notFinished userId) + + def confrontation(user1: User, user2: User): Fu[Confrontation] = { + def idGame(user: User) = (user.id, user.count.game) + confrontationCache { + (user1 < user2).fold( + idGame(user1) -> idGame(user2), + idGame(user2) -> idGame(user1)) + } + } private val confrontationCache = AsyncCache(GameRepo.confrontation, timeToLive = 1.minute) diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala index 2550a369a6..49e3725530 100644 --- a/modules/game/src/main/GameRepo.scala +++ b/modules/game/src/main/GameRepo.scala @@ -226,32 +226,37 @@ object GameRepo { _ sort Query.sortCreated skip (Random nextInt 1000) ) - // user1 wins, draws, losses + // gets 2 users (id, nbGames) + // returns user1 wins, draws, losses // the 2 userIds SHOULD be sorted by game count desc // this method is cached in lila.game.Cached - private[game] def confrontation(userIds: List[String]): Fu[Confrontation] = { - import reactivemongo.bson._ - import reactivemongo.core.commands._ - val command = Aggregate(gameTube.coll.name, Seq( - Match(BSONDocument( - "uids" -> BSONDocument("$all" -> userIds), - "s" -> BSONDocument("$gte" -> chess.Status.Mate.id) - )), - GroupField("wid")("nb" -> SumValue(1)) - )) - gameTube.coll.db.command(command) map { stream ⇒ - val res = (stream.toList map { obj ⇒ - toJSON(obj).asOpt[JsObject] flatMap { o ⇒ - o int "nb" map { nb ⇒ - ~(o str "_id") -> nb + private[game] def confrontation(users: ((String, Int), (String, Int))): Fu[Confrontation] = users match { + case (user1, user2) ⇒ { + import reactivemongo.bson._ + import reactivemongo.core.commands._ + val userIds = List(user1, user2).sortBy(_._2).map(_._1) + val command = Aggregate(gameTube.coll.name, Seq( + Match(BSONDocument( + "uids" -> BSONDocument("$all" -> userIds), + "s" -> BSONDocument("$gte" -> chess.Status.Mate.id) + )), + GroupField("wid")("nb" -> SumValue(1)) + )) + gameTube.coll.db.command(command) map { stream ⇒ + val res = (stream.toList map { obj ⇒ + toJSON(obj).asOpt[JsObject] flatMap { o ⇒ + o int "nb" map { nb ⇒ + ~(o str "_id") -> nb + } } - } - }).flatten.toMap - Confrontation( - ~(res get ~userIds.lift(0)), - ~(res get ""), - ~(res get ~userIds.lift(1)) - ) + }).flatten.toMap + Confrontation( + user1._1, user2._1, + ~(res get user1._1), + ~(res get ""), + ~(res get user2._1) + ) + } } } } diff --git a/modules/user/src/main/Confrontation.scala b/modules/user/src/main/Confrontation.scala index 9701d126ac..99baef2407 100644 --- a/modules/user/src/main/Confrontation.scala +++ b/modules/user/src/main/Confrontation.scala @@ -1,6 +1,11 @@ package lila.user -case class Confrontation(wins: Int, draws: Int, losses: Int) { +case class Confrontation( + user1: String, + user2: String, + wins: Int, + draws: Int, + losses: Int) { def games = wins + draws + losses