make all queries safer

use reactivemongo stopOnError=false by default, with new API uno/gather
killPRM
Thibault Duplessis 2016-04-02 17:13:25 +07:00
parent 1eedc9082b
commit 8c6d8c5428
69 changed files with 307 additions and 274 deletions

View File

@ -49,11 +49,11 @@ final class Env(
object assetVersion {
import reactivemongo.bson._
import lila.db.dsl._
private val coll = db("flag")
private val cache = lila.memo.MixedCache.single[Int](
f = coll.find(BSONDocument("_id" -> "asset")).one[BSONDocument].map {
_.flatMap(_.getAs[BSONNumberLike]("version"))
.fold(Net.AssetVersion)(_.toInt max Net.AssetVersion)
f = coll.primitiveOne[BSONNumberLike]($id("asset"), "version").map {
_.fold(Net.AssetVersion)(_.toInt max Net.AssetVersion)
},
timeToLive = 30.seconds,
default = Net.AssetVersion,

View File

@ -29,7 +29,7 @@ private[bookmark] final class PaginatorBuilder(
.sort(sorting)
.skip(offset)
.cursor[Bdoc]()
.collect[List](length) map { _ flatMap { _.getAs[String]("g") } }
.gather[List](length) map { _ flatMap { _.getAs[String]("g") } }
games GameRepo games gameIds
} yield games map { g => Bookmark(g, user) }

View File

@ -11,7 +11,7 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
import BSONHandlers._
import Challenge._
def byId(id: Challenge.ID) = coll.find($id(id)).one[Challenge]
def byId(id: Challenge.ID) = coll.find($id(id)).uno[Challenge]
def exists(id: Challenge.ID) = coll.count($id(id).some).map(0<)
@ -26,12 +26,12 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
def createdByChallengerId(userId: String): Fu[List[Challenge]] =
coll.find(selectCreated ++ $doc("challenger.id" -> userId))
.sort($doc("createdAt" -> 1))
.cursor[Challenge]().collect[List]()
.cursor[Challenge]().gather[List]()
def createdByDestId(userId: String): Fu[List[Challenge]] =
coll.find(selectCreated ++ $doc("destUser.id" -> userId))
.sort($doc("createdAt" -> 1))
.cursor[Challenge]().collect[List]()
.cursor[Challenge]().gather[List]()
def removeByUserId(userId: String): Funit =
coll.remove($doc("$or" -> $arr(
@ -45,7 +45,7 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
if c.active
} yield coll.find(selectCreated ++ $doc(
"challenger.id" -> challengerId,
"destUser.id" -> destUserId)).one[Challenge])
"destUser.id" -> destUserId)).uno[Challenge])
private[challenge] def countCreatedByDestId(userId: String): Fu[Int] =
coll.count(Some(selectCreated ++ $doc("destUser.id" -> userId)))
@ -53,7 +53,7 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
private[challenge] def realTimeUnseenSince(date: DateTime, max: Int): Fu[List[Challenge]] =
coll.find(selectCreated ++ selectClock ++ $doc(
"seenAt" -> $doc("$lt" -> date)
)).cursor[Challenge]().collect[List](max)
)).cursor[Challenge]().gather[List](max)
private[challenge] def expiredIds(max: Int): Fu[List[Challenge.ID]] =
coll.distinct(
@ -83,7 +83,7 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
def statusById(id: Challenge.ID) = coll.find(
$id(id),
$doc("status" -> true, "_id" -> false)
).one[Bdoc].map { _.flatMap(_.getAs[Status]("status")) }
).uno[Bdoc].map { _.flatMap(_.getAs[Status]("status")) }
private def setStatus(
challenge: Challenge,

View File

@ -18,7 +18,7 @@ final class ChatApi(
object userChat {
def findOption(chatId: ChatId): Fu[Option[UserChat]] =
coll.find(BSONDocument("_id" -> chatId)).one[UserChat]
coll.find(BSONDocument("_id" -> chatId)).uno[UserChat]
def find(chatId: ChatId): Fu[UserChat] =
findOption(chatId) map (_ | Chat.makeUser(chatId))
@ -52,7 +52,7 @@ final class ChatApi(
object playerChat {
def findOption(chatId: ChatId): Fu[Option[MixedChat]] =
coll.find(BSONDocument("_id" -> chatId)).one[MixedChat]
coll.find(BSONDocument("_id" -> chatId)).uno[MixedChat]
def find(chatId: ChatId): Fu[MixedChat] =
findOption(chatId) map (_ | Chat.makeMixed(chatId))

View File

@ -1,6 +1,6 @@
package lila.coordinate
import lila.db.dsl.Coll
import lila.db.dsl._
import reactivemongo.bson._
final class CoordinateApi(scoreColl: Coll) {
@ -8,7 +8,7 @@ final class CoordinateApi(scoreColl: Coll) {
private implicit val scoreBSONHandler = Macros.handler[Score]
def getScore(userId: String): Fu[Score] =
scoreColl.find(BSONDocument("_id" -> userId)).one[Score] map (_ | Score(userId))
scoreColl.byId[Score](userId) map (_ | Score(userId))
def addScore(userId: String, white: Boolean, hits: Int): Funit =
scoreColl.update(

View File

@ -3,25 +3,25 @@ package lila.db
import reactivemongo.api._
import reactivemongo.bson._
trait CollExt { self: dsl =>
trait CollExt { self: dsl with QueryBuilderExt =>
final implicit class ExtendColl(coll: Coll) {
def one[D: BSONDocumentReader](selector: BSONDocument): Fu[Option[D]] =
coll.find(selector).one[D]
def uno[D: BSONDocumentReader](selector: BSONDocument): Fu[Option[D]] =
coll.find(selector).uno[D]
def list[D: BSONDocumentReader](selector: BSONDocument): Fu[List[D]] =
coll.find(selector).cursor[D]().collect[List]()
coll.find(selector).list[D]()
def list[D: BSONDocumentReader](selector: BSONDocument, max: Int): Fu[List[D]] =
coll.find(selector).cursor[D]().collect[List](max)
coll.find(selector).list[D](max)
def byId[D: BSONDocumentReader, I: BSONValueWriter](id: I): Fu[Option[D]] =
one[D]($id(id))
uno[D]($id(id))
def byId[D: BSONDocumentReader](id: String): Fu[Option[D]] = one[D]($id(id))
def byId[D: BSONDocumentReader](id: String): Fu[Option[D]] = uno[D]($id(id))
def byId[D: BSONDocumentReader](id: Int): Fu[Option[D]] = one[D]($id(id))
def byId[D: BSONDocumentReader](id: Int): Fu[Option[D]] = uno[D]($id(id))
def byIds[D: BSONDocumentReader, I: BSONValueWriter](ids: Iterable[I]): Fu[List[D]] =
list[D]($inIds(ids))
@ -49,14 +49,14 @@ trait CollExt { self: dsl =>
def primitive[V: BSONValueReader](selector: BSONDocument, field: String): Fu[List[V]] =
coll.find(selector, $doc(field -> true))
.cursor[BSONDocument]().collect[List]()
.list[BSONDocument]()
.map {
_ flatMap { _.getAs[V](field) }
}
def primitiveOne[V: BSONValueReader](selector: BSONDocument, field: String): Fu[Option[V]] =
coll.find(selector, $doc(field -> true))
.one[BSONDocument]
.uno[BSONDocument]
.map {
_ flatMap { _.getAs[V](field) }
}
@ -64,7 +64,7 @@ trait CollExt { self: dsl =>
def primitiveOne[V: BSONValueReader](selector: BSONDocument, sort: BSONDocument, field: String): Fu[Option[V]] =
coll.find(selector, $doc(field -> true))
.sort(sort)
.one[BSONDocument]
.uno[BSONDocument]
.map {
_ flatMap { _.getAs[V](field) }
}
@ -76,7 +76,7 @@ trait CollExt { self: dsl =>
coll.uncheckedUpdate(selector, $set(field -> value))
def fetchUpdate[D: BSONDocumentHandler](selector: BSONDocument)(update: D => BSONDocument): Funit =
one[D](selector) flatMap {
uno[D](selector) flatMap {
_ ?? { doc =>
coll.update(selector, update(doc)).void
}

View File

@ -0,0 +1,26 @@
package lila.db
import scala.collection.generic.CanBuildFrom
import reactivemongo.api._
import reactivemongo.bson._
trait CursorExt { self: dsl =>
final implicit class ExtendCursor[A: BSONDocumentReader](val c: Cursor[A]) {
// like collect, but with stopOnError defaulting to false
def gather[M[_]](upTo: Int = Int.MaxValue)(implicit cbf: CanBuildFrom[M[_], A, M[A]]): Fu[M[A]] =
c.collect[M](upTo, stopOnError = false)
def list(limit: Option[Int]): Fu[List[A]] = gather[List](limit | Int.MaxValue)
def list(limit: Int): Fu[List[A]] = list(limit.some)
def list(): Fu[List[A]] = list(none)
// like headOption, but with stopOnError defaulting to false
def uno: Fu[Option[A]] =
c.collect[Iterable](1, stopOnError = false).map(_.headOption)
}
}

View File

@ -30,5 +30,5 @@ final class Adapter[A: BSONDocumentReader](
.sort(sort)
.skip(offset)
.cursor[A](readPreference = readPreference)
.collect[List](length)
.gather[List](length)
}

View File

@ -1,5 +1,7 @@
package lila.db
import scala.collection.generic.CanBuildFrom
import reactivemongo.api._
import reactivemongo.api.collections.GenericQueryBuilder
import reactivemongo.bson._
@ -12,9 +14,23 @@ trait QueryBuilderExt { self: dsl =>
def batch(nb: Int) = b.options(b.options batchSize nb)
def toList[A: BSONDocumentReader](limit: Option[Int]): Fu[List[A]] =
limit.fold(b.cursor[A]().collect[List]()) { l =>
b.cursor[A]().collect[List](l)
}
// like collect, but with stopOnError defaulting to false
def gather[A, M[_]](upTo: Int = Int.MaxValue)(implicit cbf: CanBuildFrom[M[_], A, M[A]], reader: BSONDocumentReader[A]): Fu[M[A]] =
b.cursor[A]().collect[M](upTo, stopOnError = false)
def list[A: BSONDocumentReader](limit: Option[Int]): Fu[List[A]] = gather[A, List](limit | Int.MaxValue)
def list[A: BSONDocumentReader](limit: Int): Fu[List[A]] = list[A](limit.some)
def list[A: BSONDocumentReader](): Fu[List[A]] = list[A](none)
// like one, but with stopOnError defaulting to false
def uno[A: BSONDocumentReader]: Fu[Option[A]] = uno[A](ReadPreference.primary)
def uno[A: BSONDocumentReader](readPreference: ReadPreference): Fu[Option[A]] =
b.copy(options = b.options.batchSize(1))
.cursor[A](readPreference = readPreference)
.collect[Iterable](1, stopOnError = false)
.map(_.headOption)
}
}

View File

@ -2,14 +2,14 @@ package lila.db
import reactivemongo.bson._
import dsl.Coll
import dsl._
object Util {
def findNextId(coll: Coll): Fu[Int] =
coll.find(BSONDocument(), BSONDocument("_id" -> true))
.sort(BSONDocument("_id" -> -1))
.one[BSONDocument] map {
.uno[BSONDocument] map {
_ flatMap { doc => doc.getAs[Int]("_id") map (1+) } getOrElse 1
}
}

View File

@ -376,4 +376,4 @@ trait dsl {
}
object dsl extends dsl with CollExt with QueryBuilderExt with Handlers
object dsl extends dsl with CollExt with QueryBuilderExt with CursorExt with Handlers

View File

@ -37,7 +37,7 @@ final class DonationApi(
def list(nb: Int) = coll.find(decentAmount)
.sort(BSONDocument("date" -> -1))
.cursor[Donation]()
.collect[List](nb)
.gather[List](nb)
def top(nb: Int): Fu[List[lila.user.User.ID]] = coll.aggregate(
Match(BSONDocument("userId" -> BSONDocument("$exists" -> true))), List(
@ -84,7 +84,7 @@ final class DonationApi(
"$lt" -> to
)),
BSONDocument("net" -> true, "_id" -> false)
).cursor[BSONDocument]().collect[List]() map2 { (obj: BSONDocument) =>
).cursor[BSONDocument]().gather[List]() map2 { (obj: BSONDocument) =>
~obj.getAs[Int]("net")
} map (_.sum) map { amount =>
Progress(from, weeklyGoal, amount)

View File

@ -32,7 +32,7 @@ private final class Cleaner(
private def cleanAnalysis: Funit = analysisColl.find(BSONDocument(
"acquired.date" -> BSONDocument("$lt" -> durationAgo(analysisTimeoutBase))
)).sort(BSONDocument("acquired.date" -> 1)).cursor[Work.Analysis]().collect[List](100).flatMap {
)).sort(BSONDocument("acquired.date" -> 1)).cursor[Work.Analysis]().gather[List](100).flatMap {
_.filter { ana =>
ana.acquiredAt.??(_ isBefore durationAgo(analysisTimeout(ana.nbPly)))
}.map { ana =>

View File

@ -63,7 +63,7 @@ final class FishnetApi(
)).sort(BSONDocument(
"sender.system" -> 1, // user requests first, then lichess auto analysis
"createdAt" -> 1 // oldest requests first
)).one[Work.Analysis].flatMap {
)).uno[Work.Analysis].flatMap {
_ ?? { work =>
repo.updateAnalysis(work assignTo client) inject work.some
}

View File

@ -15,7 +15,7 @@ private final class FishnetRepo(
import BSONHandlers._
private val clientCache = AsyncCache[Client.Key, Option[Client]](
f = key => clientColl.find(selectClient(key)).one[Client],
f = key => clientColl.find(selectClient(key)).uno[Client],
timeToLive = 10 seconds)
def getClient(key: Client.Key) = clientCache(key)
@ -33,14 +33,14 @@ private final class FishnetRepo(
clientColl.update(selectClient(key), BSONDocument("$set" -> BSONDocument("enabled" -> v))).void >> clientCache.remove(key)
def allRecentClients = clientColl.find(BSONDocument(
"instance.seenAt" -> BSONDocument("$gt" -> Client.Instance.recentSince)
)).cursor[Client]().collect[List]()
)).cursor[Client]().gather[List]()
def lichessClients = clientColl.find(BSONDocument(
"enabled" -> true,
"userId" -> BSONDocument("$regex" -> "^lichess-")
)).cursor[Client]().collect[List]()
)).cursor[Client]().gather[List]()
def addAnalysis(ana: Work.Analysis) = analysisColl.insert(ana).void
def getAnalysis(id: Work.Id) = analysisColl.find(selectWork(id)).one[Work.Analysis]
def getAnalysis(id: Work.Id) = analysisColl.find(selectWork(id)).uno[Work.Analysis]
def updateAnalysis(ana: Work.Analysis) = analysisColl.update(selectWork(ana.id), ana).void
def deleteAnalysis(ana: Work.Analysis) = analysisColl.remove(selectWork(ana.id)).void
def giveUpAnalysis(ana: Work.Analysis) = deleteAnalysis(ana) >>- logger.warn(s"Give up on analysis $ana")
@ -50,10 +50,10 @@ private final class FishnetRepo(
).some)
def getAnalysisByGameId(gameId: String) = analysisColl.find(BSONDocument(
"game.id" -> gameId
)).one[Work.Analysis]
)).uno[Work.Analysis]
def getSimilarAnalysis(work: Work.Analysis): Fu[Option[Work.Analysis]] =
analysisColl.find(BSONDocument("game.id" -> work.game.id)).one[Work.Analysis]
analysisColl.find(BSONDocument("game.id" -> work.game.id)).uno[Work.Analysis]
def selectWork(id: Work.Id) = BSONDocument("_id" -> id.value)
def selectClient(key: Client.Key) = BSONDocument("_id" -> key.value)

View File

@ -15,7 +15,7 @@ object CategRepo {
coll.find($or(
"team" $exists false,
"team" $in teams
)).sort($sort asc "pos").cursor[Categ]().collect[List]()
)).sort($sort asc "pos").cursor[Categ]().gather[List]()
def nextPosition: Fu[Int] =
coll.primitiveOne[Int]($empty, $sort desc "pos", "pos") map (~_ + 1)

View File

@ -20,7 +20,7 @@ sealed abstract class PostRepo(troll: Boolean) {
private val trollFilter = troll.fold($empty, $doc("troll" -> false))
def byCategAndId(categSlug: String, id: String): Fu[Option[Post]] =
coll.one(selectCateg(categSlug) ++ $id(id))
coll.uno[Post](selectCateg(categSlug) ++ $id(id))
def countBeforeNumber(topicId: String, number: Int): Fu[Int] =
coll.countSel(selectTopic(topicId) ++ $doc("number" -> $lt(number)))
@ -32,19 +32,19 @@ sealed abstract class PostRepo(troll: Boolean) {
coll.countSel(selectTopics(topics))
def lastByTopics(topics: List[String]): Fu[Option[Post]] =
coll.find(selectTopics(topics)).sort($sort.createdDesc).one[Post]
coll.find(selectTopics(topics)).sort($sort.createdDesc).uno[Post]
def recentInCategs(nb: Int)(categIds: List[String], langs: List[String]): Fu[List[Post]] =
coll.find(
selectCategs(categIds) ++ selectLangs(langs) ++ selectNotHidden
).sort($sort.createdDesc).cursor[Post]().collect[List](nb)
).sort($sort.createdDesc).cursor[Post]().gather[List](nb)
def removeByTopic(topicId: String): Funit =
coll.remove(selectTopic(topicId)).void
def hideByTopic(topicId: String, value: Boolean): Funit = coll.update(
selectTopic(topicId),
$doc("$set" -> $doc("hidden" -> value)),
$set("hidden" -> value),
multi = true).void
def selectTopic(topicId: String) = $doc("topicId" -> topicId) ++ trollFilter
@ -59,7 +59,7 @@ sealed abstract class PostRepo(troll: Boolean) {
if (langs.isEmpty) $empty
else $doc("lang" $in langs)
def findDuplicate(post: Post): Fu[Option[Post]] = coll.one($doc(
def findDuplicate(post: Post): Fu[Option[Post]] = coll.uno[Post]($doc(
"createdAt" $gt DateTime.now.minusHours(1),
"userId" -> ~post.userId,
"text" -> post.text

View File

@ -28,7 +28,7 @@ sealed abstract class TopicRepo(troll: Boolean) {
coll.list[Topic](byCategQuery(categ))
def byTree(categSlug: String, slug: String): Fu[Option[Topic]] =
coll.one[Topic]($doc("categId" -> categSlug, "slug" -> slug) ++ trollFilter)
coll.uno[Topic]($doc("categId" -> categSlug, "slug" -> slug) ++ trollFilter)
def nextSlug(categ: Categ, name: String, it: Int = 1): Fu[String] = {
val slug = Topic.nameToId(name) + ~(it != 1).option("-" + it)

View File

@ -22,14 +22,14 @@ final class CrosstableApi(
}
def apply(u1: String, u2: String): Fu[Option[Crosstable]] =
coll.find(select(u1, u2)).one[Crosstable] orElse create(u1, u2) recoverWith
lila.db.recoverDuplicateKey(_ => coll.find(select(u1, u2)).one[Crosstable])
coll.find(select(u1, u2)).uno[Crosstable] orElse create(u1, u2) recoverWith
lila.db.recoverDuplicateKey(_ => coll.find(select(u1, u2)).uno[Crosstable])
def nbGames(u1: String, u2: String): Fu[Int] =
coll.find(
select(u1, u2),
BSONDocument("n" -> true)
).one[BSONDocument] map {
).uno[BSONDocument] map {
~_.flatMap(_.getAs[Int]("n"))
}
@ -79,7 +79,7 @@ final class CrosstableApi(
BSONDocument(Game.BSONFields.winnerId -> true)
).sort(BSONDocument(Game.BSONFields.createdAt -> -1))
.cursor[BSONDocument](readPreference = ReadPreference.secondaryPreferred)
.collect[List](maxGames).map {
.gather[List](maxGames).map {
_.flatMap { doc =>
doc.getAs[String](Game.BSONFields.id).map { id =>
Result(id, doc.getAs[String](Game.BSONFields.winnerId))

View File

@ -31,7 +31,7 @@ object GameRepo {
coll.optionsByOrderedIds[Game](gameIds)(_.id)
def finished(gameId: ID): Fu[Option[Game]] =
coll.one[Game]($id(gameId) ++ Query.finished)
coll.uno[Game]($id(gameId) ++ Query.finished)
def player(gameId: ID, color: Color): Fu[Option[Player]] =
game(gameId) map2 { (game: Game) => game player color }
@ -67,7 +67,7 @@ object GameRepo {
coll.byOrderedIds[Game](gameIds)(_.id) map { _.flatMap(g => Pov(g, user)) }
def recentPovsByUser(user: User, nb: Int): Fu[List[Pov]] =
coll.find(Query user user).sort(Query.sortCreated).cursor[Game]().collect[List](nb)
coll.find(Query user user).sort(Query.sortCreated).cursor[Game]().gather[List](nb)
.map { _.flatMap(g => Pov(g, user)) }
def gamesForAssessment(userId: String, nb: Int): Fu[List[Game]] = coll.find(
@ -79,7 +79,7 @@ object GameRepo {
++ Query.clock(true))
.sort($sort asc F.createdAt)
.cursor[Game]()
.collect[List](nb)
.gather[List](nb)
def cursor(
selector: Bdoc,
@ -148,14 +148,14 @@ object GameRepo {
def lastPlayedPlaying(user: User): Fu[Option[Pov]] =
coll.find(Query recentlyPlaying user.id)
.sort(Query.sortUpdatedNoIndex)
.one[Game]
.uno[Game]
.map { _ flatMap { Pov(_, user) } }
def lastPlayed(user: User): Fu[Option[Pov]] =
coll.find(Query user user.id)
.sort($sort desc F.createdAt)
.cursor[Game]()
.collect[List](20).map {
.gather[List](20).map {
_.sortBy(_.updatedAt).lastOption flatMap { Pov(_, user) }
}
@ -165,7 +165,7 @@ object GameRepo {
Query.finished ++
Query.turnsMoreThan(2) ++
Query.notFromPosition
).sort(Query.sortAntiChronological).one[Game]
).sort(Query.sortAntiChronological).uno[Game]
def setTv(id: ID) {
coll.updateFieldUnchecked($id(id), F.tvAt, DateTime.now)
@ -174,7 +174,7 @@ object GameRepo {
def onTv(nb: Int): Fu[List[Game]] = coll.find($doc(F.tvAt $exists true))
.sort($sort desc F.tvAt)
.cursor[Game]()
.collect[List](nb)
.gather[List](nb)
def setAnalysed(id: ID) {
coll.updateFieldUnchecked($id(id), F.analysed, true)
@ -241,7 +241,7 @@ object GameRepo {
Query.mate ++ $doc("v" $exists false)
).sort(Query.sortCreated)
.skip(Random nextInt distribution)
.one[Game]
.uno[Game]
def insertDenormalized(g: Game, ratedCheck: Boolean = true, initialFen: Option[chess.format.FEN] = None): Funit = {
val g2 = if (ratedCheck && g.rated && g.userIds.distinct.size != 2)
@ -345,9 +345,9 @@ object GameRepo {
def random: Fu[Option[Game]] = coll.find($empty)
.sort(Query.sortCreated)
.skip(Random nextInt 1000)
.one[Game]
.uno[Game]
def findMirror(game: Game): Fu[Option[Game]] = coll.one[Game]($doc(
def findMirror(game: Game): Fu[Option[Game]] = coll.uno[Game]($doc(
F.id -> $doc("$ne" -> game.id),
F.playerUids -> $doc("$in" -> game.userIds),
F.status -> Status.Started.id,
@ -360,7 +360,7 @@ object GameRepo {
F.binaryPieces -> game.binaryPieces
))
def findPgnImport(pgn: String): Fu[Option[Game]] = coll.one[Game](
def findPgnImport(pgn: String): Fu[Option[Game]] = coll.uno[Game](
$doc(s"${F.pgnImport}.h" -> PgnImport.hash(pgn))
)
@ -375,10 +375,10 @@ object GameRepo {
F.id -> false,
F.binaryPgn -> true
)
).one[Bdoc] map { _ flatMap extractPgnMoves }
).uno[Bdoc] map { _ flatMap extractPgnMoves }
def lastGameBetween(u1: String, u2: String, since: DateTime): Fu[Option[Game]] =
coll.one[Game]($doc(
coll.uno[Game]($doc(
F.playerUids $all List(u1, u2),
F.createdAt $gt since
))
@ -389,7 +389,7 @@ object GameRepo {
F.id -> false,
F.playerUids -> true
)
).one[Bdoc] map { ~_.flatMap(_.getAs[List[String]](F.playerUids)) }
).uno[Bdoc] map { ~_.flatMap(_.getAs[List[String]](F.playerUids)) }
// #TODO this breaks it all since reactivemongo > 0.11.9
def activePlayersSinceNOPENOPENOPE(since: DateTime, max: Int): Fu[List[UidNb]] = {

View File

@ -45,5 +45,5 @@ final class HistoryApi(coll: Coll) {
Days.daysBetween(from.withTimeAtStartOfDay, to.withTimeAtStartOfDay).getDays
def get(userId: String): Fu[Option[History]] =
coll.find(BSONDocument("_id" -> userId)).one[History]
coll.find(BSONDocument("_id" -> userId)).uno[History]
}

View File

@ -15,7 +15,7 @@ private[i18n] final class TranslationRepo(coll: Coll) {
"_id") map (opt => ~opt + 1)
def findFrom(id: Int): Fu[List[Translation]] =
coll.find($doc("_id" $gte id)).sort($sort asc "_id").cursor[Translation]().collect[List]()
coll.find($doc("_id" $gte id)).sort($sort asc "_id").cursor[Translation]().gather[List]()
def insert(t: Translation) = coll insert t
}

View File

@ -56,11 +56,11 @@ private final class Indexer(storage: Storage, sequencer: ActorRef) {
.find(gameQuery(user))
.sort(Query.sortCreated)
.skip(maxGames - 1)
.one[Game]
.uno[Game]
} orElse GameRepo.coll
.find(gameQuery(user))
.sort(Query.sortCreated)
.one[Game]
.uno[Game]
private def computeFrom(user: User, from: DateTime, fromNumber: Int): Funit = {
storage nbByPerf user.id flatMap { nbs =>

View File

@ -21,10 +21,10 @@ private final class Storage(coll: Coll) {
coll.aggregate(operators.head, operators.tail, allowDiskUse = true)
def fetchFirst(userId: String): Fu[Option[Entry]] =
coll.find(selectUserId(userId)).sort(sortChronological).one[Entry]
coll.find(selectUserId(userId)).sort(sortChronological).uno[Entry]
def fetchLast(userId: String): Fu[Option[Entry]] =
coll.find(selectUserId(userId)).sort(sortAntiChronological).one[Entry]
coll.find(selectUserId(userId)).sort(sortAntiChronological).uno[Entry]
def count(userId: String): Fu[Int] =
coll.count(selectUserId(userId).some)
@ -41,7 +41,7 @@ private final class Storage(coll: Coll) {
def removeAll(userId: String) = coll.remove(selectUserId(userId)).void
def find(id: String) = coll.find(selectId(id)).one[Entry]
def find(id: String) = coll.find(selectId(id)).uno[Entry]
def ecos(userId: String): Fu[Set[String]] =
coll.distinct(F.eco, selectUserId(userId).some) map lila.db.BSON.asStringSet

View File

@ -19,7 +19,7 @@ private final class UserCacheApi(coll: Coll) {
private implicit val userCacheBSONHandler = Macros.handler[UserCache]
def find(id: String) = coll.one[UserCache]($id(id))
def find(id: String) = coll.uno[UserCache]($id(id))
def save(u: UserCache) = coll.update($id(u.id), u, upsert = true).void

View File

@ -27,8 +27,8 @@ final class SeekApi(
private val cache = AsyncCache[CacheKey, List[Seek]](
f = {
case ForAnon => allCursor.collect[List](maxPerPage)
case ForUser => allCursor.collect[List]()
case ForAnon => allCursor.gather[List](maxPerPage)
case ForUser => allCursor.gather[List]()
},
timeToLive = 3.seconds)
@ -56,7 +56,7 @@ final class SeekApi(
}._1.reverse
def find(id: String): Fu[Option[Seek]] =
coll.find($doc("_id" -> id)).one[Seek]
coll.find($doc("_id" -> id)).uno[Seek]
def insert(seek: Seek) = coll.insert(seek) >> findByUser(seek.user.id).flatMap {
case seeks if seeks.size <= maxPerUser => funit
@ -67,7 +67,7 @@ final class SeekApi(
def findByUser(userId: String): Fu[List[Seek]] =
coll.find($doc("user.id" -> userId))
.sort($doc("createdAt" -> -1))
.cursor[Seek]().collect[List]()
.cursor[Seek]().gather[List]()
def remove(seek: Seek) =
coll.remove($doc("_id" -> seek.id)).void >> cache.clear
@ -82,7 +82,7 @@ final class SeekApi(
}
def findArchived(gameId: String): Fu[Option[Seek]] =
archiveColl.find($doc("gameId" -> gameId)).one[Seek]
archiveColl.find($doc("gameId" -> gameId)).uno[Seek]
def removeBy(seekId: String, userId: String) =
coll.remove($doc(

View File

@ -18,7 +18,7 @@ final class MongoCache[K, V: MongoCache.Handler] private (
keyToString: K => String) {
def apply(k: K): Fu[V] = cache(k) {
coll.find(select(k)).one[Entry] flatMap {
coll.find(select(k)).uno[Entry] flatMap {
case None => f(k) flatMap { v =>
coll.insert(makeEntry(k, v)) recover
lila.db.recoverDuplicateKey(_ => ()) inject v

View File

@ -13,13 +13,13 @@ object ThreadRepo {
type ID = String
def byUser(user: ID): Fu[List[Thread]] =
coll.find(userQuery(user)).sort(recentSort).cursor[Thread]().collect[List]()
coll.find(userQuery(user)).sort(recentSort).cursor[Thread]().gather[List]()
def visibleByUser(user: ID): Fu[List[Thread]] =
coll.find(visibleByUserQuery(user)).sort(recentSort).cursor[Thread]().collect[List]()
coll.find(visibleByUserQuery(user)).sort(recentSort).cursor[Thread]().gather[List]()
def visibleByUser(user: ID, nb: Int): Fu[List[Thread]] =
coll.find(visibleByUserQuery(user)).sort(recentSort).cursor[Thread]().collect[List](nb)
coll.find(visibleByUserQuery(user)).sort(recentSort).cursor[Thread]().gather[List](nb)
def userUnreadIds(userId: String): Fu[List[String]] = coll.aggregate(
Match($doc(

View File

@ -3,7 +3,7 @@ package lila.mod
import akka.actor.ActorSelection
import lila.analyse.{ Analysis, AnalysisRepo }
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.db.dsl.Coll
import lila.db.dsl._
import lila.evaluation.Statistics
import lila.evaluation.{ AccountAction, Analysed, GameAssessment, PlayerAssessment, PlayerAggregateAssessment, PlayerFlags, PlayerAssessments, Assessible }
import lila.game.{ Game, Player, GameRepo, Source, Pov }
@ -29,17 +29,16 @@ final class AssessApi(
private implicit val playerAssessmentBSONhandler = Macros.handler[PlayerAssessment]
def createPlayerAssessment(assessed: PlayerAssessment) =
collAssessments.update(BSONDocument("_id" -> assessed._id), assessed, upsert = true).void
collAssessments.update($id(assessed._id), assessed, upsert = true).void
def getPlayerAssessmentById(id: String) =
collAssessments.find(BSONDocument("_id" -> id))
.one[PlayerAssessment]
collAssessments.byId[PlayerAssessment](id)
def getPlayerAssessmentsByUserId(userId: String, nb: Int = 100) =
collAssessments.find(BSONDocument("userId" -> userId))
.sort(BSONDocument("date" -> -1))
collAssessments.find($doc("userId" -> userId))
.sort($doc("date" -> -1))
.cursor[PlayerAssessment]()
.collect[List](nb)
.gather[List](nb)
def getResultsByGameIdAndColor(gameId: String, color: Color) =
getPlayerAssessmentById(gameId + "/" + color.name)

View File

@ -2,7 +2,7 @@ package lila.mod
import chess.Color
import chess.variant
import lila.db.dsl.Coll
import lila.db.dsl._
import lila.game.Game
import lila.user.User
@ -25,11 +25,10 @@ final class BoostingApi(
variant.ThreeCheck)
def getBoostingRecord(id: String): Fu[Option[BoostingRecord]] =
collBoosting.find(BSONDocument("_id" -> id))
.one[BoostingRecord]
collBoosting.byId[BoostingRecord](id)
def createBoostRecord(record: BoostingRecord) =
collBoosting.update(BSONDocument("_id" -> record.id), record, upsert = true).void
collBoosting.update($id(record.id), record, upsert = true).void
def determineBoosting(record: BoostingRecord, winner: User, loser: User): Funit =
(record.games >= nbGamesToMark) ?? {

View File

@ -22,7 +22,7 @@ final class Gamify(
historyColl.find($empty).sort($doc(
"year" -> -1,
"month" -> -1
)).cursor[HistoryMonth]().collect[List]().flatMap { months =>
)).cursor[HistoryMonth]().gather[List]().flatMap { months =>
months.headOption match {
case Some(m) if m._id == lastId || !orCompute => fuccess(months)
case Some(m) => buildHistoryAfter(m.year, m.month, until) >> history(false)

View File

@ -86,7 +86,7 @@ final class ModlogApi(coll: Coll) {
Modlog(mod, none, Modlog.terminateTournament, details = name.some)
}
def recent = coll.find($empty).sort($sort naturalDesc).cursor[Modlog]().collect[List](100)
def recent = coll.find($empty).sort($sort naturalDesc).cursor[Modlog]().gather[List](100)
def wasUnengined(userId: String) = coll.exists($doc(
"user" -> userId,
@ -99,7 +99,7 @@ final class ModlogApi(coll: Coll) {
))
def userHistory(userId: String): Fu[List[Modlog]] =
coll.find($doc("user" -> userId)).sort($sort desc "date").cursor[Modlog]().collect[List](100)
coll.find($doc("user" -> userId)).sort($sort desc "date").cursor[Modlog]().gather[List](100)
private def add(m: Modlog): Funit = {
lila.mon.mod.log.create()

View File

@ -28,7 +28,7 @@ private[opening] final class OpeningApi(
def find(openingId: Opening.ID, userId: String): Fu[Option[Attempt]] =
attemptColl.find($doc(
Attempt.BSONFields.id -> Attempt.makeId(openingId, userId)
)).one[Attempt]
)).uno[Attempt]
def add(a: Attempt) = attemptColl insert a void
@ -54,7 +54,7 @@ private[opening] final class OpeningApi(
def apply(fen: String, max: Int): Fu[List[String]] = nameColl.find(
$doc("_id" -> fen),
$doc("_id" -> false)
).one[Bdoc] map { obj =>
).uno[Bdoc] map { obj =>
~obj.??(_.getAs[List[String]]("names"))
}
}

View File

@ -20,7 +20,7 @@ private[opening] final class Selector(
case None =>
openingColl.find($empty)
.skip(Random nextInt anonSkipMax)
.one[Opening] flatten "Can't find a opening for anon player!"
.uno[Opening] flatten "Can't find a opening for anon player!"
case Some(user) => api.attempt.playedIds(user, modulo) flatMap { ids =>
tryRange(user, toleranceStep, ids)
} recoverWith {
@ -29,7 +29,7 @@ private[opening] final class Selector(
}).mon(_.opening.selector.time) >>- lila.mon.opening.selector.count()
private def tryRange(user: User, tolerance: Int, ids: BSONArray): Fu[Opening] =
openingColl.one[Opening]($doc(
openingColl.uno[Opening]($doc(
Opening.BSONFields.id $nin ids,
Opening.BSONFields.rating $gt
(user.perfs.opening.intRating - tolerance) $lt

View File

@ -34,7 +34,7 @@ object UserInfos {
Attempt.BSONFields.userId -> userId
)).sort(BSONDocument(
Attempt.BSONFields.date -> -1
)).cursor[Attempt]().collect[List](math.max(historySize, chartSize))
)).cursor[Attempt]().gather[List](math.max(historySize, chartSize))
}
private def makeHistory(attempts: List[Attempt]) = attempts.take(historySize)

View File

@ -8,7 +8,7 @@ import scala.concurrent.duration._
import chess.Color
import lila.db.BSON._
import lila.db.dsl.Coll
import lila.db.dsl._
import lila.game.{ Pov, Game, Player, Source }
final class PlaybanApi(
@ -48,23 +48,23 @@ final class PlaybanApi(
}
def currentBan(userId: String): Fu[Option[TempBan]] = coll.find(
BSONDocument("_id" -> userId, "b.0" -> BSONDocument("$exists" -> true)),
BSONDocument("_id" -> false, "b" -> BSONDocument("$slice" -> -1))
).one[BSONDocument].map {
$doc("_id" -> userId, "b.0" -> $doc("$exists" -> true)),
$doc("_id" -> false, "b" -> $doc("$slice" -> -1))
).uno[Bdoc].map {
_.flatMap(_.getAs[List[TempBan]]("b")).??(_.find(_.inEffect))
}
def bans(userId: String): Fu[List[TempBan]] = coll.find(
BSONDocument("_id" -> userId, "b.0" -> BSONDocument("$exists" -> true)),
BSONDocument("_id" -> false, "b" -> true)
).one[BSONDocument].map {
$doc("_id" -> userId, "b.0" -> $doc("$exists" -> true)),
$doc("_id" -> false, "b" -> true)
).uno[Bdoc].map {
~_.flatMap(_.getAs[List[TempBan]]("b"))
}
def bans(userIds: List[String]): Fu[Map[String, Int]] = coll.find(
BSONDocument("_id" -> BSONDocument("$in" -> userIds)),
BSONDocument("b" -> true)
).cursor[BSONDocument]().collect[List]().map {
$inIds(userIds),
$doc("b" -> true)
).cursor[Bdoc]().gather[List]().map {
_.flatMap { obj =>
obj.getAs[String]("_id") flatMap { id =>
obj.getAs[BSONArray]("b") map { id -> _.stream.size }
@ -74,9 +74,9 @@ final class PlaybanApi(
private def save(outcome: Outcome): String => Funit = userId => {
coll.findAndUpdate(
selector = BSONDocument("_id" -> userId),
update = BSONDocument("$push" -> BSONDocument(
"o" -> BSONDocument(
selector = $doc("_id" -> userId),
update = $doc("$push" -> $doc(
"o" -> $doc(
"$each" -> List(outcome),
"$slice" -> -20)
)),
@ -89,11 +89,11 @@ final class PlaybanApi(
private def legiferate(record: UserRecord): Funit = record.newBan ?? { ban =>
coll.update(
BSONDocument("_id" -> record.userId),
BSONDocument(
"$unset" -> BSONDocument("o" -> true),
"$push" -> BSONDocument(
"b" -> BSONDocument(
$doc("_id" -> record.userId),
$doc(
"$unset" -> $doc("o" -> true),
"$push" -> $doc(
"b" -> $doc(
"$each" -> List(ban),
"$slice" -> -30)
)

View File

@ -15,7 +15,7 @@ final class PrefApi(
cacheTtl: Duration,
bus: lila.common.Bus) {
private def fetchPref(id: String): Fu[Option[Pref]] = coll.find(BSONDocument("_id" -> id)).one[Pref]
private def fetchPref(id: String): Fu[Option[Pref]] = coll.find(BSONDocument("_id" -> id)).uno[Pref]
private val cache = AsyncCache(fetchPref, timeToLive = cacheTtl)
private implicit val prefBSONHandler = new BSON[Pref] {
@ -110,7 +110,7 @@ final class PrefApi(
def getPref[A](userId: String, pref: Pref => A): Fu[A] = getPref(userId) map pref
def followable(userId: String): Fu[Boolean] =
coll.find(BSONDocument("_id" -> userId), BSONDocument("follow" -> true)).one[BSONDocument] map {
coll.find(BSONDocument("_id" -> userId), BSONDocument("follow" -> true)).uno[BSONDocument] map {
_ flatMap (_.getAs[Boolean]("follow")) getOrElse Pref.default.follow
}

View File

@ -11,16 +11,16 @@ private final class DeviceApi(coll: Coll) {
private implicit val DeviceBSONHandler = Macros.handler[Device]
private[push] def findByDeviceId(deviceId: String): Fu[Option[Device]] =
coll.find($id(deviceId)).one[Device]
coll.find($id(deviceId)).uno[Device]
private[push] def findByUserId(userId: String): Fu[List[Device]] =
coll.find($doc("userId" -> userId)).cursor[Device]().collect[List]()
coll.find($doc("userId" -> userId)).cursor[Device]().gather[List]()
private[push] def findLastByUserId(platform: String)(userId: String): Fu[Option[Device]] =
coll.find($doc(
"platform" -> platform,
"userId" -> userId
)).sort($doc("seenAt" -> -1)).one[Device]
)).sort($doc("seenAt" -> -1)).uno[Device]
def register(user: User, platform: String, deviceId: String) = {
lila.mon.push.register.in(platform)()

View File

@ -44,11 +44,11 @@ private[puzzle] final class Daily(
private def findCurrent = coll.find(
$doc("day" -> $doc("$gt" -> DateTime.now.minusMinutes(24 * 60 - 15)))
).one[Puzzle]
).uno[Puzzle]
private def findNew = coll.find(
$doc("day" -> $doc("$exists" -> false))
).sort($doc("vote.sum" -> -1)).one[Puzzle] flatMap {
).sort($doc("vote.sum" -> -1)).uno[Puzzle] flatMap {
case Some(puzzle) => coll.update(
$doc("_id" -> puzzle.id),
$doc("$set" -> $doc("day" -> DateTime.now))

View File

@ -20,13 +20,13 @@ private[puzzle] final class PuzzleApi(
object puzzle {
def find(id: PuzzleId): Fu[Option[Puzzle]] =
puzzleColl.find($doc("_id" -> id)).one[Puzzle]
puzzleColl.find($doc("_id" -> id)).uno[Puzzle]
def latest(nb: Int): Fu[List[Puzzle]] =
puzzleColl.find($empty)
.sort($doc("date" -> -1))
.cursor[Puzzle]()
.collect[List](nb)
.gather[List](nb)
def importBatch(json: JsValue, token: String): Fu[List[Try[PuzzleId]]] =
if (token != apiToken) fufail("Invalid API token")
@ -57,7 +57,7 @@ private[puzzle] final class PuzzleApi(
def export(nb: Int): Fu[List[Puzzle]] = List(true, false).map { mate =>
puzzleColl.find($doc("mate" -> mate))
.sort($doc(Puzzle.BSONFields.voteSum -> -1))
.cursor[Puzzle]().collect[List](nb / 2)
.cursor[Puzzle]().gather[List](nb / 2)
}.sequenceFu.map(_.flatten)
def disable(id: PuzzleId): Funit =
@ -72,7 +72,7 @@ private[puzzle] final class PuzzleApi(
def find(puzzleId: PuzzleId, userId: String): Fu[Option[Attempt]] =
attemptColl.find($doc(
Attempt.BSONFields.id -> Attempt.makeId(puzzleId, userId)
)).one[Attempt]
)).uno[Attempt]
def vote(a1: Attempt, v: Boolean): Fu[(Puzzle, Attempt)] = puzzle find a1.puzzleId flatMap {
case None => fufail(s"Can't vote for non existing puzzle ${a1.puzzleId}")
@ -111,7 +111,7 @@ private[puzzle] final class PuzzleApi(
Attempt.BSONFields.id -> false
)).sort($doc(Attempt.BSONFields.date -> -1))
.cursor[Bdoc]()
.collect[List](5) map {
.gather[List](5) map {
case attempts if attempts.size < 5 => true
case attempts => attempts.foldLeft(false) {
case (true, _) => true

View File

@ -34,7 +34,7 @@ private[puzzle] final class Selector(
case None =>
puzzleColl.find(popularSelector(isMate) ++ mateSelector(isMate))
.skip(Random nextInt anonSkipMax)
.one[Puzzle]
.uno[Puzzle]
case Some(user) if user.perfs.puzzle.nb > maxAttempts => fuccess(none)
case Some(user) =>
val rating = user.perfs.puzzle.intRating min 2300 max 900
@ -59,7 +59,7 @@ private[puzzle] final class Selector(
(rating - tolerance + decay) $lt
(rating + tolerance + decay)
)).sort($sort desc Puzzle.BSONFields.voteSum)
.one[Puzzle] flatMap {
.uno[Puzzle] flatMap {
case None if (tolerance + step) <= toleranceMax =>
tryRange(rating, tolerance + step, step, decay, ids, isMate)
case res => fuccess(res)

View File

@ -39,7 +39,7 @@ object UserInfos {
)).sort(BSONDocument(
Attempt.BSONFields.date -> -1
)).cursor[Attempt]()
.collect[List](math.max(historySize, chartSize))
.gather[List](math.max(historySize, chartSize))
}
private def makeHistory(attempts: List[Attempt]) = attempts.take(historySize)

View File

@ -57,10 +57,10 @@ final class QaApi(
}
def findById(id: QuestionId): Fu[Option[Question]] =
questionColl.find(BSONDocument("_id" -> id)).one[Question]
questionColl.find(BSONDocument("_id" -> id)).uno[Question]
def findByIds(ids: List[QuestionId]): Fu[List[Question]] =
questionColl.find(BSONDocument("_id" -> BSONDocument("$in" -> ids.distinct))).cursor[Question]().collect[List]()
questionColl.find(BSONDocument("_id" -> BSONDocument("$in" -> ids.distinct))).cursor[Question]().gather[List]()
def accept(q: Question) = questionColl.update(
BSONDocument("_id" -> q.id),
@ -87,7 +87,7 @@ final class QaApi(
prefix = "qa:popular",
f = (nb: Int) => questionColl.find(BSONDocument())
.sort(BSONDocument("vote.score" -> -1))
.cursor[Question]().collect[List](nb),
.cursor[Question]().gather[List](nb),
timeToLive = 3 hour)
def popular(max: Int): Fu[List[Question]] = popularCache(max)
@ -95,10 +95,10 @@ final class QaApi(
def byTag(tag: String, max: Int): Fu[List[Question]] =
questionColl.find(BSONDocument("tags" -> tag.toLowerCase))
.sort(BSONDocument("vote.score" -> -1))
.cursor[Question]().collect[List](max)
.cursor[Question]().gather[List](max)
def byTags(tags: List[String], max: Int): Fu[List[Question]] =
questionColl.find(BSONDocument("tags" -> BSONDocument("$in" -> tags.map(_.toLowerCase)))).cursor[Question]().collect[List](max)
questionColl.find(BSONDocument("tags" -> BSONDocument("$in" -> tags.map(_.toLowerCase)))).cursor[Question]().gather[List](max)
def addComment(c: Comment)(q: Question) = questionColl.update(
BSONDocument("_id" -> q.id),
@ -176,7 +176,7 @@ final class QaApi(
}
def findById(id: AnswerId): Fu[Option[Answer]] =
answerColl.find(BSONDocument("_id" -> id)).one[Answer]
answerColl.find(BSONDocument("_id" -> id)).uno[Answer]
def accept(q: Question, a: Answer) = (question accept q) >> answerColl.update(
BSONDocument("questionId" -> q.id),
@ -190,7 +190,7 @@ final class QaApi(
def popular(questionId: QuestionId): Fu[List[Answer]] =
answerColl.find(BSONDocument("questionId" -> questionId))
.sort(BSONDocument("vote.score" -> -1))
.cursor[Answer]().collect[List]()
.cursor[Answer]().gather[List]()
def zipWithQuestions(answers: List[Answer]): Fu[List[AnswerWithQuestion]] =
question.findByIds(answers.map(_.questionId)) map { qs =>

View File

@ -37,6 +37,6 @@ final class Search(collection: Coll) {
def apply(q: String): Fu[List[Question]] =
collection.find(BSONDocument(
"$text" -> BSONDocument("$search" -> q)
)).cursor[Question]().collect[List]()
)).cursor[Question]().gather[List]()
}

View File

@ -29,7 +29,7 @@ final class RelationApi(
def fetchRelation(u1: ID, u2: ID): Fu[Option[Relation]] = coll.find(
$doc("u1" -> u1, "u2" -> u2),
$doc("r" -> true, "_id" -> false)
).one[BSONDocument].map {
).uno[BSONDocument].map {
_.flatMap(_.getAs[Boolean]("r"))
}

View File

@ -48,7 +48,7 @@ private[relation] object RelationRepo {
).sort($sort.naturalAsc)
.hint($doc("u1" -> 1))
.cursor[Bdoc]()
.collect[List](nb).map {
.gather[List](nb).map {
_.flatMap { _.getAs[String]("_id") }
} flatMap { ids =>
coll.remove($inIds(ids)).void

View File

@ -151,7 +151,7 @@ private[report] final class ReportApi(coll: Coll) {
def nbUnprocessed = coll.countSel(unprocessedSelect)
def recent(nb: Int) =
coll.find($empty).sort($sort.createdDesc).cursor[Report]().collect[List](nb)
coll.find($empty).sort($sort.createdDesc).cursor[Report]().gather[List](nb)
def unprocessedAndRecent(nb: Int): Fu[List[Report.WithUser]] =
recentUnprocessed(nb) |+| recentProcessed(nb) flatMap { all =>
@ -164,16 +164,16 @@ private[report] final class ReportApi(coll: Coll) {
}
def recentUnprocessed(nb: Int) =
coll.find(unprocessedSelect).sort($sort.createdDesc).cursor[Report]().collect[List](nb)
coll.find(unprocessedSelect).sort($sort.createdDesc).cursor[Report]().gather[List](nb)
def recentProcessed(nb: Int) =
coll.find(processedSelect).sort($sort.createdDesc).cursor[Report]().collect[List](nb)
coll.find(processedSelect).sort($sort.createdDesc).cursor[Report]().gather[List](nb)
private def selectRecent(user: User, reason: Reason) = $doc(
"createdAt" -> $gt(DateTime.now minusDays 7),
"createdAt" $gt DateTime.now.minusDays(7),
"user" -> user.id,
"reason" -> reason.name)
private def findRecent(user: User, reason: Reason): Fu[Option[Report]] =
coll.one(selectRecent(user, reason))
coll.uno[Report](selectRecent(user, reason))
}

View File

@ -59,7 +59,7 @@ final class ForecastApi(coll: Coll, roundMap: akka.actor.ActorSelection) {
}
def loadForDisplay(pov: Pov): Fu[Option[Forecast]] =
pov.forecastable ?? coll.find(BSONDocument("_id" -> pov.fullId)).one[Forecast] flatMap {
pov.forecastable ?? coll.find(BSONDocument("_id" -> pov.fullId)).uno[Forecast] flatMap {
case None => fuccess(none)
case Some(fc) =>
if (firstStep(fc.steps).exists(_.ply != pov.game.turns + 1)) clearPov(pov) inject none
@ -67,7 +67,7 @@ final class ForecastApi(coll: Coll, roundMap: akka.actor.ActorSelection) {
}
def loadForPlay(pov: Pov): Fu[Option[Forecast]] =
pov.game.forecastable ?? coll.find(BSONDocument("_id" -> pov.fullId)).one[Forecast] flatMap {
pov.game.forecastable ?? coll.find(BSONDocument("_id" -> pov.fullId)).uno[Forecast] flatMap {
case None => fuccess(none)
case Some(fc) =>
if (firstStep(fc.steps).exists(_.ply != pov.game.turns)) clearPov(pov) inject none

View File

@ -8,7 +8,7 @@ import org.joda.time.DateTime
import reactivemongo.bson._
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.db.dsl.Coll
import lila.db.dsl._
import lila.game.Event
import lila.socket.actorApi.GetVersion
@ -79,19 +79,19 @@ private[round] object History {
private def serverStarting = !lila.common.PlayApp.startedSinceMinutes(5)
private def load(coll: Coll, gameId: String, withPersistence: Boolean): Fu[VersionedEvents] =
coll.find(BSONDocument("_id" -> gameId)).one[BSONDocument].map {
coll.byId[Bdoc](gameId).map {
_.flatMap(_.getAs[VersionedEvents]("e")) ?? (_.reverse)
} addEffect {
case events if events.nonEmpty && !withPersistence => coll.remove(BSONDocument("_id" -> gameId)).void
case events if events.nonEmpty && !withPersistence => coll.remove($doc("_id" -> gameId)).void
case _ =>
}
private def persist(coll: Coll, gameId: String)(vevs: List[VersionedEvent]) {
if (vevs.nonEmpty) coll.uncheckedUpdate(
BSONDocument("_id" -> gameId),
BSONDocument(
"$set" -> BSONDocument("e" -> vevs.reverse),
"$setOnInsert" -> BSONDocument("d" -> DateTime.now)),
$doc("_id" -> gameId),
$doc(
"$set" -> $doc("e" -> vevs.reverse),
"$setOnInsert" -> $doc("d" -> DateTime.now)),
upsert = true)
}
}

View File

@ -1,21 +1,19 @@
package lila.round
import lila.db.dsl.Coll
import lila.db.dsl._
import reactivemongo.bson._
final class NoteApi(coll: Coll) {
def get(gameId: String, userId: String): Fu[String] =
coll.find(BSONDocument("_id" -> makeId(gameId, userId))).one[BSONDocument] map {
_ flatMap (_.getAs[String]("t")) getOrElse ""
}
coll.primitiveOne[String]($id(makeId(gameId, userId)), "t") map (~_)
def set(gameId: String, userId: String, text: String) = {
if (text.isEmpty) coll.remove(BSONDocument("_id" -> makeId(gameId, userId)))
else coll.update(
BSONDocument("_id" -> makeId(gameId, userId)),
BSONDocument("$set" -> BSONDocument("t" -> text)),
$id(makeId(gameId, userId)),
$set("t" -> text),
upsert = true)
}.void

View File

@ -36,7 +36,7 @@ object Store {
coll.find(
BSONDocument("_id" -> sessionId, "up" -> true),
BSONDocument("user" -> true, "_id" -> false)
).one[BSONDocument] map { _ flatMap (_.getAs[String]("user")) }
).uno[BSONDocument] map { _ flatMap (_.getAs[String]("user")) }
case class UserIdAndFingerprint(user: String, fp: Option[String])
private implicit val UserIdAndFingerprintBSONReader = Macros.reader[UserIdAndFingerprint]
@ -45,7 +45,7 @@ object Store {
coll.find(
BSONDocument("_id" -> sessionId, "up" -> true),
BSONDocument("user" -> true, "fp" -> true, "_id" -> false)
).one[UserIdAndFingerprint]
).uno[UserIdAndFingerprint]
def delete(sessionId: String): Funit =
coll.update(
@ -74,7 +74,7 @@ object Store {
def openSessions(userId: String, nb: Int): Fu[List[UserSession]] =
coll.find(
BSONDocument("user" -> userId, "up" -> true)
).sort(BSONDocument("date" -> -1)).cursor[UserSession]().collect[List](nb)
).sort(BSONDocument("date" -> -1)).cursor[UserSession]().gather[List](nb)
def setFingerprint(id: String, fingerprint: String): Fu[String] = {
import java.util.Base64
@ -100,7 +100,7 @@ object Store {
coll.find(
BSONDocument("user" -> userId),
BSONDocument("_id" -> false, "ip" -> true, "ua" -> true, "fp" -> true)
).cursor[Info]().collect[List]()
).cursor[Info]().gather[List]()
private case class DedupInfo(_id: String, ip: String, ua: String) {
def compositeKey = s"$ip $ua"
@ -112,7 +112,7 @@ object Store {
"user" -> userId,
"up" -> true
)).sort(BSONDocument("date" -> -1))
.cursor[DedupInfo]().collect[List]() flatMap { sessions =>
.cursor[DedupInfo]().gather[List]() flatMap { sessions =>
val olds = sessions.groupBy(_.compositeKey).values.map(_ drop 1).flatten
.filter(_._id != keepSessionId)
coll.remove(

View File

@ -68,7 +68,7 @@ object UserSpy {
coll.find(
BSONDocument("user" -> user.id),
BSONDocument(field -> true)
).cursor[BSONDocument]().collect[List]() map {
).cursor[BSONDocument]().gather[List]() map {
_.flatMap(_.getAs[Value](field)).toSet
}

View File

@ -19,7 +19,7 @@ final class ShutupApi(
def getPublicLines(userId: String): Fu[List[String]] =
coll.find($doc("_id" -> userId), $doc("pub" -> 1))
.one[Bdoc].map {
.uno[Bdoc].map {
~_.flatMap(_.getAs[List[String]]("pub"))
}

View File

@ -7,7 +7,7 @@ import reactivemongo.core.commands._
import chess.Status
import chess.variant.Variant
import lila.db.BSON
import lila.db.dsl.Coll
import lila.db.dsl._
import lila.game.{ Game, GameRepo }
import lila.user.{ User, UserRepo }
@ -34,7 +34,7 @@ private[simul] final class SimulRepo(simulColl: Coll) {
status = r.get[Status]("status"),
wins = r boolO "wins",
hostColor = r.strO("hostColor").flatMap(chess.Color.apply) | chess.White)
def writes(w: BSON.Writer, o: SimulPairing) = BSONDocument(
def writes(w: BSON.Writer, o: SimulPairing) = $doc(
"player" -> o.player,
"gameId" -> o.gameId,
"status" -> o.status,
@ -44,20 +44,19 @@ private[simul] final class SimulRepo(simulColl: Coll) {
private implicit val SimulBSONHandler = Macros.handler[Simul]
private val createdSelect = BSONDocument("status" -> SimulStatus.Created.id)
private val startedSelect = BSONDocument("status" -> SimulStatus.Started.id)
private val finishedSelect = BSONDocument("status" -> SimulStatus.Finished.id)
private val createdSort = BSONDocument("createdAt" -> -1)
private val createdSelect = $doc("status" -> SimulStatus.Created.id)
private val startedSelect = $doc("status" -> SimulStatus.Started.id)
private val finishedSelect = $doc("status" -> SimulStatus.Finished.id)
private val createdSort = $doc("createdAt" -> -1)
def find(id: Simul.ID): Fu[Option[Simul]] =
simulColl.find(BSONDocument("_id" -> id)).one[Simul]
simulColl.byId[Simul](id)
def exists(id: Simul.ID): Fu[Boolean] =
simulColl.count(BSONDocument("_id" -> id).some) map (0 !=)
simulColl.countSel($id(id)) map (0 !=)
def createdByHostId(hostId: String): Fu[List[Simul]] =
simulColl.find(createdSelect ++ BSONDocument("hostId" -> hostId))
.cursor[Simul]().collect[List]()
simulColl.find(createdSelect ++ $doc("hostId" -> hostId)).list[Simul]()
def findStarted(id: Simul.ID): Fu[Option[Simul]] =
find(id) map (_ filter (_.isStarted))
@ -65,50 +64,47 @@ private[simul] final class SimulRepo(simulColl: Coll) {
def findCreated(id: Simul.ID): Fu[Option[Simul]] =
find(id) map (_ filter (_.isCreated))
def allCreated: Fu[List[Simul]] = simulColl.find(
createdSelect
).sort(createdSort).cursor[Simul]().collect[List]()
def allCreated: Fu[List[Simul]] =
simulColl.find(createdSelect).sort(createdSort).list[Simul]()
def allCreatedFeaturable: Fu[List[Simul]] = simulColl.find(
createdSelect ++ BSONDocument(
"createdAt" -> BSONDocument("$gte" -> DateTime.now.minusMinutes(15)),
"hostRating" -> BSONDocument("$gte" -> 1700)
createdSelect ++ $doc(
"createdAt" -> $doc("$gte" -> DateTime.now.minusMinutes(15)),
"hostRating" -> $doc("$gte" -> 1700)
)
).sort(createdSort).cursor[Simul]().collect[List]()
).sort(createdSort).list[Simul]()
def allStarted: Fu[List[Simul]] = simulColl.find(
startedSelect
).sort(createdSort).cursor[Simul]().collect[List]()
).sort(createdSort).list[Simul]()
def allFinished(max: Int): Fu[List[Simul]] = simulColl.find(
finishedSelect
).sort(createdSort).cursor[Simul]().collect[List](max)
).sort(createdSort).list[Simul](max)
def allNotFinished =
simulColl.find(
BSONDocument("status" -> BSONDocument("$ne" -> SimulStatus.Finished.id))
).cursor[Simul]().collect[List]()
simulColl.find($doc("status" $ne SimulStatus.Finished.id)).list[Simul]()
def create(simul: Simul): Funit =
simulColl insert simul void
def update(simul: Simul) =
simulColl.update(BSONDocument("_id" -> simul.id), simul).void
simulColl.update($id(simul.id), simul).void
def remove(simul: Simul) =
simulColl.remove(BSONDocument("_id" -> simul.id)).void
simulColl.remove($id(simul.id)).void
def setHostGameId(simul: Simul, gameId: String) = simulColl.update(
BSONDocument("_id" -> simul.id),
BSONDocument("$set" -> BSONDocument("hostGameId" -> gameId))
$id(simul.id),
$set("hostGameId" -> gameId)
).void
def setHostSeenNow(simul: Simul) = simulColl.update(
BSONDocument("_id" -> simul.id),
BSONDocument("$set" -> BSONDocument("hostSeenAt" -> DateTime.now))
$id(simul.id),
$set("hostSeenAt" -> DateTime.now)
).void
def cleanup = simulColl.remove(
createdSelect ++ BSONDocument(
"createdAt" -> BSONDocument("$lt" -> (DateTime.now minusMinutes 60))))
createdSelect ++ $doc(
"createdAt" -> $doc("$lt" -> (DateTime.now minusMinutes 60))))
}

View File

@ -32,7 +32,7 @@ private[team] final class PaginatorBuilder(
def slice(offset: Int, length: Int): Fu[Seq[MemberWithUser]] = for {
members coll.member.find(selector)
.sort(sorting).skip(offset).cursor[Member]().collect[List](length)
.sort(sorting).skip(offset).cursor[Member]().gather[List](length)
users UserRepo byOrderedIds members.map(_.user)
} yield members zip users map {
case (member, user) => MemberWithUser(member, user)

View File

@ -17,7 +17,7 @@ object RequestRepo {
coll.exists(selectId(teamId, userId))
def find(teamId: ID, userId: ID): Fu[Option[Request]] =
coll.one[Request](selectId(teamId, userId))
coll.uno[Request](selectId(teamId, userId))
def countByTeam(teamId: ID): Fu[Int] =
coll.countSel(teamQuery(teamId))

View File

@ -21,7 +21,7 @@ object TeamRepo {
def cursor(selector: Bdoc) = coll.find(selector).cursor[Team]()
def owned(id: String, createdBy: String): Fu[Option[Team]] =
coll.one[Team]($id(id) ++ $doc("createdBy" -> createdBy))
coll.uno[Team]($id(id) ++ $doc("createdBy" -> createdBy))
def teamIdsByCreator(userId: String): Fu[List[String]] =
coll.distinct("_id", BSONDocument("createdBy" -> userId).some) map lila.db.BSON.asStrings

View File

@ -18,14 +18,14 @@ private[timeline] final class EntryRepo(coll: Coll, userMax: Int) {
coll.find($doc("users" -> userId))
.sort($doc("date" -> -1))
.cursor[Entry]()
.collect[List](max)
.gather[List](max)
def findRecent(typ: String, since: DateTime) =
coll.find($doc(
"typ" -> typ,
"date" -> $doc("$gt" -> since)
)).cursor[Entry]()
.collect[List]()
.gather[List]()
def channelUserIdRecentExists(channel: String, userId: String): Fu[Boolean] =
coll.count($doc(

View File

@ -23,10 +23,10 @@ object PairingRepo {
private val recentSort = $doc("d" -> -1)
private val chronoSort = $doc("d" -> 1)
def byId(id: String): Fu[Option[Pairing]] = coll.find(selectId(id)).one[Pairing]
def byId(id: String): Fu[Option[Pairing]] = coll.find(selectId(id)).uno[Pairing]
def recentByTour(tourId: String, nb: Int): Fu[Pairings] =
coll.find(selectTour(tourId)).sort(recentSort).cursor[Pairing]().collect[List](nb)
coll.find(selectTour(tourId)).sort(recentSort).cursor[Pairing]().gather[List](nb)
def lastOpponents(tourId: String, userIds: Iterable[String], nb: Int): Fu[Pairing.LastOpponents] =
coll.find(
@ -45,7 +45,7 @@ object PairingRepo {
coll.find(
selectTourUser(tourId, userId),
$doc("_id" -> false, "u" -> true)
).cursor[Bdoc]().collect[List]().map {
).cursor[Bdoc]().gather[List]().map {
_.flatMap { doc =>
~doc.getAs[List[String]]("u").filter(userId!=)
}.toSet
@ -55,14 +55,14 @@ object PairingRepo {
coll.find(
selectTourUser(tourId, userId),
$doc("_id" -> true)
).sort(recentSort).cursor[Bdoc]().collect[List](nb).map {
).sort(recentSort).cursor[Bdoc]().gather[List](nb).map {
_.flatMap(_.getAs[String]("_id"))
}
def byTourUserNb(tourId: String, userId: String, nb: Int): Fu[Option[Pairing]] =
(nb > 0) ?? coll.find(
selectTourUser(tourId, userId)
).sort(chronoSort).skip(nb - 1).one[Pairing]
).sort(chronoSort).skip(nb - 1).uno[Pairing]
def removeByTour(tourId: String) = coll.remove(selectTour(tourId)).void
@ -92,15 +92,15 @@ object PairingRepo {
def removePlaying(tourId: String) = coll.remove(selectTour(tourId) ++ selectPlaying).void
def findPlaying(tourId: String): Fu[Pairings] =
coll.find(selectTour(tourId) ++ selectPlaying).cursor[Pairing]().collect[List]()
coll.find(selectTour(tourId) ++ selectPlaying).cursor[Pairing]().gather[List]()
def findPlaying(tourId: String, userId: String): Fu[Option[Pairing]] =
coll.find(selectTourUser(tourId, userId) ++ selectPlaying).one[Pairing]
coll.find(selectTourUser(tourId, userId) ++ selectPlaying).uno[Pairing]
def finishedByPlayerChronological(tourId: String, userId: String): Fu[Pairings] =
coll.find(
selectTourUser(tourId, userId) ++ selectFinished
).sort(chronoSort).cursor[Pairing]().collect[List]()
).sort(chronoSort).cursor[Pairing]().gather[List]()
def insert(pairing: Pairing) = coll.insert {
pairingHandler.write(pairing) ++ $doc("d" -> DateTime.now)

View File

@ -24,10 +24,10 @@ object PlayerRepo {
private val selectWithdraw = BSONDocument("w" -> true)
private val bestSort = BSONDocument("m" -> -1)
def byId(id: String): Fu[Option[Player]] = coll.find(selectId(id)).one[Player]
def byId(id: String): Fu[Option[Player]] = coll.uno[Player](selectId(id))
def bestByTour(tourId: String, nb: Int, skip: Int = 0): Fu[List[Player]] =
coll.find(selectTour(tourId)).sort(bestSort).skip(skip).cursor[Player]().collect[List](nb)
coll.find(selectTour(tourId)).sort(bestSort).skip(skip).cursor[Player]().gather[List](nb)
def bestByTourWithRank(tourId: String, nb: Int, skip: Int = 0): Fu[RankedPlayers] =
bestByTour(tourId, nb, skip).map { res =>
@ -63,7 +63,7 @@ object PlayerRepo {
multi = true).void
def find(tourId: String, userId: String): Fu[Option[Player]] =
coll.find(selectTourUser(tourId, userId)).one[Player]
coll.find(selectTourUser(tourId, userId)).uno[Player]
def update(tourId: String, userId: String)(f: Player => Fu[Player]) =
find(tourId, userId) flatten s"No such player: $tourId/$userId" flatMap f flatMap { player =>
@ -94,7 +94,7 @@ object PlayerRepo {
def withPoints(tourId: String): Fu[List[Player]] =
coll.find(
selectTour(tourId) ++ BSONDocument("m" -> BSONDocument("$gt" -> 0))
).cursor[Player]().collect[List]()
).cursor[Player]().gather[List]()
private def aggregationUserIdList(res: Stream[BSONDocument]): List[String] =
res.headOption flatMap { _.getAs[List[String]]("uids") } getOrElse Nil
@ -108,7 +108,7 @@ object PlayerRepo {
coll.distinct("uid", (selectTour(tourId) ++ selectActive).some) map lila.db.BSON.asStrings
def winner(tourId: String): Fu[Option[Player]] =
coll.find(selectTour(tourId)).sort(bestSort).one[Player]
coll.find(selectTour(tourId)).sort(bestSort).uno[Player]
// freaking expensive (marathons)
private[tournament] def computeRanking(tourId: String): Fu[Ranking] =
@ -133,7 +133,7 @@ object PlayerRepo {
def byTourAndUserIds(tourId: String, userIds: Iterable[String]): Fu[List[Player]] =
coll.find(selectTour(tourId) ++ BSONDocument(
"uid" -> BSONDocument("$in" -> userIds)
)).cursor[Player]().collect[List]()
)).cursor[Player]().gather[List]()
.chronometer.logIfSlow(200, logger) { players =>
s"PlayerRepo.byTourAndUserIds $tourId ${userIds.size} user IDs, ${players.size} players"
}.result

View File

@ -31,35 +31,35 @@ object TournamentRepo {
private val nonEmptySelect = $doc("nbPlayers" -> $doc("$ne" -> 0))
private val selectUnique = $doc("schedule.freq" -> "unique")
def byId(id: String): Fu[Option[Tournament]] = coll.find($id(id)).one[Tournament]
def byId(id: String): Fu[Option[Tournament]] = coll.find($id(id)).uno[Tournament]
def byIds(ids: Iterable[String]): Fu[List[Tournament]] =
coll.find($doc("_id" -> $doc("$in" -> ids)))
.cursor[Tournament]().collect[List]()
.cursor[Tournament]().gather[List]()
def uniqueById(id: String): Fu[Option[Tournament]] =
coll.find($id(id) ++ selectUnique).one[Tournament]
coll.find($id(id) ++ selectUnique).uno[Tournament]
def recentAndNext: Fu[List[Tournament]] =
coll.find(sinceSelect(DateTime.now minusDays 1))
.cursor[Tournament]().collect[List]()
.cursor[Tournament]().gather[List]()
def byIdAndPlayerId(id: String, userId: String): Fu[Option[Tournament]] =
coll.find(
$id(id) ++ $doc("players.id" -> userId)
).one[Tournament]
).uno[Tournament]
def createdById(id: String): Fu[Option[Tournament]] =
coll.find($id(id) ++ createdSelect).one[Tournament]
coll.find($id(id) ++ createdSelect).uno[Tournament]
def enterableById(id: String): Fu[Option[Tournament]] =
coll.find($id(id) ++ enterableSelect).one[Tournament]
coll.find($id(id) ++ enterableSelect).uno[Tournament]
def startedById(id: String): Fu[Option[Tournament]] =
coll.find($id(id) ++ startedSelect).one[Tournament]
coll.find($id(id) ++ startedSelect).uno[Tournament]
def finishedById(id: String): Fu[Option[Tournament]] =
coll.find($id(id) ++ finishedSelect).one[Tournament]
coll.find($id(id) ++ finishedSelect).uno[Tournament]
def startedOrFinishedById(id: String): Fu[Option[Tournament]] =
byId(id) map { _ filterNot (_.isCreated) }
@ -68,25 +68,25 @@ object TournamentRepo {
createdById(id) map (_ filter (_.createdBy == userId))
def allEnterable: Fu[List[Tournament]] =
coll.find(enterableSelect).cursor[Tournament]().collect[List]()
coll.find(enterableSelect).cursor[Tournament]().gather[List]()
def nonEmptyEnterable: Fu[List[Tournament]] =
coll.find(enterableSelect ++ nonEmptySelect).cursor[Tournament]().collect[List]()
coll.find(enterableSelect ++ nonEmptySelect).cursor[Tournament]().gather[List]()
def createdIncludingScheduled: Fu[List[Tournament]] = coll.find(createdSelect).toList[Tournament](None)
def createdIncludingScheduled: Fu[List[Tournament]] = coll.find(createdSelect).list[Tournament](None)
def started: Fu[List[Tournament]] =
coll.find(startedSelect).sort($doc("createdAt" -> -1)).toList[Tournament](None)
coll.find(startedSelect).sort($doc("createdAt" -> -1)).list[Tournament](None)
def publicStarted: Fu[List[Tournament]] =
coll.find(startedSelect ++ $doc("private" -> $doc("$exists" -> false)))
.sort($doc("createdAt" -> -1))
.cursor[Tournament]().collect[List]()
.list[Tournament]()
def finished(limit: Int): Fu[List[Tournament]] =
coll.find(finishedSelect)
.sort($doc("startsAt" -> -1))
.cursor[Tournament]().collect[List](limit)
.list[Tournament](limit)
def finishedNotable(limit: Int): Fu[List[Tournament]] =
coll.find(finishedSelect ++ $doc(
@ -95,7 +95,7 @@ object TournamentRepo {
scheduledSelect
)))
.sort($doc("startsAt" -> -1))
.cursor[Tournament]().collect[List](limit)
.list[Tournament](limit)
def finishedPaginator(maxPerPage: Int, page: Int) = Paginator(
adapter = new Adapter[Tournament](
@ -138,15 +138,15 @@ object TournamentRepo {
def publicCreatedSorted(aheadMinutes: Int): Fu[List[Tournament]] = coll.find(
allCreatedSelect(aheadMinutes) ++ $doc("private" -> $doc("$exists" -> false))
).sort($doc("startsAt" -> 1)).cursor[Tournament]().collect[List]()
).sort($doc("startsAt" -> 1)).list[Tournament](none)
def allCreated(aheadMinutes: Int): Fu[List[Tournament]] =
coll.find(allCreatedSelect(aheadMinutes)).cursor[Tournament]().collect[List]()
coll.find(allCreatedSelect(aheadMinutes)).cursor[Tournament]().gather[List]()
private def stillWorthEntering: Fu[List[Tournament]] =
coll.find(startedSelect ++ $doc(
"private" -> $doc("$exists" -> false)
)).sort($doc("startsAt" -> 1)).toList[Tournament](none) map {
)).sort($doc("startsAt" -> 1)).list[Tournament](none) map {
_.filter(_.isStillWorthEntering)
}
@ -174,15 +174,15 @@ object TournamentRepo {
coll.find(selectUnique)
.sort($doc("startsAt" -> -1))
.hint($doc("startsAt" -> -1))
.cursor[Tournament]().collect[List]()
.list[Tournament]()
def scheduledUnfinished: Fu[List[Tournament]] =
coll.find(scheduledSelect ++ unfinishedSelect)
.sort($doc("startsAt" -> 1)).cursor[Tournament]().collect[List]()
.sort($doc("startsAt" -> 1)).list[Tournament]()
def scheduledCreated: Fu[List[Tournament]] =
coll.find(createdSelect ++ scheduledSelect)
.sort($doc("startsAt" -> 1)).cursor[Tournament]().collect[List]()
.sort($doc("startsAt" -> 1)).list[Tournament]()
def scheduledDedup: Fu[List[Tournament]] = scheduledCreated map {
import Schedule.Freq
@ -204,12 +204,12 @@ object TournamentRepo {
"schedule.speed" -> $doc("$in" -> Schedule.Speed.mostPopular.map(_.name))
)
).sort($doc("startsAt" -> -1))
.toList[Tournament](Schedule.Speed.mostPopular.size.some)
.list[Tournament](Schedule.Speed.mostPopular.size.some)
def lastFinishedDaily(variant: Variant): Fu[Option[Tournament]] = coll.find(
finishedSelect ++ sinceSelect(DateTime.now minusDays 1) ++ variantSelect(variant) ++
$doc("schedule.freq" -> Schedule.Freq.Daily.name)
).sort($doc("startsAt" -> -1)).one[Tournament]
).sort($doc("startsAt" -> -1)).uno[Tournament]
def update(tour: Tournament) = coll.update($doc("_id" -> tour.id), tour)
@ -229,5 +229,5 @@ object TournamentRepo {
Schedule.Freq.Marathon.name,
Schedule.Freq.Unique.name
))
) ++ nonEmptySelect).cursor[Tournament]().collect[List]()
) ++ nonEmptySelect).cursor[Tournament]().gather[List]()
}

View File

@ -4,6 +4,7 @@ import akka.actor._
import com.typesafe.config.Config
import lila.common.PimpedConfig._
import lila.db.dsl._
import scala.collection.JavaConversions._
import scala.concurrent.duration._
@ -39,11 +40,9 @@ final class Env(
lazy val streamerList = new StreamerList(new {
import reactivemongo.bson._
private val coll = db("flag")
def get = coll.find(BSONDocument("_id" -> "streamer")).one[BSONDocument].map {
~_.flatMap(_.getAs[String]("text"))
}
def get = coll.primitiveOne[String]($id("streamer"), "text") map (~_)
def set(text: String) =
coll.update(BSONDocument("_id" -> "streamer"), BSONDocument("text" -> text), upsert = true).void
coll.update($id("streamer"), $doc("text" -> text), upsert = true).void
})
object isStreamer {

View File

@ -25,7 +25,7 @@ final class LightUserApi(coll: Coll) {
id => coll.find(
BSONDocument(F.id -> id),
BSONDocument(F.username -> true, F.title -> true)
).one[LightUser],
).uno[LightUser],
timeToLive = 20 minutes,
default = id => LightUser(id, id, None).some,
logger = logger branch "LightUserApi")

View File

@ -25,7 +25,7 @@ final class NoteApi(
"to" -> user.id,
"from" -> BSONDocument("$in" -> (myFriendIds + me.id))
) ++ me.troll.fold(BSONDocument(), BSONDocument("troll" -> false))
).sort(BSONDocument("date" -> -1)).cursor[Note]().collect[List](100)
).sort(BSONDocument("date" -> -1)).cursor[Note]().gather[List](100)
def write(to: User, text: String, from: User) = {

View File

@ -52,7 +52,7 @@ final class RankingApi(
PerfType.id2key(perfId) ?? { perfKey =>
coll.find(BSONDocument("perf" -> perfId, "stable" -> true))
.sort(BSONDocument("rating" -> -1))
.cursor[Ranking]().collect[List](nb) map {
.cursor[Ranking]().gather[List](nb) map {
_.flatMap { r =>
lightUser(r.user).map { light =>
User.LightPerf(

View File

@ -24,5 +24,5 @@ final class TrophyApi(coll: Coll) {
def awardMarathonWinner(userId: String): Funit = award(userId, Trophy.Kind.MarathonWinner)
def findByUser(user: User, max: Int = 12): Fu[List[Trophy]] =
coll.find(BSONDocument("user" -> user.id)).cursor[Trophy]().collect[List](max)
coll.find(BSONDocument("user" -> user.id)).cursor[Trophy]().gather[List](max)
}

View File

@ -23,13 +23,13 @@ object UserRepo {
val normalize = User normalize _
def topNbGame(nb: Int): Fu[List[User]] =
coll.find(enabledSelect).sort($sort desc "count.game").cursor[User]().collect[List](nb)
coll.find(enabledSelect).sort($sort desc "count.game").cursor[User]().gather[List](nb)
def byId(id: ID): Fu[Option[User]] = coll.byId[User](id)
def byIds(ids: Iterable[ID]): Fu[List[User]] = coll.byIds[User](ids)
def byEmail(email: String): Fu[Option[User]] = coll.one[User]($doc(F.email -> email))
def byEmail(email: String): Fu[Option[User]] = coll.uno[User]($doc(F.email -> email))
def idByEmail(email: String): Fu[Option[String]] =
coll.primitiveOne[String]($doc(F.email -> email), "_id")
@ -49,7 +49,7 @@ object UserRepo {
coll.list[User](enabledSelect ++ $inIds(ids))
def enabledById(id: ID): Fu[Option[User]] =
coll.one[User](enabledSelect ++ $id(id))
coll.uno[User](enabledSelect ++ $id(id))
def named(username: String): Fu[Option[User]] = coll.byId[User](normalize(username))
@ -60,7 +60,7 @@ object UserRepo {
coll.find($doc("_id" -> $doc("$in" -> ids)) ++ goodLadSelectBson)
.sort($doc(s"perfs.standard.gl.r" -> -1))
.cursor[User](ReadPreference.secondaryPreferred)
.collect[List](nb)
.gather[List](nb)
// expensive, send to secondary
def idsByIdsSortRating(ids: Iterable[ID], nb: Int): Fu[List[User.ID]] =
@ -69,12 +69,12 @@ object UserRepo {
$doc("_id" -> true))
.sort($doc(s"perfs.standard.gl.r" -> -1))
.cursor[BSONDocument](ReadPreference.secondaryPreferred)
.collect[List](nb).map {
.gather[List](nb).map {
_.flatMap { _.getAs[String]("_id") }
}
def allSortToints(nb: Int) =
coll.find($empty).sort($sort desc F.toints).cursor[User]().collect[List](nb)
coll.find($empty).sort($sort desc F.toints).cursor[User]().gather[List](nb)
def usernameById(id: ID) =
coll.primitiveOne[String]($id(id), F.username)
@ -86,7 +86,7 @@ object UserRepo {
coll.find(
$doc("_id" -> $doc("$in" -> BSONArray(u1, u2))),
$doc(s"${F.count}.game" -> true)
).cursor[BSONDocument]().collect[List]() map { docs =>
).cursor[BSONDocument]().gather[List]() map { docs =>
docs.sortBy {
_.getAs[BSONDocument](F.count).flatMap(_.getAs[BSONNumberLike]("game")).??(_.toInt)
}.map(_.getAs[String]("_id")).flatten match {
@ -101,7 +101,7 @@ object UserRepo {
case (u1, u2) => coll.find(
$doc("_id" -> $doc("$in" -> BSONArray(u1, u2))),
$doc("_id" -> true)
).sort($doc(F.colorIt -> 1)).one[BSONDocument].map {
).sort($doc(F.colorIt -> 1)).uno[BSONDocument].map {
_.fold(scala.util.Random.nextBoolean) { doc =>
doc.getAs[String]("_id") contains u1
}
@ -208,7 +208,7 @@ object UserRepo {
checkPassword($doc(F.email -> email), password)
private def checkPassword(select: BSONDocument, password: String): Fu[Boolean] =
coll.one[AuthData](select) map {
coll.uno[AuthData](select) map {
_ ?? (data => data.enabled && data.compare(password))
}
@ -235,7 +235,7 @@ object UserRepo {
val regex = "^" + escaped + ".*$"
coll.find($doc("_id" -> BSONRegex(regex, "")), $doc(F.username -> true))
.sort($sort desc "_id")
.cursor[BSONDocument]().collect[List](max)
.cursor[BSONDocument]().gather[List](max)
.map {
_ flatMap { _.getAs[String](F.username) }
}
@ -291,7 +291,7 @@ object UserRepo {
def perfOf(id: ID, perfType: PerfType): Fu[Option[Perf]] = coll.find(
$doc("_id" -> id),
$doc(s"${F.perfs}.${perfType.key}" -> true)
).one[BSONDocument].map {
).uno[BSONDocument].map {
_.flatMap(_.getAs[BSONDocument](F.perfs)).flatMap(_.getAs[Perf](perfType.key))
}

View File

@ -41,7 +41,7 @@ private[video] final class VideoApi(
private val maxPerPage = 18
def find(id: Video.ID): Fu[Option[Video]] =
videoColl.find($doc("_id" -> id)).one[Video]
videoColl.find($doc("_id" -> id)).uno[Video]
def search(user: Option[User], query: String, page: Int): Fu[Paginator[VideoView]] = {
val q = query.split(' ').map { word => s""""$word"""" } mkString " "
@ -119,7 +119,7 @@ private[video] final class VideoApi(
"_id" $ne video.id
)).sort($doc("metadata.likes" -> -1))
.cursor[Video]()
.collect[List]().map { videos =>
.gather[List]().map { videos =>
videos.sortBy { v => -v.similarity(video) } take max
} flatMap videoViews(user)
@ -140,7 +140,7 @@ private[video] final class VideoApi(
def find(videoId: Video.ID, userId: String): Fu[Option[View]] =
viewColl.find($doc(
View.BSONFields.id -> View.makeId(videoId, userId)
)).one[View]
)).uno[View]
def add(a: View) = (viewColl insert a).void recover
lila.db.recoverDuplicateKey(_ => ())

View File

@ -9,11 +9,11 @@ private[wiki] final class Api(coll: Coll) {
import Page.PageBSONHandler
def show(slug: String, lang: String): Fu[Option[(Page, List[Page])]] = for {
page coll.one[Page]($doc("slug" -> slug, "lang" -> lang)) orElse
coll.one[Page]($doc("slug" -> slug, "lang" -> DefaultLang))
page coll.uno[Page]($doc("slug" -> slug, "lang" -> lang)) orElse
coll.uno[Page]($doc("slug" -> slug, "lang" -> DefaultLang))
pages coll.find($doc(
"lang" $in Seq(lang, DefaultLang)
)).sort($sort asc "number").cursor[Page]().collect[List]()
)).sort($sort asc "number").cursor[Page]().gather[List]()
} yield page map { _ -> makeMenu(pages) }
private def makeMenu(pages: List[Page]): List[Page] = {