diff --git a/app/Env.scala b/app/Env.scala
index 1ef48733d1..778f42c34a 100644
--- a/app/Env.scala
+++ b/app/Env.scala
@@ -44,7 +44,8 @@ final class Env(
isDonor = Env.donation.isDonor,
isHostingSimul = Env.simul.isHosting,
isStreamer = Env.tv.isStreamer.apply,
- insightShare = Env.insight.share) _
+ insightShare = Env.insight.share,
+ getPlayTime = Env.game.playTime.apply) _
system.actorOf(Props(new actor.Renderer), name = RendererName)
diff --git a/app/Global.scala b/app/Global.scala
index 86a6c23ac8..80c94828c8 100644
--- a/app/Global.scala
+++ b/app/Global.scala
@@ -1,51 +1,10 @@
package lila.app
-import lila.common.HTTPRequest
-import play.api.mvc._
-import play.api.mvc.Results._
-import play.api.{ Application, GlobalSettings, Mode }
+import play.api.{ Application, GlobalSettings }
object Global extends GlobalSettings {
override def onStart(app: Application) {
- kamon.Kamon.start()
- lila.app.Env.current
+ lila.app.Env.current // preload modules
}
-
- override def onStop(app: Application) {
- kamon.Kamon.shutdown()
- }
-
- override def onRouteRequest(req: RequestHeader): Option[Handler] = {
- lila.mon.http.request.all()
- Env.i18n.requestHandler(req) orElse super.onRouteRequest(req)
- }
-
- private def niceError(req: RequestHeader): Boolean =
- req.method == "GET" &&
- HTTPRequest.isSynchronousHttp(req) &&
- !HTTPRequest.hasFileExtension(req)
-
- override def onHandlerNotFound(req: RequestHeader) =
- if (niceError(req)) controllers.Main.notFound(req)
- else fuccess(NotFound("404 - Resource not found"))
-
- override def onBadRequest(req: RequestHeader, error: String) =
- if (error startsWith "Illegal character in path") fuccess(Redirect("/"))
- else if (error startsWith "Cannot parse parameter") onHandlerNotFound(req)
- else if (niceError(req)) {
- lila.mon.http.response.code400()
- controllers.Lobby.handleStatus(req, Results.BadRequest)
- }
- else fuccess(BadRequest(error))
-
- override def onError(req: RequestHeader, ex: Throwable) =
- if (niceError(req)) {
- if (lila.common.PlayApp.isProd) {
- lila.mon.http.response.code500()
- fuccess(InternalServerError(views.html.base.errorPage(ex)(lila.api.Context(req))))
- }
- else super.onError(req, ex)
- }
- else fuccess(InternalServerError(ex.getMessage))
}
diff --git a/app/LilaPlayStuff.scala b/app/LilaPlayStuff.scala
new file mode 100644
index 0000000000..8d96cca9c8
--- /dev/null
+++ b/app/LilaPlayStuff.scala
@@ -0,0 +1,76 @@
+package lila.app
+
+import com.google.inject.AbstractModule
+import com.google.inject.name.Names
+import javax.inject._
+import lila.common.HTTPRequest
+import play.api._
+import play.api.http._
+import play.api.inject.ApplicationLifecycle
+import play.api.mvc._
+import play.api.mvc.RequestHeader
+import play.api.mvc.Results._
+import play.api.routing.Router
+import scala.concurrent._
+
+class LilaHttpRequestHandler @Inject() (errorHandler: HttpErrorHandler,
+ configuration: HttpConfiguration, filters: HttpFilters,
+ router: Router) extends DefaultHttpRequestHandler(router, errorHandler, configuration, filters) {
+
+ override def routeRequest(req: RequestHeader) = {
+ lila.mon.http.request.all()
+ Env.i18n.requestHandler(req) orElse super.routeRequest(req)
+ }
+}
+
+class LilaHttpErrorHandler @Inject() (
+ env: Environment,
+ config: Configuration,
+ sourceMapper: OptionalSourceMapper,
+ router: Provider[Router]) extends DefaultHttpErrorHandler(env, config, sourceMapper, router) {
+
+ private def niceError(req: RequestHeader): Boolean =
+ req.method == "GET" &&
+ HTTPRequest.isSynchronousHttp(req) &&
+ !HTTPRequest.hasFileExtension(req)
+
+ def onHandlerNotFound(req: RequestHeader) =
+ if (niceError(req)) controllers.Main.notFound(req)
+ else fuccess(NotFound("404 - Resource not found"))
+
+ override def onBadRequest(req: RequestHeader, error: String) =
+ if (error startsWith "Illegal character in path") fuccess(Redirect("/"))
+ else if (error startsWith "Cannot parse parameter") onHandlerNotFound(req)
+ else if (niceError(req)) {
+ lila.mon.http.response.code400()
+ controllers.Lobby.handleStatus(req, Results.BadRequest)
+ }
+ else fuccess(BadRequest(error))
+
+ override def onProdServerError(req: RequestHeader, ex: UsefulException): Future[Result] = {
+ lila.mon.http.response.code500()
+ fuccess(InternalServerError(views.html.base.errorPage(ex)(lila.api.Context(req))))
+ }
+}
+
+@Singleton
+final class LilaLifecycle @Inject() (lifecycle: ApplicationLifecycle) {
+
+ play.api.Logger("boot").info("Lifecycle bindings")
+
+ lifecycle.addStopHook { () =>
+ play.api.Logger("play").info("LilaLifecycle shutdown")
+ kamon.Kamon.shutdown()
+ funit
+ }
+}
+
+final class LilaModule extends AbstractModule {
+
+ play.api.Logger("boot").info("Kamon start")
+ kamon.Kamon.start()
+
+ def configure() = {
+ bind(classOf[LilaLifecycle]).asEagerSingleton
+ }
+}
diff --git a/app/controllers/Account.scala b/app/controllers/Account.scala
index 32c62f14ea..f88635e6f1 100644
--- a/app/controllers/Account.scala
+++ b/app/controllers/Account.scala
@@ -5,9 +5,7 @@ import play.api.mvc._, Results._
import lila.api.Context
import lila.app._
import lila.common.LilaCookie
-import lila.db.api.$find
import lila.security.Permission
-import lila.user.tube.userTube
import lila.user.{ User => UserModel, UserRepo }
import views._
diff --git a/app/controllers/Challenge.scala b/app/controllers/Challenge.scala
index 83870d0274..4cd5664b53 100644
--- a/app/controllers/Challenge.scala
+++ b/app/controllers/Challenge.scala
@@ -137,7 +137,7 @@ object Challenge extends LilaController {
}
}
- def websocket(id: String, apiVersion: Int) = SocketOption[JsValue] { implicit ctx =>
+ def websocket(id: String, apiVersion: Int) = SocketOption { implicit ctx =>
env.api byId id flatMap {
_ ?? { c =>
get("sri") ?? { uid =>
diff --git a/app/controllers/LilaController.scala b/app/controllers/LilaController.scala
index 59e4fc300c..22f318dbf4 100644
--- a/app/controllers/LilaController.scala
+++ b/app/controllers/LilaController.scala
@@ -1,14 +1,12 @@
package controllers
+import akka.stream.scaladsl._
import ornicar.scalalib.Zero
import play.api.data.Form
import play.api.http._
-import play.api.libs.iteratee.{ Iteratee, Enumerator }
import play.api.libs.json.{ Json, JsValue, JsObject, JsArray, Writes }
import play.api.mvc._, Results._
-import play.api.mvc.WebSocket.FrameFormatter
import play.twirl.api.Html
-import scalaz.Monoid
import lila.api.{ PageData, Context, HeaderContext, BodyContext, TokenBucket }
import lila.app._
@@ -48,23 +46,19 @@ private[controllers] trait LilaController
CACHE_CONTROL -> "no-cache, no-store, must-revalidate", EXPIRES -> "0"
)
- protected def Socket[A: FrameFormatter](f: Context => Fu[(Iteratee[A, _], Enumerator[A])]) =
- WebSocket.tryAccept[A] { req => reqToCtx(req) flatMap f map scala.util.Right.apply }
-
- protected def SocketEither[A: FrameFormatter](f: Context => Fu[Either[Result, (Iteratee[A, _], Enumerator[A])]]) =
- WebSocket.tryAccept[A] { req => reqToCtx(req) flatMap f }
+ protected def Socket(f: Context => Fu[JsFlow]) =
+ WebSocket.acceptOrResult[JsObject, JsObject] { req =>
+ reqToCtx(req) flatMap f map Right.apply
+ }
- protected def SocketOption[A: FrameFormatter](f: Context => Fu[Option[(Iteratee[A, _], Enumerator[A])]]) =
- WebSocket.tryAccept[A] { req =>
- reqToCtx(req) flatMap f map {
- case None => Left(NotFound(jsonError("socket resource not found")))
- case Some(pair) => Right(pair)
- }
+ protected def SocketEither(f: Context => Fu[Either[Result, JsFlow]]) =
+ WebSocket.acceptOrResult[JsObject, JsObject] { req =>
+ reqToCtx(req) flatMap f
}
- protected def SocketOptionLimited[A: FrameFormatter](consumer: TokenBucket.Consumer, name: String)(f: Context => Fu[Option[(Iteratee[A, _], Enumerator[A])]]) =
- rateLimitedSocket[A](consumer, name) { ctx =>
- f(ctx) map {
+ protected def SocketOption(f: Context => Fu[Option[JsFlow]]): WebSocket =
+ WebSocket.acceptOrResult[JsObject, JsObject] { req =>
+ reqToCtx(req) flatMap f map {
case None => Left(NotFound(jsonError("socket resource not found")))
case Some(pair) => Right(pair)
}
diff --git a/app/controllers/LilaSocket.scala b/app/controllers/LilaSocket.scala
index 10fe5b20d4..5d4fce0f0d 100644
--- a/app/controllers/LilaSocket.scala
+++ b/app/controllers/LilaSocket.scala
@@ -1,10 +1,11 @@
package controllers
+import akka.stream.scaladsl._
import play.api.http._
-import play.api.libs.iteratee._
import play.api.libs.json._
import play.api.mvc._, Results._
-import play.api.mvc.WebSocket.FrameFormatter
+import play.api.mvc.WebSocket.MessageFlowTransformer
+import scala.concurrent.duration._
import lila.api.{ Context, TokenBucket }
import lila.app._
@@ -12,36 +13,27 @@ import lila.common.HTTPRequest
trait LilaSocket { self: LilaController =>
- private type AcceptType[A] = Context => Fu[Either[Result, (Iteratee[A, _], Enumerator[A])]]
-
- private val logger = lila.log("ratelimit")
+ protected implicit val jsonMessageFlowTransformer: MessageFlowTransformer[JsObject, JsObject] = {
+ import scala.util.control.NonFatal
+ import play.api.libs.streams.AkkaStreams
+ import websocket._
+ def closeOnException[T](block: => Option[T]) = try {
+ Left(block getOrElse {
+ sys error "Not a JsObject"
+ })
+ }
+ catch {
+ case NonFatal(e) => Right(CloseMessage(Some(CloseCodes.Unacceptable),
+ "Unable to parse json message"))
+ }
- def rateLimitedSocket[A: FrameFormatter](consumer: TokenBucket.Consumer, name: String)(f: AcceptType[A]): WebSocket[A, A] =
- WebSocket[A, A] { req =>
- reqToCtx(req) flatMap { ctx =>
- val ip = HTTPRequest lastRemoteAddress req
- def userInfo = {
- val sri = get("sri", req) | "none"
- val username = ctx.usernameOrAnon
- s"user:$username sri:$sri"
- }
- // logger.debug(s"socket:$name socket connect $ip $userInfo")
- f(ctx).map { resultOrSocket =>
- resultOrSocket.right.map {
- case (readIn, writeOut) => (e, i) => {
- writeOut |>> i
- e &> Enumeratee.mapInputM { in =>
- consumer(ip).map { credit =>
- if (credit >= 0) in
- else {
- logger.info(s"socket:$name socket close $ip $userInfo $in")
- Input.EOF
- }
- }
- } |>> readIn
- }
- }
- }
+ new MessageFlowTransformer[JsObject, JsObject] {
+ def transform(flow: Flow[JsObject, JsObject, _]) = {
+ AkkaStreams.bypassWith[Message, JsObject, Message](Flow[Message].collect {
+ case BinaryMessage(data) => closeOnException(Json.parse(data.iterator.asInputStream).asOpt[JsObject])
+ case TextMessage(text) => closeOnException(Json.parse(text).asOpt[JsObject])
+ })(flow map { json => TextMessage(Json.stringify(json)) })
}
}
+ }
}
diff --git a/app/controllers/Lobby.scala b/app/controllers/Lobby.scala
index 24b2b1d4d3..fdcad1c7e9 100644
--- a/app/controllers/Lobby.scala
+++ b/app/controllers/Lobby.scala
@@ -48,12 +48,22 @@ object Lobby extends LilaController {
size = 10,
rate = 6)
- def socket(apiVersion: Int) = SocketOptionLimited[JsValue](socketConsumer, "lobby") { implicit ctx =>
+ import akka.stream.scaladsl.Flow
+ import scala.concurrent.duration._
+ private val wsThrottler = Flow[JsObject].throttle(
+ elements = 10,
+ per = 5.second,
+ maximumBurst = 10,
+ mode = akka.stream.ThrottleMode.Enforcing)
+
+ def socket(apiVersion: Int) = SocketOption { implicit ctx =>
get("sri") ?? { uid =>
Env.lobby.socketHandler(
uid = uid,
user = ctx.me,
- mobile = getBool("mobile")) map some
+ mobile = getBool("mobile")) map { flow =>
+ wsThrottler.via(flow).some
+ }
}
}
diff --git a/app/controllers/Main.scala b/app/controllers/Main.scala
index 266981cfbd..a702d58377 100644
--- a/app/controllers/Main.scala
+++ b/app/controllers/Main.scala
@@ -37,7 +37,7 @@ object Main extends LilaController {
def websocket = SocketOption { implicit ctx =>
get("sri") ?? { uid =>
- Env.site.socketHandler(uid, ctx.userId, get("flag")) map some
+ fuccess(Env.site.socketHandler(uid, ctx.userId, get("flag")).some)
}
}
diff --git a/app/controllers/Prismic.scala b/app/controllers/Prismic.scala
index b256c06854..9c18532d96 100644
--- a/app/controllers/Prismic.scala
+++ b/app/controllers/Prismic.scala
@@ -17,8 +17,12 @@ object Prismic {
case _ => logger info message
}
+ import play.api.libs.ws._
+ import play.api.Play.current
+ private val httpClient: WSClient = WS.client
+
private val fetchPrismicApi = AsyncCache.single[PrismicApi](
- f = PrismicApi.get(Env.api.PrismicApiUrl, logger = prismicLogger),
+ f = PrismicApi.get(httpClient)(Env.api.PrismicApiUrl, logger = prismicLogger),
timeToLive = 1 minute)
def prismicApi = fetchPrismicApi(true)
@@ -33,7 +37,7 @@ object Prismic {
api.forms("everything")
.query(s"""[[:d = at(document.id, "$id")]]""")
.ref(api.master.ref)
- .submit() map {
+ .submit(httpClient) map {
_.results.headOption
}
}
@@ -52,7 +56,7 @@ object Prismic {
api.forms("variant")
.query(s"""[[:d = at(my.variant.key, "${variant.key}")]]""")
.ref(api.master.ref)
- .submit() map {
+ .submit(httpClient) map {
_.results.headOption map (_ -> makeLinkResolver(api))
}
}
diff --git a/app/controllers/Round.scala b/app/controllers/Round.scala
index 3e69d0b1e1..97b0937676 100644
--- a/app/controllers/Round.scala
+++ b/app/controllers/Round.scala
@@ -23,7 +23,7 @@ object Round extends LilaController with TheftPrevention {
private def bookmarkApi = Env.bookmark.api
private def analyser = Env.analyse.analyser
- def websocketWatcher(gameId: String, color: String) = SocketOption[JsValue] { implicit ctx =>
+ def websocketWatcher(gameId: String, color: String) = SocketOption { implicit ctx =>
get("sri") ?? { uid =>
env.socketHandler.watcher(
gameId = gameId,
@@ -35,7 +35,7 @@ object Round extends LilaController with TheftPrevention {
}
}
- def websocketPlayer(fullId: String, apiVersion: Int) = SocketEither[JsValue] { implicit ctx =>
+ def websocketPlayer(fullId: String, apiVersion: Int) = SocketEither { implicit ctx =>
GameRepo pov fullId flatMap {
case Some(pov) =>
if (isTheft(pov)) fuccess(Left(theftResponse))
diff --git a/app/controllers/Simul.scala b/app/controllers/Simul.scala
index b18f11c7d2..18e40a5905 100644
--- a/app/controllers/Simul.scala
+++ b/app/controllers/Simul.scala
@@ -114,7 +114,7 @@ object Simul extends LilaController {
}
}
- def websocket(id: String, apiVersion: Int) = SocketOption[JsValue] { implicit ctx =>
+ def websocket(id: String, apiVersion: Int) = SocketOption { implicit ctx =>
get("sri") ?? { uid =>
env.socketHandler.join(id, uid, ctx.me)
}
diff --git a/app/controllers/Tournament.scala b/app/controllers/Tournament.scala
index 38cf412970..34b622cb22 100644
--- a/app/controllers/Tournament.scala
+++ b/app/controllers/Tournament.scala
@@ -172,7 +172,7 @@ object Tournament extends LilaController {
}
}
- def websocket(id: String, apiVersion: Int) = SocketOption[JsValue] { implicit ctx =>
+ def websocket(id: String, apiVersion: Int) = SocketOption { implicit ctx =>
get("sri") ?? { uid =>
env.socketHandler.join(id, uid, ctx.me)
}
diff --git a/app/controllers/Tv.scala b/app/controllers/Tv.scala
index 4cdf6e03a5..4b99160209 100644
--- a/app/controllers/Tv.scala
+++ b/app/controllers/Tv.scala
@@ -79,10 +79,11 @@ object Tv extends LilaController {
import akka.pattern.ask
import lila.round.TvBroadcast
import play.api.libs.EventSource
- implicit val encoder = play.api.libs.Comet.CometMessage.jsonMessages
- Env.round.tvBroadcast ? TvBroadcast.GetEnumerator mapTo
- manifest[TvBroadcast.EnumeratorType] map { enum =>
- Ok.chunked(enum &> EventSource()).as("text/event-stream")
+ import akka.stream.scaladsl.Source
+ Env.round.tvBroadcast ? TvBroadcast.GetPublisher mapTo
+ manifest[TvBroadcast.PublisherType] map { publisher =>
+ val source = Source fromPublisher publisher
+ Ok.chunked(source via EventSource.flow).as("text/event-stream")
}
}
diff --git a/app/controllers/User.scala b/app/controllers/User.scala
index 5fbe6ba03a..a711e02886 100644
--- a/app/controllers/User.scala
+++ b/app/controllers/User.scala
@@ -7,12 +7,10 @@ import lila.api.{ Context, BodyContext }
import lila.app._
import lila.app.mashup.GameFilterMenu
import lila.common.LilaCookie
-import lila.db.api.$find
import lila.evaluation.{ PlayerAggregateAssessment }
import lila.game.{ GameRepo, Pov }
import lila.rating.PerfType
import lila.security.Permission
-import lila.user.tube.userTube
import lila.user.{ User => UserModel, UserRepo }
import views._
diff --git a/app/controllers/WorldMap.scala b/app/controllers/WorldMap.scala
index 9ffbde72e3..3e233cd162 100644
--- a/app/controllers/WorldMap.scala
+++ b/app/controllers/WorldMap.scala
@@ -14,10 +14,8 @@ object WorldMap extends LilaController {
}
def stream = Action.async {
- Env.worldMap.getStream map { stream =>
- Ok.chunked(
- stream &> EventSource()
- ) as "text/event-stream"
+ Env.worldMap.getSource map { source =>
+ Ok.chunked(source via EventSource.flow).as("text/event-stream")
}
}
}
diff --git a/app/mashup/GameFilter.scala b/app/mashup/GameFilter.scala
index a7789c4458..329e59b8db 100644
--- a/app/mashup/GameFilter.scala
+++ b/app/mashup/GameFilter.scala
@@ -2,11 +2,10 @@ package lila.app
package mashup
import lila.common.paginator.Paginator
-import lila.db.api.SortOrder
+import lila.db.dsl._
import lila.game.{ Game, Query, GameRepo }
import lila.user.User
-import play.api.libs.json._
import play.api.mvc.Request
import scalaz.NonEmptyList
@@ -35,7 +34,6 @@ case class GameFilterMenu(
object GameFilterMenu {
import GameFilter._
- import lila.db.Implicits.docId
val all = NonEmptyList.nel(All, List(Me, Rated, Win, Loss, Draw, Playing, Bookmark, Imported, Search))
@@ -96,22 +94,22 @@ object GameFilterMenu {
me: Option[User],
page: Int)(implicit req: Request[_]): Fu[Paginator[Game]] = {
val nb = cachedNbOf(user, info, filter)
- def std(query: JsObject) = pag.recentlyCreated(query, nb)(page)
+ def std(query: Bdoc) = pag.recentlyCreated(query, nb)(page)
filter match {
case Bookmark => Env.bookmark.api.gamePaginatorByUser(user, page)
case Imported => pag.apply(
selector = Query imported user.id,
- sort = Seq("pgni.ca" -> SortOrder.Descending),
+ sort = $sort desc "pgni.ca",
nb = nb)(page)
- case All => std(Query started user)
+ case All => std(Query started user.id)
case Me => std(Query.opponents(user, me | user))
- case Rated => std(Query rated user)
- case Win => std(Query win user)
- case Loss => std(Query loss user)
- case Draw => std(Query draw user)
+ case Rated => std(Query rated user.id)
+ case Win => std(Query win user.id)
+ case Loss => std(Query loss user.id)
+ case Draw => std(Query draw user.id)
case Playing => pag(
selector = Query nowPlaying user.id,
- sort = Seq(),
+ sort = $empty,
nb = nb)(page) addEffect { p =>
p.currentPageResults.filter(_.finishedOrAborted) foreach GameRepo.unsetPlayingUids
}
diff --git a/app/mashup/TeamInfo.scala b/app/mashup/TeamInfo.scala
index 9956a60742..624d458df4 100644
--- a/app/mashup/TeamInfo.scala
+++ b/app/mashup/TeamInfo.scala
@@ -3,10 +3,9 @@ package mashup
import scala.concurrent.duration._
-import lila.db.api._
+import lila.db.dsl._
import lila.forum.MiniForumPost
import lila.game.{ GameRepo, Game }
-import lila.team.tube._
import lila.team.{ Team, Request, RequestRepo, MemberRepo, RequestWithUser, TeamApi }
import lila.user.{ User, UserRepo }
diff --git a/app/mashup/UserInfo.scala b/app/mashup/UserInfo.scala
index 092bc53287..25a3870c1d 100644
--- a/app/mashup/UserInfo.scala
+++ b/app/mashup/UserInfo.scala
@@ -67,7 +67,8 @@ object UserInfo {
isDonor: String => Fu[Boolean],
isHostingSimul: String => Fu[Boolean],
isStreamer: String => Boolean,
- insightShare: lila.insight.Share)(user: User, ctx: Context): Fu[UserInfo] =
+ insightShare: lila.insight.Share,
+ getPlayTime: User => Fu[User.PlayTime])(user: User, ctx: Context): Fu[UserInfo] =
countUsers() zip
getRanks(user.id) zip
(gameCached nbPlaying user.id) zip
@@ -81,7 +82,7 @@ object UserInfo {
isDonor(user.id) zip
trophyApi.findByUser(user) zip
(user.count.rated >= 10).??(insightShare.grant(user, ctx.me)) zip
- PlayTime(user) flatMap {
+ getPlayTime(user) flatMap {
case (((((((((((((nbUsers, ranks), nbPlaying), nbImported), crosstable), ratingChart), nbFollowing), nbFollowers), nbBlockers), nbPosts), isDonor), trophies), insightVisible), playTime) =>
(nbPlaying > 0) ?? isHostingSimul(user.id) map { hasSimul =>
new UserInfo(
diff --git a/app/views/base/errorPage.scala.html b/app/views/base/errorPage.scala.html
index 348fa41b6a..335a63a41a 100644
--- a/app/views/base/errorPage.scala.html
+++ b/app/views/base/errorPage.scala.html
@@ -1,16 +1,27 @@
@(ex: Throwable)(implicit ctx: Context)
@site.layout(
-title = "Internal server error") {
+title = "Internal server error",
+moreCss = cssTag("notFound.css")) {
-
Something went wrong on this page.
-
-
-
If the problem persists, please report it in the forum.
-
Or send me an email at lichess.contact@gmail.com
-
-
-
@ex.getMessage
+
+
}
diff --git a/app/views/site/worldMap.scala.html b/app/views/site/worldMap.scala.html
index 6c6df523f8..77ccb077f6 100644
--- a/app/views/site/worldMap.scala.html
+++ b/app/views/site/worldMap.scala.html
@@ -1,7 +1,6 @@
@()
-
Lichess World Map
diff --git a/bin/build-deps.sh b/bin/build-deps.sh
index b00d5e44d8..19135ad3a2 100755
--- a/bin/build-deps.sh
+++ b/bin/build-deps.sh
@@ -22,8 +22,8 @@ cd ..
git clone https://github.com/ornicar/scala-kit --branch lichess-fork
cd scala-kit
-git checkout b019b3a2522d3f1697c39ec0c79e88c18ea49a91
-sbt -Dversion=1.2.11-THIB publish-local
+git checkout 75fb61c8306a7a3db4c5dae6f110972130e7c6bc
+sbt -Dversion=1.2.11-THIB2 publish-local
cd ..
git clone https://github.com/ornicar/maxmind-geoip2-scala --branch customBuild
diff --git a/conf/base.conf b/conf/base.conf
index 0639a23eb4..a99ddb3997 100644
--- a/conf/base.conf
+++ b/conf/base.conf
@@ -14,6 +14,9 @@ net {
}
forcedev = false
play {
+ modules.enabled += "lila.app.LilaModule"
+ http.requestHandler = "lila.app.LilaHttpRequestHandler"
+ http.errorHandler = "lila.app.LilaHttpErrorHandler"
server {
netty {
# The maximum length of the initial line. This effectively restricts the maximum length of a URL that the server will
@@ -24,6 +27,10 @@ play {
# The maximum length of the HTTP headers. The most common effect of this is a restriction in cookie length, including
# number of cookies and size of cookie values.
maxHeaderSize = 4096 # 8192
+
+ # "native is faster but only available on linux
+ # "native" is enabled in production
+ transport = "jdk"
}
}
i18n {
@@ -38,16 +45,26 @@ play {
ws {
useragent = ${net.base_url}
compressionEnabled = true
+ followRedirects = true
timeout {
- connection = 5 seconds
- idle = 5 minutes
- request = 5 minutes
+ connection = 10 seconds
+ idle = 30 seconds
+ request = 1 minute
+ }
+
+ # Configuration specific to the Ahc implementation of the WS client
+ ahc {
+ keepAlive = true
+ maxNumberOfRedirects = 3
+ maxRequestRetry = 3
+ # If non null, the maximum time that a connection should live for in the pool.
+ maxConnectionLifetime = null
+ # If non null, the time after which a connection that has been idle in the pool should be closed.
+ idleConnectionInPoolTimeout = 1 minute
}
- }
- crypto {
- secret="CiebwjgIM9cHQ;I?Xk:sfqDJ;BhIe:jsL?r=?IPF[saf>s^r0]?0grUq4>q?5mP^"
}
akka.actor-system = "lila"
+ crypto.secret="CiebwjgIM9cHQ;I?Xk:sfqDJ;BhIe:jsL?r=?IPF[saf>s^r0]?0grUq4>q?5mP^"
}
app {
scheduler {
diff --git a/conf/logback.xml b/conf/logback.xml
index 4c9e1dd4f5..ae988ca169 100644
--- a/conf/logback.xml
+++ b/conf/logback.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/modules/analyse/src/main/Analysis.scala b/modules/analyse/src/main/Analysis.scala
index a29c1add6d..2929642834 100644
--- a/modules/analyse/src/main/Analysis.scala
+++ b/modules/analyse/src/main/Analysis.scala
@@ -74,6 +74,4 @@ object Analysis {
"by" -> o.by,
"date" -> w.date(o.date))
}
-
- private[analyse] lazy val tube = lila.db.BsTube(analysisBSONHandler)
}
diff --git a/modules/analyse/src/main/AnalysisRepo.scala b/modules/analyse/src/main/AnalysisRepo.scala
index 5c27466eaf..5f148119e0 100644
--- a/modules/analyse/src/main/AnalysisRepo.scala
+++ b/modules/analyse/src/main/AnalysisRepo.scala
@@ -2,25 +2,25 @@ package lila.analyse
import org.joda.time.DateTime
import play.api.libs.json.Json
-import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.Game
-import tube.analysisTube
object AnalysisRepo {
import Analysis.analysisBSONHandler
+ // dirty
+ private val coll = Env.current.analysisColl
+
type ID = String
- def save(analysis: Analysis) = analysisTube.coll insert analysis void
+ def save(analysis: Analysis) = coll insert analysis void
- def byId(id: ID): Fu[Option[Analysis]] = $find byId id
+ def byId(id: ID): Fu[Option[Analysis]] = coll.byId[Analysis](id)
def byIds(ids: Seq[ID]): Fu[Seq[Option[Analysis]]] =
- $find optionsByOrderedIds ids
+ coll.optionsByOrderedIds[Analysis](ids)(_.id)
def associateToGames(games: List[Game]): Fu[List[(Game, Analysis)]] =
byIds(games.map(_.id)) map { as =>
@@ -29,7 +29,7 @@ object AnalysisRepo {
}
}
- def remove(id: String) = $remove byId id
+ def remove(id: String) = coll remove $id(id)
- def exists(id: String) = $count exists id
+ def exists(id: String) = coll exists $id(id)
}
diff --git a/modules/analyse/src/main/package.scala b/modules/analyse/src/main/package.scala
index 52e0eb1278..08d552a8df 100644
--- a/modules/analyse/src/main/package.scala
+++ b/modules/analyse/src/main/package.scala
@@ -5,10 +5,4 @@ package object analyse extends PackageObject with WithPlay {
type InfoAdvices = List[(Info, Option[Advice])]
type PgnMove = String
-
- object tube {
-
- private[analyse] implicit lazy val analysisTube =
- Analysis.tube inColl Env.current.analysisColl
- }
}
diff --git a/modules/api/src/main/Cli.scala b/modules/api/src/main/Cli.scala
index 856606c2f7..74a89894c2 100644
--- a/modules/api/src/main/Cli.scala
+++ b/modules/api/src/main/Cli.scala
@@ -20,11 +20,6 @@ private[api] final class Cli(bus: lila.common.Bus, renderer: ActorSelection) ext
def process = {
case "deploy" :: "pre" :: Nil => remindDeploy(lila.hub.actorApi.RemindDeployPre)
case "deploy" :: "post" :: Nil => remindDeploy(lila.hub.actorApi.RemindDeployPost)
- case "rating" :: "fest" :: Nil => RatingFest(
- lila.db.Env.current,
- lila.round.Env.current.perfsUpdater,
- lila.game.Env.current,
- lila.user.Env.current) inject "done"
}
private def remindDeploy(event: RemindDeploy): Fu[String] = {
@@ -48,10 +43,8 @@ private[api] final class Cli(bus: lila.common.Bus, renderer: ActorSelection) ext
lila.game.Env.current.cli.process orElse
lila.gameSearch.Env.current.cli.process orElse
lila.teamSearch.Env.current.cli.process orElse
- lila.forum.Env.current.cli.process orElse
lila.forumSearch.Env.current.cli.process orElse
lila.team.Env.current.cli.process orElse
- lila.round.Env.current.cli.process orElse
lila.puzzle.Env.current.cli.process orElse
lila.tournament.Env.current.cli.process orElse
lila.explorer.Env.current.cli.process orElse
diff --git a/modules/api/src/main/Env.scala b/modules/api/src/main/Env.scala
index bbe3877204..70ea0b2b48 100644
--- a/modules/api/src/main/Env.scala
+++ b/modules/api/src/main/Env.scala
@@ -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,
diff --git a/modules/api/src/main/GameApi.scala b/modules/api/src/main/GameApi.scala
index 43bb97605c..662ba85f2e 100644
--- a/modules/api/src/main/GameApi.scala
+++ b/modules/api/src/main/GameApi.scala
@@ -7,12 +7,10 @@ import chess.format.pgn.Pgn
import lila.analyse.{ AnalysisRepo, Analysis }
import lila.common.paginator.{ Paginator, PaginatorJson }
import lila.common.PimpedJson._
-import lila.db.api._
-import lila.db.Implicits._
-import lila.db.paginator.{ BSONAdapter, CachedAdapter }
+import lila.db.dsl._
+import lila.db.paginator.{ Adapter, CachedAdapter }
import lila.game.BSONHandlers._
import lila.game.Game.{ BSONFields => G }
-import lila.game.tube.gameTube
import lila.game.{ Game, GameRepo, PerfPicker }
import lila.hub.actorApi.{ router => R }
import lila.user.User
@@ -38,16 +36,16 @@ private[api] final class GameApi(
nb: Option[Int],
page: Option[Int]): Fu[JsObject] = Paginator(
adapter = new CachedAdapter(
- adapter = new BSONAdapter[Game](
- collection = gameTube.coll,
- selector = BSONDocument(
+ adapter = new Adapter[Game](
+ collection = GameRepo.coll,
+ selector = $doc(
G.playerUids -> user.id,
- G.status -> BSONDocument("$gte" -> chess.Status.Mate.id),
- G.rated -> rated.map(_.fold[BSONValue](BSONBoolean(true), BSONDocument("$exists" -> false))),
- G.analysed -> analysed.map(_.fold[BSONValue](BSONBoolean(true), BSONDocument("$exists" -> false)))
+ G.status -> $doc("$gte" -> chess.Status.Mate.id),
+ G.rated -> rated.map(_.fold[BSONValue](BSONBoolean(true), $doc("$exists" -> false))),
+ G.analysed -> analysed.map(_.fold[BSONValue](BSONBoolean(true), $doc("$exists" -> false)))
),
- projection = BSONDocument(),
- sort = BSONDocument(G.createdAt -> -1)
+ projection = $empty,
+ sort = $doc(G.createdAt -> -1)
),
nbResults = fuccess {
rated.fold(user.count.game)(_.fold(user.count.rated, user.count.casual))
@@ -74,7 +72,7 @@ private[api] final class GameApi(
withFens: Boolean,
withMoveTimes: Boolean,
token: Option[String]): Fu[Option[JsObject]] =
- $find byId id flatMap {
+ GameRepo game id flatMap {
_ ?? { g =>
gamesJson(
withAnalysis = withAnalysis,
diff --git a/modules/api/src/main/PgnDump.scala b/modules/api/src/main/PgnDump.scala
index c34e03bcaf..142be09f56 100644
--- a/modules/api/src/main/PgnDump.scala
+++ b/modules/api/src/main/PgnDump.scala
@@ -1,8 +1,7 @@
package lila.api
import chess.format.pgn.{ Pgn, Parser }
-import lila.db.api.$query
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.Game
import lila.game.{ GameRepo, Query }
import play.api.libs.iteratee._
@@ -22,15 +21,11 @@ final class PgnDump(
def filename(game: Game) = dumper filename game
def exportUserGames(userId: String): Enumerator[String] = PgnStream {
- import lila.game.tube.gameTube
- import lila.game.BSONHandlers.gameBSONHandler
- pimpQB($query(Query user userId)).sort(Query.sortCreated).cursor[Game]()
+ GameRepo.sortedCursor(Query user userId, Query.sortCreated)
}
def exportGamesFromIds(ids: List[String]): Enumerator[String] = PgnStream {
- import lila.game.tube.gameTube
- import lila.game.BSONHandlers.gameBSONHandler
- pimpQB($query byIds ids).sort(Query.sortCreated).cursor[Game]()
+ GameRepo.sortedCursor($inIds(ids), Query.sortCreated)
}
private def PgnStream(cursor: reactivemongo.api.Cursor[Game]): Enumerator[String] = {
diff --git a/modules/api/src/main/RatingFest.scala b/modules/api/src/main/RatingFest.scala
deleted file mode 100644
index e8ffcdf0bb..0000000000
--- a/modules/api/src/main/RatingFest.scala
+++ /dev/null
@@ -1,86 +0,0 @@
-package lila.api
-
-import play.api.libs.iteratee._
-import play.api.libs.json.Json
-import reactivemongo.bson._
-
-import lila.db.api._
-import lila.db.Implicits._
-import lila.game.BSONHandlers.gameBSONHandler
-import lila.game.Game.{ BSONFields => G }
-import lila.game.{ Game, GameRepo, PerfPicker }
-import lila.round.PerfsUpdater
-import lila.user.{ User, UserRepo }
-
-object RatingFest {
-
- def apply(
- db: lila.db.Env,
- perfsUpdater: PerfsUpdater,
- gameEnv: lila.game.Env,
- userEnv: lila.user.Env) = {
-
- // val limit = Int.MaxValue
- // val limit = 100000
- val limit = Int.MaxValue
- val bulkSize = 4
-
- def rerate(g: Game) = UserRepo.pair(g.whitePlayer.userId, g.blackPlayer.userId).flatMap {
- case (Some(white), Some(black)) =>
- perfsUpdater.save(g, white, black, resetGameRatings = true) void
- case _ => funit
- }
-
- def unrate(game: Game) =
- (game.whitePlayer.ratingDiff.isDefined || game.blackPlayer.ratingDiff.isDefined) ?? GameRepo.unrate(game.id).void
-
- def log(x: Any) = lila.log("ratingFest") info x.toString
-
- var nb = 0
- for {
- _ <- fuccess(log("Removing history"))
- _ <- db("history3").remove(BSONDocument())
- _ = log("Reseting perfs")
- _ <- lila.user.tube.userTube.coll.update(
- BSONDocument(),
- BSONDocument("$unset" -> BSONDocument(
- List(
- "global", "white", "black",
- "standard", "chess960", "kingOfTheHill", "threeCheck",
- "bullet", "blitz", "classical", "correspondence"
- ).map { name => s"perfs.$name" -> BSONBoolean(true) }
- )),
- multi = true)
- _ = log("Gathering cheater IDs")
- engineIds <- UserRepo.engineIds
- _ = log(s"Found ${engineIds.size} cheaters")
- _ = log("Starting the party")
- _ <- lila.game.tube.gameTube |> { implicit gameTube =>
- val query = $query(lila.game.Query.rated)
- // val query = $query.all
- // .batch(100)
- .sort($sort asc G.createdAt)
- var started = nowMillis
- $enumerate.bulk[Game](query, bulkSize, limit) { games =>
- nb = nb + bulkSize
- if (nb % 1000 == 0) {
- val perS = 1000 * 1000 / math.max(1, nowMillis - started)
- started = nowMillis
- log("Processed %d games at %d/s".format(nb, perS))
- }
- games.map { game =>
- game.userIds match {
- case _ if !game.rated => funit
- case _ if !game.finished => funit
- case _ if game.fromPosition => funit
- case List(uidW, uidB) if (uidW == uidB) => funit
- case List(uidW, uidB) if engineIds(uidW) || engineIds(uidB) => unrate(game)
- case List(uidW, uidB) => rerate(game)
- case _ => funit
- }
- }.sequenceFu.void
- } andThen { case _ => log(nb) }
- }
- } yield ()
- }
-}
diff --git a/modules/api/src/main/UserApi.scala b/modules/api/src/main/UserApi.scala
index 381a6865df..892d488a94 100644
--- a/modules/api/src/main/UserApi.scala
+++ b/modules/api/src/main/UserApi.scala
@@ -3,12 +3,10 @@ package lila.api
import play.api.libs.json._
import lila.common.PimpedJson._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.GameRepo
import lila.hub.actorApi.{ router => R }
import lila.rating.Perf
-import lila.user.tube.userTube
import lila.user.{ UserRepo, User, Perfs, Profile }
import makeTimeout.short
diff --git a/modules/blog/src/main/BlogApi.scala b/modules/blog/src/main/BlogApi.scala
index f4970e5f85..330a96fb36 100644
--- a/modules/blog/src/main/BlogApi.scala
+++ b/modules/blog/src/main/BlogApi.scala
@@ -6,15 +6,19 @@ import scala.concurrent.duration._
final class BlogApi(prismicUrl: String, collection: String) {
+ import play.api.libs.ws._
+ import play.api.Play.current
+ private val httpClient: WSClient = WS.client
+
def recent(api: Api, ref: Option[String], nb: Int): Fu[Option[Response]] =
api.forms(collection).ref(resolveRef(api)(ref) | api.master.ref)
.orderings(s"[my.$collection.date desc]")
- .pageSize(nb).page(1).submit().fold(_ => none, some _)
+ .pageSize(nb).page(1).submit(httpClient).fold(_ => none, some _)
def one(api: Api, ref: Option[String], id: String) =
api.forms(collection)
.query(s"""[[:d = at(document.id, "$id")]]""")
- .ref(resolveRef(api)(ref) | api.master.ref).submit() map (_.results.headOption)
+ .ref(resolveRef(api)(ref) | api.master.ref).submit(httpClient) map (_.results.headOption)
// -- Build a Prismic context
def context(refName: Option[String])(implicit linkResolver: (Api, Option[String]) => DocumentLinkResolver) =
@@ -39,7 +43,7 @@ final class BlogApi(prismicUrl: String, collection: String) {
}
private val fetchPrismicApi = AsyncCache.single[Api](
- f = Api.get(prismicUrl, cache = cache, logger = prismicLogger),
+ f = Api.get(httpClient)(prismicUrl, cache = cache, logger = prismicLogger),
timeToLive = 10 seconds)
def prismicApi = fetchPrismicApi(true)
diff --git a/modules/bookmark/src/main/BookmarkApi.scala b/modules/bookmark/src/main/BookmarkApi.scala
index 9bc731e236..f5a38e07e5 100644
--- a/modules/bookmark/src/main/BookmarkApi.scala
+++ b/modules/bookmark/src/main/BookmarkApi.scala
@@ -1,17 +1,18 @@
package lila.bookmark
-import lila.db.api._
-import lila.game.tube.gameTube
+import lila.db.dsl._
import lila.game.{ Game, GameRepo }
import lila.user.User
-import tube.bookmarkTube
final class BookmarkApi(
+ coll: Coll,
cached: Cached,
paginator: PaginatorBuilder) {
+ import lila.game.BSONHandlers.gameBSONHandler
+
def toggle(gameId: String, userId: String): Funit =
- $find.byId[Game](gameId) flatMap {
+ coll.byId[Game](gameId) flatMap {
_ ?? { game =>
BookmarkRepo.toggle(gameId, userId) flatMap { bookmarked =>
GameRepo.incBookmarks(gameId, bookmarked.fold(1, -1)) >>-
diff --git a/modules/bookmark/src/main/BookmarkRepo.scala b/modules/bookmark/src/main/BookmarkRepo.scala
index 8154a64fc3..b0a440ef26 100644
--- a/modules/bookmark/src/main/BookmarkRepo.scala
+++ b/modules/bookmark/src/main/BookmarkRepo.scala
@@ -1,19 +1,19 @@
package lila.bookmark
import org.joda.time.DateTime
-import play.api.libs.json._
import reactivemongo.bson._
-import lila.db.api._
-import lila.db.Implicits._
-import tube.bookmarkTube
+import lila.db.dsl._
case class Bookmark(game: lila.game.Game, user: lila.user.User)
private[bookmark] object BookmarkRepo {
+ // dirty
+ private val coll = Env.current.bookmarkColl
+
def toggle(gameId: String, userId: String): Fu[Boolean] =
- $count exists selectId(gameId, userId) flatMap { e =>
+ coll exists selectId(gameId, userId) flatMap { e =>
e.fold(
remove(gameId, userId),
add(gameId, userId, DateTime.now)
@@ -21,25 +21,25 @@ private[bookmark] object BookmarkRepo {
}
def gameIdsByUserId(userId: String): Fu[Set[String]] =
- bookmarkTube.coll.distinct("g", BSONDocument("u" -> userId).some) map lila.db.BSON.asStringSet
+ coll.distinct("g", $doc("u" -> userId).some) map lila.db.BSON.asStringSet
def removeByGameId(gameId: String): Funit =
- $remove(Json.obj("g" -> gameId))
+ coll.remove($doc("g" -> gameId)).void
def removeByGameIds(gameIds: List[String]): Funit =
- $remove(Json.obj("g" -> $in(gameIds)))
+ coll.remove($doc("g" -> $in(gameIds: _*))).void
private def add(gameId: String, userId: String, date: DateTime): Funit =
- $insert(Json.obj(
+ coll.insert($doc(
"_id" -> makeId(gameId, userId),
"g" -> gameId,
"u" -> userId,
- "d" -> $date(date)))
+ "d" -> date)).void
- def userIdQuery(userId: String) = Json.obj("u" -> userId)
- def makeId(gameId: String, userId: String) = gameId + userId
- def selectId(gameId: String, userId: String) = $select(makeId(gameId, userId))
+ def userIdQuery(userId: String) = $doc("u" -> userId)
+ def makeId(gameId: String, userId: String) = s"$gameId$userId"
+ def selectId(gameId: String, userId: String) = $id(makeId(gameId, userId))
- def remove(gameId: String, userId: String): Funit = $remove(selectId(gameId, userId))
- def remove(selector: JsObject): Funit = $remove(selector)
+ def remove(gameId: String, userId: String): Funit = coll.remove(selectId(gameId, userId)).void
+ def remove(selector: Bdoc): Funit = coll.remove(selector).void
}
diff --git a/modules/bookmark/src/main/Env.scala b/modules/bookmark/src/main/Env.scala
index 4f5a6b783f..7b2f65b264 100644
--- a/modules/bookmark/src/main/Env.scala
+++ b/modules/bookmark/src/main/Env.scala
@@ -20,9 +20,11 @@ final class Env(
private lazy val cached = new Cached
lazy val paginator = new PaginatorBuilder(
+ coll = bookmarkColl,
maxPerPage = PaginatorMaxPerPage)
lazy val api = new BookmarkApi(
+ coll = bookmarkColl,
cached = cached,
paginator = paginator)
diff --git a/modules/bookmark/src/main/PaginatorBuilder.scala b/modules/bookmark/src/main/PaginatorBuilder.scala
index e661ac4bfd..9b0008002b 100644
--- a/modules/bookmark/src/main/PaginatorBuilder.scala
+++ b/modules/bookmark/src/main/PaginatorBuilder.scala
@@ -1,18 +1,14 @@
package lila.bookmark
-import play.api.libs.json._
-import play.modules.reactivemongo.json.ImplicitBSONHandlers._
-
import lila.common.paginator._
-import lila.common.PimpedJson._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
import lila.game.{ Game, GameRepo }
import lila.user.User
-import tube.bookmarkTube
-private[bookmark] final class PaginatorBuilder(maxPerPage: Int) {
+private[bookmark] final class PaginatorBuilder(
+ coll: Coll,
+ maxPerPage: Int) {
def byUser(user: User, page: Int): Fu[Paginator[Bookmark]] =
paginator(new UserAdapter(user), page)
@@ -26,17 +22,15 @@ private[bookmark] final class PaginatorBuilder(maxPerPage: Int) {
final class UserAdapter(user: User) extends AdapterLike[Bookmark] {
- def nbResults: Fu[Int] = $count(selector)
+ def nbResults: Fu[Int] = coll countSel selector
def slice(offset: Int, length: Int): Fu[Seq[Bookmark]] = for {
- gameIds ← $primitive(
- selector,
- "g",
- _ sort sorting skip offset,
- length.some)(_.asOpt[String])
- games ← lila.game.tube.gameTube |> { implicit t =>
- $find.byOrderedIds[Game](gameIds)
- }
+ gameIds ← coll.find(selector, $doc("g" -> true))
+ .sort(sorting)
+ .skip(offset)
+ .cursor[Bdoc]()
+ .gather[List](length) map { _ flatMap { _.getAs[String]("g") } }
+ games ← GameRepo games gameIds
} yield games map { g => Bookmark(g, user) }
private def selector = BookmarkRepo userIdQuery user.id
diff --git a/modules/bookmark/src/main/package.scala b/modules/bookmark/src/main/package.scala
index 545c2376d3..c2b938bf74 100644
--- a/modules/bookmark/src/main/package.scala
+++ b/modules/bookmark/src/main/package.scala
@@ -1,12 +1,3 @@
package lila
-import lila.db.JsTube
-
-package object bookmark extends PackageObject with WithPlay {
-
- object tube {
-
- private[bookmark] implicit lazy val bookmarkTube =
- JsTube.json inColl Env.current.bookmarkColl
- }
-}
+package object bookmark extends PackageObject with WithPlay
diff --git a/modules/challenge/src/main/BSONHandlers.scala b/modules/challenge/src/main/BSONHandlers.scala
index 7e78144f59..d42d43c9d2 100644
--- a/modules/challenge/src/main/BSONHandlers.scala
+++ b/modules/challenge/src/main/BSONHandlers.scala
@@ -5,9 +5,8 @@ import reactivemongo.bson._
import chess.Mode
import chess.variant.Variant
import lila.db.BSON
-import lila.db.BSON._
-import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits._
+import lila.db.BSON.{ Reader, Writer }
+import lila.db.dsl._
import lila.rating.PerfType
private object BSONHandlers {
@@ -37,9 +36,9 @@ private object BSONHandlers {
r intO "d" map TimeControl.Correspondence.apply
} getOrElse TimeControl.Unlimited
def writes(w: Writer, t: TimeControl) = t match {
- case TimeControl.Clock(l, i) => BSONDocument("l" -> l, "i" -> i)
- case TimeControl.Correspondence(d) => BSONDocument("d" -> d)
- case TimeControl.Unlimited => BSONDocument()
+ case TimeControl.Clock(l, i) => $doc("l" -> l, "i" -> i)
+ case TimeControl.Correspondence(d) => $doc("d" -> d)
+ case TimeControl.Unlimited => $empty
}
}
implicit val VariantBSONHandler = new BSONHandler[BSONInteger, Variant] {
@@ -56,19 +55,19 @@ private object BSONHandlers {
}
implicit val RatingBSONHandler = new BSON[Rating] {
def reads(r: Reader) = Rating(r.int("i"), r.boolD("p"))
- def writes(w: Writer, r: Rating) = BSONDocument(
+ def writes(w: Writer, r: Rating) = $doc(
"i" -> r.int,
"p" -> w.boolO(r.provisional))
}
implicit val RegisteredBSONHandler = new BSON[Registered] {
def reads(r: Reader) = Registered(r.str("id"), r.get[Rating]("r"))
- def writes(w: Writer, r: Registered) = BSONDocument(
+ def writes(w: Writer, r: Registered) = $doc(
"id" -> r.id,
"r" -> r.rating)
}
implicit val AnonymousBSONHandler = new BSON[Anonymous] {
def reads(r: Reader) = Anonymous(r.str("s"))
- def writes(w: Writer, a: Anonymous) = BSONDocument(
+ def writes(w: Writer, a: Anonymous) = $doc(
"s" -> a.secret)
}
implicit val EitherChallengerBSONHandler = new BSON[EitherChallenger] {
diff --git a/modules/challenge/src/main/ChallengeRepo.scala b/modules/challenge/src/main/ChallengeRepo.scala
index 49e439f8d6..ffa5e08ef4 100644
--- a/modules/challenge/src/main/ChallengeRepo.scala
+++ b/modules/challenge/src/main/ChallengeRepo.scala
@@ -1,12 +1,9 @@
package lila.challenge
import org.joda.time.DateTime
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONRegex, BSONArray, BSONBoolean }
import scala.concurrent.duration._
-import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits.LilaBSONDocumentZero
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
@@ -14,9 +11,9 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
import BSONHandlers._
import Challenge._
- def byId(id: Challenge.ID) = coll.find(selectId(id)).one[Challenge]
+ def byId(id: Challenge.ID) = coll.find($id(id)).uno[Challenge]
- def exists(id: Challenge.ID) = coll.count(selectId(id).some).map(0<)
+ def exists(id: Challenge.ID) = coll.count($id(id).some).map(0<)
def insert(c: Challenge): Funit =
coll.insert(c) >> c.challenger.right.toOption.?? { challenger =>
@@ -27,55 +24,55 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
}
def createdByChallengerId(userId: String): Fu[List[Challenge]] =
- coll.find(selectCreated ++ BSONDocument("challenger.id" -> userId))
- .sort(BSONDocument("createdAt" -> 1))
- .cursor[Challenge]().collect[List]()
+ coll.find(selectCreated ++ $doc("challenger.id" -> userId))
+ .sort($doc("createdAt" -> 1))
+ .cursor[Challenge]().gather[List]()
def createdByDestId(userId: String): Fu[List[Challenge]] =
- coll.find(selectCreated ++ BSONDocument("destUser.id" -> userId))
- .sort(BSONDocument("createdAt" -> 1))
- .cursor[Challenge]().collect[List]()
+ coll.find(selectCreated ++ $doc("destUser.id" -> userId))
+ .sort($doc("createdAt" -> 1))
+ .cursor[Challenge]().gather[List]()
def removeByUserId(userId: String): Funit =
- coll.remove(BSONDocument("$or" -> BSONArray(
- BSONDocument("challenger.id" -> userId),
- BSONDocument("destUser.id" -> userId)
- ))).void
+ coll.remove($or(
+ $doc("challenger.id" -> userId),
+ $doc("destUser.id" -> userId)
+ )).void
def like(c: Challenge) = ~(for {
challengerId <- c.challengerUserId
destUserId <- c.destUserId
if c.active
- } yield coll.find(selectCreated ++ BSONDocument(
+ } 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 ++ BSONDocument("destUser.id" -> userId)))
+ coll.count(Some(selectCreated ++ $doc("destUser.id" -> userId)))
private[challenge] def realTimeUnseenSince(date: DateTime, max: Int): Fu[List[Challenge]] =
- coll.find(selectCreated ++ selectClock ++ BSONDocument(
- "seenAt" -> BSONDocument("$lt" -> date)
- )).cursor[Challenge]().collect[List](max)
+ coll.find(selectCreated ++ selectClock ++ $doc(
+ "seenAt" -> $doc("$lt" -> date)
+ )).cursor[Challenge]().gather[List](max)
private[challenge] def expiredIds(max: Int): Fu[List[Challenge.ID]] =
coll.distinct(
"_id",
- BSONDocument("expiresAt" -> BSONDocument("$lt" -> DateTime.now)).some
+ $doc("expiresAt" -> $doc("$lt" -> DateTime.now)).some
) map lila.db.BSON.asStrings
def setSeenAgain(id: Challenge.ID) = coll.update(
- selectId(id),
- BSONDocument(
- "$set" -> BSONDocument(
+ $id(id),
+ $doc(
+ "$set" -> $doc(
"status" -> Status.Created.id,
"seenAt" -> DateTime.now,
"expiresAt" -> inTwoWeeks))
).void
def setSeen(id: Challenge.ID) = coll.update(
- selectId(id),
- BSONDocument("$set" -> BSONDocument("seenAt" -> DateTime.now))
+ $id(id),
+ $doc("$set" -> $doc("seenAt" -> DateTime.now))
).void
def offline(challenge: Challenge) = setStatus(challenge, Status.Offline, Some(_ plusHours 3))
@@ -84,25 +81,24 @@ private final class ChallengeRepo(coll: Coll, maxPerUser: Int) {
def accept(challenge: Challenge) = setStatus(challenge, Status.Accepted, Some(_ plusHours 3))
def statusById(id: Challenge.ID) = coll.find(
- selectId(id),
- BSONDocument("status" -> true, "_id" -> false)
- ).one[BSONDocument].map { _.flatMap(_.getAs[Status]("status")) }
+ $id(id),
+ $doc("status" -> true, "_id" -> false)
+ ).uno[Bdoc].map { _.flatMap(_.getAs[Status]("status")) }
private def setStatus(
challenge: Challenge,
status: Status,
expiresAt: Option[DateTime => DateTime] = None) = coll.update(
- selectCreated ++ selectId(challenge.id),
- BSONDocument("$set" -> BSONDocument(
+ selectCreated ++ $id(challenge.id),
+ $doc("$set" -> $doc(
"status" -> status.id,
"expiresAt" -> expiresAt.fold(inTwoWeeks) { _(DateTime.now) }
))
).void
- private[challenge] def remove(id: Challenge.ID) = coll.remove(selectId(id)).void
+ private[challenge] def remove(id: Challenge.ID) = coll.remove($id(id)).void
- private def selectId(id: Challenge.ID) = BSONDocument("_id" -> id)
- private val selectCreated = BSONDocument("status" -> Status.Created.id)
- private val selectClock = BSONDocument("timeControl.l" -> BSONDocument("$exists" -> true))
+ private val selectCreated = $doc("status" -> Status.Created.id)
+ private val selectClock = $doc("timeControl.l" $exists true)
}
diff --git a/modules/challenge/src/main/Env.scala b/modules/challenge/src/main/Env.scala
index 6b063aee01..c9f5880820 100644
--- a/modules/challenge/src/main/Env.scala
+++ b/modules/challenge/src/main/Env.scala
@@ -45,7 +45,7 @@ final class Env(
lazy val socketHandler = new SocketHandler(
hub = hub,
socketHub = socketHub,
- pingChallenge = api.ping)
+ pingChallenge = api.ping)(system)
lazy val api = new ChallengeApi(
repo = repo,
diff --git a/modules/challenge/src/main/Socket.scala b/modules/challenge/src/main/Socket.scala
index 24cf3ca3db..2a52fa1d1d 100644
--- a/modules/challenge/src/main/Socket.scala
+++ b/modules/challenge/src/main/Socket.scala
@@ -1,12 +1,11 @@
package lila.challenge
import akka.actor._
-import play.api.libs.iteratee.Concurrent
import play.api.libs.json._
import scala.concurrent.duration.Duration
import lila.hub.TimeBomb
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.{ SocketActor, History, Historical }
private final class Socket(
@@ -40,15 +39,11 @@ private final class Socket(
if (timeBomb.boom) self ! PoisonPill
}
- case GetVersion => sender ! history.version
+ case GetVersion => sender ! history.version
- case Socket.Join(uid, userId, owner) =>
- val (enumerator, channel) = Concurrent.broadcast[JsValue]
- val member = Socket.Member(channel, userId, owner)
- addMember(uid, member)
- sender ! Socket.Connected(enumerator, member)
+ case Socket.AddMember(uid, member) => addMember(uid, member)
- case Quit(uid) => quit(uid)
+ case Quit(uid) => quit(uid)
}
protected def shouldSkipMessageFor(message: Message, member: Socket.Member) = false
@@ -57,14 +52,13 @@ private final class Socket(
private object Socket {
case class Member(
- channel: JsChannel,
+ out: ActorRef,
userId: Option[String],
owner: Boolean) extends lila.socket.SocketMember {
val troll = false
}
- case class Join(uid: String, userId: Option[String], owner: Boolean)
- case class Connected(enumerator: JsEnumerator, member: Member)
+ case class AddMember(uid: String, member: Member)
case object Reload
}
diff --git a/modules/challenge/src/main/SocketHandler.scala b/modules/challenge/src/main/SocketHandler.scala
index 2d05025ff0..2e20fc17c2 100644
--- a/modules/challenge/src/main/SocketHandler.scala
+++ b/modules/challenge/src/main/SocketHandler.scala
@@ -1,14 +1,11 @@
package lila.challenge
-import scala.concurrent.duration._
-
import akka.actor._
import akka.pattern.ask
-import akka.actor.ActorSelection
import lila.common.PimpedJson._
import lila.hub.actorApi.map._
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.Handler
import lila.user.User
import makeTimeout.short
@@ -16,20 +13,25 @@ import makeTimeout.short
private[challenge] final class SocketHandler(
hub: lila.hub.Env,
socketHub: ActorRef,
- pingChallenge: Challenge.ID => Funit) {
+ pingChallenge: Challenge.ID => Funit)(implicit system: ActorSystem) {
def join(
challengeId: Challenge.ID,
uid: String,
userId: Option[User.ID],
- owner: Boolean): Fu[Option[JsSocketHandler]] = for {
- socket ← socketHub ? Get(challengeId) mapTo manifest[ActorRef]
- join = Socket.Join(uid = uid, userId = userId, owner = owner)
- handler ← Handler(hub, socket, uid, join, userId) {
- case Socket.Connected(enum, member) =>
- (controller(socket, challengeId, uid, member), enum, member)
+ owner: Boolean): Fu[Option[JsFlow]] =
+ socketHub ? Get(challengeId) mapTo manifest[ActorRef] map { socket =>
+ Handler.actorRef { out =>
+ val member = Socket.Member(out, userId, owner)
+ socket ! Socket.AddMember(uid, member)
+ Handler.props(hub, socket, member, uid, userId) {
+ case ("p", o) => o int "v" foreach { v =>
+ socket ! PingVersion(uid, v)
+ }
+ case ("ping", _) if member.owner => pingChallenge(challengeId)
+ }
+ }.some
}
- } yield handler.some
private def controller(
socket: ActorRef,
diff --git a/modules/chat/src/main/ChatApi.scala b/modules/chat/src/main/ChatApi.scala
index 1e9d318326..8156f52511 100644
--- a/modules/chat/src/main/ChatApi.scala
+++ b/modules/chat/src/main/ChatApi.scala
@@ -3,7 +3,7 @@ package lila.chat
import chess.Color
import reactivemongo.bson.BSONDocument
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
final class ChatApi(
@@ -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))
diff --git a/modules/common/src/main/AkkaStream.scala b/modules/common/src/main/AkkaStream.scala
new file mode 100644
index 0000000000..1dd89bdcd0
--- /dev/null
+++ b/modules/common/src/main/AkkaStream.scala
@@ -0,0 +1,23 @@
+package lila.common
+
+import akka.actor._
+import akka.stream._
+import akka.stream.scaladsl._
+import org.reactivestreams.Publisher
+
+object AkkaStream {
+
+ def actorPublisher[T](
+ bufferSize: Int,
+ overflowStrategy: OverflowStrategy = OverflowStrategy.dropHead)(implicit materializer: Materializer): (ActorRef, Publisher[T]) = {
+ Source.actorRef[T](20, OverflowStrategy.dropHead)
+ .toMat(Sink asPublisher false)(Keep.both).run()
+ }
+
+ def actorSource[T](
+ bufferSize: Int,
+ overflowStrategy: OverflowStrategy)(implicit materializer: Materializer): (ActorRef, Source[T, _]) = {
+ val (out, publisher) = actorPublisher(bufferSize, overflowStrategy)
+ (out, Source fromPublisher publisher)
+ }
+}
diff --git a/modules/common/src/main/PackageObject.scala b/modules/common/src/main/PackageObject.scala
index 204ef2a21e..75089730ea 100644
--- a/modules/common/src/main/PackageObject.scala
+++ b/modules/common/src/main/PackageObject.scala
@@ -97,6 +97,9 @@ trait WithPlay { self: PackageObject =>
implicit def execontext = play.api.libs.concurrent.Execution.defaultContext
+ implicit def materializer(implicit system: akka.actor.ActorSystem): akka.stream.Materializer =
+ akka.stream.ActorMaterializer()
+
implicit val LilaFutureMonad = new Monad[Fu] {
override def map[A, B](fa: Fu[A])(f: A => B) = fa map f
def point[A](a: => A) = fuccess(a)
@@ -123,95 +126,8 @@ trait WithPlay { self: PackageObject =>
Future sequence t
}
- implicit final class LilaPimpedFuture[A](fua: Fu[A]) {
-
- def >>-(sideEffect: => Unit): Fu[A] = fua andThen {
- case _ => sideEffect
- }
-
- def >>[B](fub: => Fu[B]): Fu[B] = fua flatMap (_ => fub)
-
- def void: Funit = fua map (_ => Unit)
-
- def inject[B](b: => B): Fu[B] = fua map (_ => b)
-
- def injectAnyway[B](b: => B): Fu[B] = fua.fold(_ => b, _ => b)
-
- def effectFold(fail: Exception => Unit, succ: A => Unit) {
- fua onComplete {
- case scala.util.Failure(e: Exception) => fail(e)
- case scala.util.Failure(e) => throw e // Throwables
- case scala.util.Success(e) => succ(e)
- }
- }
-
- def andThenAnyway(sideEffect: => Unit): Fu[A] = {
- fua onComplete {
- case scala.util.Failure(_) => sideEffect
- case scala.util.Success(_) => sideEffect
- }
- fua
- }
-
- def fold[B](fail: Exception => B, succ: A => B): Fu[B] =
- fua map succ recover { case e: Exception => fail(e) }
-
- def flatFold[B](fail: Exception => Fu[B], succ: A => Fu[B]): Fu[B] =
- fua flatMap succ recoverWith { case e: Exception => fail(e) }
-
- def logFailure(logger: => lila.log.Logger, msg: Exception => String): Fu[A] =
- addFailureEffect { e => logger.warn(msg(e), e) }
- def logFailure(logger: => lila.log.Logger): Fu[A] = logFailure(logger, _.toString)
-
- def addEffect(effect: A => Unit) = fua ~ (_ foreach effect)
-
- def addFailureEffect(effect: Exception => Unit) = fua ~ (_ onFailure {
- case e: Exception => effect(e)
- })
-
- def addEffects(fail: Exception => Unit, succ: A => Unit): Fu[A] =
- fua andThen {
- case scala.util.Failure(e: Exception) => fail(e)
- case scala.util.Failure(e) => throw e // Throwables
- case scala.util.Success(e) => succ(e)
- }
-
- def mapFailure(f: Exception => Exception) = fua recover {
- case cause: Exception => throw f(cause)
- }
-
- def prefixFailure(p: => String) = fua mapFailure { e =>
- common.LilaException(s"$p ${e.getMessage}")
- }
-
- def thenPp: Fu[A] = fua ~ {
- _.effectFold(
- e => println("[failure] " + e),
- a => println("[success] " + a)
- )
- }
-
- def thenPp(msg: String): Fu[A] = fua ~ {
- _.effectFold(
- e => println(s"[$msg] [failure] $e"),
- a => println(s"[$msg] [success] $a")
- )
- }
-
- def awaitSeconds(seconds: Int): A = {
- import scala.concurrent.duration._
- scala.concurrent.Await.result(fua, seconds.seconds)
- }
-
- def withTimeout(duration: FiniteDuration, error: => Throwable)(implicit system: akka.actor.ActorSystem): Fu[A] = {
- Future firstCompletedOf Seq(fua,
- akka.pattern.after(duration, system.scheduler)(fufail(error)))
- }
-
- def chronometer = lila.common.Chronometer(fua)
-
- def mon(path: lila.mon.RecPath) = chronometer.mon(path).result
- }
+ implicit def LilaPimpedFuture[A](fua: Fu[A]): PimpedFuture.LilaPimpedFuture[A] =
+ new PimpedFuture.LilaPimpedFuture(fua)
implicit final class LilaPimpedFutureZero[A: Zero](fua: Fu[A]) {
@@ -258,6 +174,8 @@ trait WithPlay { self: PackageObject =>
implicit final class LilaPimpedActorSystem(self: akka.actor.ActorSystem) {
def lilaBus = lila.common.Bus(self)
+
+ def lilaMat = materializer(self)
}
object makeTimeout {
diff --git a/modules/common/src/main/PimpedConfig.scala b/modules/common/src/main/PimpedConfig.scala
index 975ae81072..4674f0cf16 100644
--- a/modules/common/src/main/PimpedConfig.scala
+++ b/modules/common/src/main/PimpedConfig.scala
@@ -7,7 +7,7 @@ import com.typesafe.config.Config
object PimpedConfig {
- implicit final class LilaPimpedConfig(config: Config) {
+ implicit final class LilaPimpedConfig(val config: Config) extends AnyVal {
def millis(name: String): Int = config.getDuration(name, TimeUnit.MILLISECONDS).toInt
def seconds(name: String): Int = config.getDuration(name, TimeUnit.SECONDS).toInt
diff --git a/modules/common/src/main/PimpedFuture.scala b/modules/common/src/main/PimpedFuture.scala
new file mode 100644
index 0000000000..94713a8f97
--- /dev/null
+++ b/modules/common/src/main/PimpedFuture.scala
@@ -0,0 +1,109 @@
+package lila
+
+import play.api.libs.concurrent.Execution.Implicits._
+import scala.concurrent.Future
+import scala.concurrent.duration._
+
+object PimpedFuture {
+
+ private type Fu[A] = Future[A]
+ private type Funit = Fu[Unit]
+
+ final class LilaPimpedFuture[A](val fua: Fu[A]) extends AnyVal {
+
+ def >>-(sideEffect: => Unit): Fu[A] = fua andThen {
+ case _ => sideEffect
+ }
+
+ def >>[B](fub: => Fu[B]): Fu[B] = fua flatMap (_ => fub)
+
+ def void: Funit = fua map (_ => Unit)
+
+ def inject[B](b: => B): Fu[B] = fua map (_ => b)
+
+ def injectAnyway[B](b: => B): Fu[B] = fold(_ => b, _ => b)
+
+ def effectFold(fail: Exception => Unit, succ: A => Unit) {
+ fua onComplete {
+ case scala.util.Failure(e: Exception) => fail(e)
+ case scala.util.Failure(e) => throw e // Throwables
+ case scala.util.Success(e) => succ(e)
+ }
+ }
+
+ def andThenAnyway(sideEffect: => Unit): Fu[A] = {
+ fua onComplete {
+ case scala.util.Failure(_) => sideEffect
+ case scala.util.Success(_) => sideEffect
+ }
+ fua
+ }
+
+ def fold[B](fail: Exception => B, succ: A => B): Fu[B] =
+ fua map succ recover { case e: Exception => fail(e) }
+
+ def flatFold[B](fail: Exception => Fu[B], succ: A => Fu[B]): Fu[B] =
+ fua flatMap succ recoverWith { case e: Exception => fail(e) }
+
+ def logFailure(logger: => lila.log.Logger, msg: Exception => String): Fu[A] =
+ addFailureEffect { e => logger.warn(msg(e), e) }
+ def logFailure(logger: => lila.log.Logger): Fu[A] = logFailure(logger, _.toString)
+
+ def addEffect(effect: A => Unit) = {
+ fua foreach effect
+ fua
+ }
+
+ def addFailureEffect(effect: Exception => Unit) = {
+ fua onFailure {
+ case e: Exception => effect(e)
+ }
+ fua
+ }
+
+ def addEffects(fail: Exception => Unit, succ: A => Unit): Fu[A] =
+ fua andThen {
+ case scala.util.Failure(e: Exception) => fail(e)
+ case scala.util.Failure(e) => throw e // Throwables
+ case scala.util.Success(e) => succ(e)
+ }
+
+ def mapFailure(f: Exception => Exception) = fua recover {
+ case cause: Exception => throw f(cause)
+ }
+
+ def prefixFailure(p: => String) = mapFailure { e =>
+ common.LilaException(s"$p ${e.getMessage}")
+ }
+
+ def thenPp: Fu[A] = {
+ effectFold(
+ e => println("[failure] " + e),
+ a => println("[success] " + a)
+ )
+ fua
+ }
+
+ def thenPp(msg: String): Fu[A] = {
+ effectFold(
+ e => println(s"[$msg] [failure] $e"),
+ a => println(s"[$msg] [success] $a")
+ )
+ fua
+ }
+
+ def awaitSeconds(seconds: Int): A = {
+ import scala.concurrent.duration._
+ scala.concurrent.Await.result(fua, seconds.seconds)
+ }
+
+ def withTimeout(duration: FiniteDuration, error: => Throwable)(implicit system: akka.actor.ActorSystem): Fu[A] = {
+ Future firstCompletedOf Seq(fua,
+ akka.pattern.after(duration, system.scheduler)(Future failed error))
+ }
+
+ def chronometer = lila.common.Chronometer(fua)
+
+ def mon(path: lila.mon.RecPath) = chronometer.mon(path).result
+ }
+}
diff --git a/modules/common/src/main/PimpedJson.scala b/modules/common/src/main/PimpedJson.scala
index c5663a2f5b..79af9c7a32 100644
--- a/modules/common/src/main/PimpedJson.scala
+++ b/modules/common/src/main/PimpedJson.scala
@@ -2,11 +2,9 @@ package lila.common
import play.api.libs.json._
-object PimpedJson extends PimpedJson
+object PimpedJson {
-trait PimpedJson {
-
- implicit final class LilaPimpedJsObject(js: JsObject) {
+ implicit final class LilaPimpedJsObject(val js: JsObject) extends AnyVal {
def str(key: String): Option[String] =
(js \ key).asOpt[String]
@@ -41,7 +39,7 @@ trait PimpedJson {
}
}
- implicit final class LilaPimpedJsValue(js: JsValue) {
+ implicit final class LilaPimpedJsValue(val js: JsValue) extends AnyVal {
def str(key: String): Option[String] =
js.asOpt[JsObject] flatMap { obj =>
diff --git a/modules/coordinate/src/main/CoordinateApi.scala b/modules/coordinate/src/main/CoordinateApi.scala
index 7730b2b58b..9d0207b7bb 100644
--- a/modules/coordinate/src/main/CoordinateApi.scala
+++ b/modules/coordinate/src/main/CoordinateApi.scala
@@ -1,6 +1,6 @@
package lila.coordinate
-import lila.db.Types.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(
diff --git a/modules/db/src/main/BSON.scala b/modules/db/src/main/BSON.scala
index 4b4f48e5ef..2049689b84 100644
--- a/modules/db/src/main/BSON.scala
+++ b/modules/db/src/main/BSON.scala
@@ -15,21 +15,32 @@ abstract class BSON[T]
def reads(reader: Reader): T
def writes(writer: Writer, obj: T): BSONDocument
- def read(doc: BSONDocument): T = reads(new Reader(doc))
+ def read(doc: BSONDocument): T = try {
+ reads(new Reader(doc))
+ }
+ catch {
+ case e: Exception =>
+ logger.error(s"Can't read malformed doc ${debug(doc)}", e)
+ throw e
+ }
+
def write(obj: T): BSONDocument = writes(writer, obj)
}
-object BSON {
-
- implicit object BSONJodaDateTimeHandler extends BSONHandler[BSONDateTime, DateTime] {
- def read(x: BSONDateTime) = new DateTime(x.value)
- def write(x: DateTime) = BSONDateTime(x.getMillis)
- }
+object BSON extends Handlers {
- def stringAnyValHandler[A](from: A => String, to: String => A) = new BSONHandler[BSONString, A] {
- def read(x: BSONString) = to(x.value)
- def write(x: A) = BSONString(from(x))
- }
+ def LoggingHandler[T](logger: lila.log.Logger)(handler: BSONHandler[BSONDocument, T]) =
+ new BSONHandler[BSONDocument, T] with BSONDocumentReader[T] with BSONDocumentWriter[T] {
+ def read(doc: BSONDocument): T = try {
+ handler read doc
+ }
+ catch {
+ case e: Exception =>
+ logger.error(s"Can't read malformed doc ${debug(doc)}", e)
+ throw e
+ }
+ def write(obj: T): BSONDocument = handler write obj
+ }
object MapDocument {
@@ -88,24 +99,6 @@ object BSON {
}
}
- private def readStream[T](array: BSONArray, reader: BSONReader[BSONValue, T]): Stream[T] = {
- array.stream.filter(_.isSuccess).map { v =>
- reader.read(v.get)
- }
- }
-
- implicit def bsonArrayToListHandler[T](implicit reader: BSONReader[_ <: BSONValue, T], writer: BSONWriter[T, _ <: BSONValue]): BSONHandler[BSONArray, List[T]] = new BSONHandler[BSONArray, List[T]] {
- def read(array: BSONArray) = readStream(array, reader.asInstanceOf[BSONReader[BSONValue, T]]).toList
- def write(repr: List[T]) =
- new BSONArray(repr.map(s => scala.util.Try(writer.write(s))).to[Stream])
- }
-
- implicit def bsonArrayToVectorHandler[T](implicit reader: BSONReader[_ <: BSONValue, T], writer: BSONWriter[T, _ <: BSONValue]): BSONHandler[BSONArray, Vector[T]] = new BSONHandler[BSONArray, Vector[T]] {
- def read(array: BSONArray) = readStream(array, reader.asInstanceOf[BSONReader[BSONValue, T]]).toVector
- def write(repr: Vector[T]) =
- new BSONArray(repr.map(s => scala.util.Try(writer.write(s))).to[Stream])
- }
-
final class Reader(val doc: BSONDocument) {
val map = {
diff --git a/modules/db/src/main/ByteArray.scala b/modules/db/src/main/ByteArray.scala
index d2d5361177..dc5e9b5dc0 100644
--- a/modules/db/src/main/ByteArray.scala
+++ b/modules/db/src/main/ByteArray.scala
@@ -2,13 +2,9 @@ package lila.db
import scala.util.{ Try, Success, Failure }
-import play.api.data.validation.ValidationError
-import play.api.libs.json._
import reactivemongo.bson._
import reactivemongo.bson.utils.Converters
-import lila.common.PimpedJson._
-
case class ByteArray(value: Array[Byte]) {
def isEmpty = value.isEmpty
@@ -36,21 +32,6 @@ object ByteArray {
def write(ba: ByteArray) = BSONBinary(ba.value, subtype)
}
- implicit object JsByteArrayFormat extends OFormat[ByteArray] {
-
- def reads(json: JsValue) = (for {
- hexStr ← json str "$binary"
- bytes ← fromHexStr(hexStr).toOption
- } yield bytes) match {
- case None => JsError(s"error reading ByteArray from $json")
- case Some(ba) => JsSuccess(ba)
- }
-
- def writes(byteArray: ByteArray) = Json.obj(
- "$binary" -> byteArray.toHexStr,
- "$type" -> binarySubType)
- }
-
def parseByte(s: String): Byte = {
var i = s.length - 1
var sum = 0
diff --git a/modules/db/src/main/CollExt.scala b/modules/db/src/main/CollExt.scala
new file mode 100644
index 0000000000..23b42ef51f
--- /dev/null
+++ b/modules/db/src/main/CollExt.scala
@@ -0,0 +1,85 @@
+package lila.db
+
+import reactivemongo.api._
+import reactivemongo.bson._
+
+trait CollExt { self: dsl with QueryBuilderExt =>
+
+ final implicit class ExtendColl(coll: Coll) {
+
+ 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).list[D]()
+
+ def list[D: BSONDocumentReader](selector: BSONDocument, max: Int): Fu[List[D]] =
+ coll.find(selector).list[D](max)
+
+ def byId[D: BSONDocumentReader, I: BSONValueWriter](id: I): Fu[Option[D]] =
+ uno[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]] = uno[D]($id(id))
+
+ def byIds[D: BSONDocumentReader, I: BSONValueWriter](ids: Iterable[I]): Fu[List[D]] =
+ list[D]($inIds(ids))
+
+ def byIds[D: BSONDocumentReader](ids: Iterable[String]): Fu[List[D]] =
+ byIds[D, String](ids)
+
+ def countSel(selector: BSONDocument): Fu[Int] = coll count selector.some
+
+ def exists(selector: BSONDocument): Fu[Boolean] = countSel(selector).map(0!=)
+
+ def byOrderedIds[D: BSONDocumentReader](ids: Iterable[String])(docId: D => String): Fu[List[D]] =
+ byIds[D](ids) map { docs =>
+ val docsMap = docs.map(u => docId(u) -> u).toMap
+ ids.flatMap(docsMap.get).toList
+ }
+ // def byOrderedIds[A <: Identified[String]: TubeInColl](ids: Iterable[String]): Fu[List[A]] =
+ // byOrderedIds[String, A](ids)
+
+ def optionsByOrderedIds[D: BSONDocumentReader](ids: Iterable[String])(docId: D => String): Fu[List[Option[D]]] =
+ byIds[D](ids) map { docs =>
+ val docsMap = docs.map(u => docId(u) -> u).toMap
+ ids.map(docsMap.get).toList
+ }
+
+ def primitive[V: BSONValueReader](selector: BSONDocument, field: String): Fu[List[V]] =
+ coll.find(selector, $doc(field -> true))
+ .list[BSONDocument]()
+ .map {
+ _ flatMap { _.getAs[V](field) }
+ }
+
+ def primitiveOne[V: BSONValueReader](selector: BSONDocument, field: String): Fu[Option[V]] =
+ coll.find(selector, $doc(field -> true))
+ .uno[BSONDocument]
+ .map {
+ _ flatMap { _.getAs[V](field) }
+ }
+
+ def primitiveOne[V: BSONValueReader](selector: BSONDocument, sort: BSONDocument, field: String): Fu[Option[V]] =
+ coll.find(selector, $doc(field -> true))
+ .sort(sort)
+ .uno[BSONDocument]
+ .map {
+ _ flatMap { _.getAs[V](field) }
+ }
+
+ def updateField[V: BSONValueWriter](selector: BSONDocument, field: String, value: V) =
+ coll.update(selector, $set(field -> value))
+
+ def updateFieldUnchecked[V: BSONValueWriter](selector: BSONDocument, field: String, value: V) =
+ coll.uncheckedUpdate(selector, $set(field -> value))
+
+ def fetchUpdate[D: BSONDocumentHandler](selector: BSONDocument)(update: D => BSONDocument): Funit =
+ uno[D](selector) flatMap {
+ _ ?? { doc =>
+ coll.update(selector, update(doc)).void
+ }
+ }
+ }
+}
diff --git a/modules/db/src/main/CursorExt.scala b/modules/db/src/main/CursorExt.scala
new file mode 100644
index 0000000000..53a47c0c1c
--- /dev/null
+++ b/modules/db/src/main/CursorExt.scala
@@ -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)
+ }
+}
diff --git a/modules/db/src/main/Env.scala b/modules/db/src/main/Env.scala
index 4f46e42cef..c0b66b57df 100644
--- a/modules/db/src/main/Env.scala
+++ b/modules/db/src/main/Env.scala
@@ -5,7 +5,7 @@ import reactivemongo.api._
import scala.concurrent.duration._
import scala.concurrent.Future
import scala.util.{ Success, Failure }
-import Types._
+import dsl._
final class Env(
config: Config,
diff --git a/modules/db/src/main/Handlers.scala b/modules/db/src/main/Handlers.scala
new file mode 100644
index 0000000000..f0e25bada2
--- /dev/null
+++ b/modules/db/src/main/Handlers.scala
@@ -0,0 +1,35 @@
+package lila.db
+
+import org.joda.time.DateTime
+import reactivemongo.bson._
+
+trait Handlers {
+
+ implicit object BSONJodaDateTimeHandler extends BSONHandler[BSONDateTime, DateTime] {
+ def read(x: BSONDateTime) = new DateTime(x.value)
+ def write(x: DateTime) = BSONDateTime(x.getMillis)
+ }
+
+ def stringAnyValHandler[A](from: A => String, to: String => A) = new BSONHandler[BSONString, A] {
+ def read(x: BSONString) = to(x.value)
+ def write(x: A) = BSONString(from(x))
+ }
+
+ implicit def bsonArrayToListHandler[T](implicit reader: BSONReader[_ <: BSONValue, T], writer: BSONWriter[T, _ <: BSONValue]): BSONHandler[BSONArray, List[T]] = new BSONHandler[BSONArray, List[T]] {
+ def read(array: BSONArray) = readStream(array, reader.asInstanceOf[BSONReader[BSONValue, T]]).toList
+ def write(repr: List[T]) =
+ new BSONArray(repr.map(s => scala.util.Try(writer.write(s))).to[Stream])
+ }
+
+ implicit def bsonArrayToVectorHandler[T](implicit reader: BSONReader[_ <: BSONValue, T], writer: BSONWriter[T, _ <: BSONValue]): BSONHandler[BSONArray, Vector[T]] = new BSONHandler[BSONArray, Vector[T]] {
+ def read(array: BSONArray) = readStream(array, reader.asInstanceOf[BSONReader[BSONValue, T]]).toVector
+ def write(repr: Vector[T]) =
+ new BSONArray(repr.map(s => scala.util.Try(writer.write(s))).to[Stream])
+ }
+
+ private def readStream[T](array: BSONArray, reader: BSONReader[BSONValue, T]): Stream[T] = {
+ array.stream.filter(_.isSuccess).map { v =>
+ reader.read(v.get)
+ }
+ }
+}
diff --git a/modules/db/src/main/PaginatorAdapter.scala b/modules/db/src/main/PaginatorAdapter.scala
index 4db059ba22..0e9e15ba1c 100644
--- a/modules/db/src/main/PaginatorAdapter.scala
+++ b/modules/db/src/main/PaginatorAdapter.scala
@@ -1,28 +1,13 @@
package lila.db
package paginator
-import api._
-import Implicits._
-import play.api.libs.json._
+import dsl._
import reactivemongo.api.collections.bson.BSONCollection
import reactivemongo.api._
import reactivemongo.bson._
import lila.common.paginator.AdapterLike
-final class Adapter[A: TubeInColl](
- selector: JsObject,
- sort: Sort,
- readPreference: ReadPreference = ReadPreference.primary) extends AdapterLike[A] {
-
- def nbResults: Fu[Int] = $count(selector)
-
- def slice(offset: Int, length: Int): Fu[Seq[A]] = $find(
- pimpQB($query(selector)).sort(sort: _*) skip offset,
- length,
- readPreference = readPreference)
-}
-
final class CachedAdapter[A](
adapter: AdapterLike[A],
val nbResults: Fu[Int]) extends AdapterLike[A] {
@@ -31,7 +16,7 @@ final class CachedAdapter[A](
adapter.slice(offset, length)
}
-final class BSONAdapter[A: BSONDocumentReader](
+final class Adapter[A: BSONDocumentReader](
collection: BSONCollection,
selector: BSONDocument,
projection: BSONDocument,
@@ -43,7 +28,7 @@ final class BSONAdapter[A: BSONDocumentReader](
def slice(offset: Int, length: Int): Fu[Seq[A]] =
collection.find(selector, projection)
.sort(sort)
- .copy(options = QueryOpts(skipN = offset))
+ .skip(offset)
.cursor[A](readPreference = readPreference)
- .collect[List](length)
+ .gather[List](length)
}
diff --git a/modules/db/src/main/QueryBuilderExt.scala b/modules/db/src/main/QueryBuilderExt.scala
new file mode 100644
index 0000000000..487f9462ae
--- /dev/null
+++ b/modules/db/src/main/QueryBuilderExt.scala
@@ -0,0 +1,36 @@
+package lila.db
+
+import scala.collection.generic.CanBuildFrom
+
+import reactivemongo.api._
+import reactivemongo.api.collections.GenericQueryBuilder
+import reactivemongo.bson._
+
+trait QueryBuilderExt { self: dsl =>
+
+ final implicit class ExtendQueryBuilder[A](val b: dsl.QueryBuilder) {
+
+ def skip(nb: Int) = b.options(b.options skip nb)
+
+ def batch(nb: Int) = b.options(b.options batchSize nb)
+
+ // 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)
+ }
+}
diff --git a/modules/db/src/main/Tube.scala b/modules/db/src/main/Tube.scala
deleted file mode 100644
index 71bcd5d967..0000000000
--- a/modules/db/src/main/Tube.scala
+++ /dev/null
@@ -1,139 +0,0 @@
-package lila.db
-
-import scala.util.{ Try, Success, Failure }
-
-import play.api.libs.functional.syntax._
-import play.api.libs.json._
-import reactivemongo.bson._
-import Reads.constraints._
-import Types.Coll
-
-import lila.common.LilaException
-
-trait InColl[A] { implicit def coll: Types.Coll }
-
-trait Tube[Doc] extends BSONDocumentReader[Option[Doc]]
-
-case class BsTube[Doc](handler: BSONHandler[BSONDocument, Doc]) extends Tube[Doc] {
-
- def read(bson: BSONDocument): Option[Doc] = handler readTry bson match {
- case Success(doc) => Some(doc)
- case Failure(err) =>
- logger.error(s"[tube] Cannot read ${lila.db.BSON.debug(bson)}\n$err\n", err)
- None
- }
-
- def write(doc: Doc): BSONDocument = handler write doc
-
- def inColl(c: Coll): BsTubeInColl[Doc] =
- new BsTube[Doc](handler) with InColl[Doc] { def coll = c }
-}
-
-case class JsTube[Doc](
- reader: Reads[Doc],
- writer: Writes[Doc],
- flags: Seq[JsTube.Flag.type => JsTube.Flag] = Seq.empty)
- extends Tube[Doc]
- with Reads[Doc]
- with Writes[Doc] {
-
- import play.modules.reactivemongo.json._
-
- implicit def reads(js: JsValue): JsResult[Doc] = reader reads js
- implicit def writes(doc: Doc): JsValue = writer writes doc
-
- def read(bson: BSONDocument): Option[Doc] = {
- val js = JsObjectReader read bson
- fromMongo(js) match {
- case JsSuccess(v, _) => Some(v)
- case e =>
- logger.error("[tube] Cannot read %s\n%s".format(js, e))
- None
- }
- }
-
- def read(js: JsObject): JsResult[Doc] = reads(js)
-
- def write(doc: Doc): JsResult[JsObject] = writes(doc) match {
- case obj: JsObject => JsSuccess(obj)
- case something =>
- logger.error(s"[tube] Cannot write $doc\ngot $something")
- JsError()
- }
-
- def toMongo(doc: Doc): JsResult[JsObject] = flag(_.NoId)(
- write(doc),
- write(doc) flatMap JsTube.toMongoId
- )
-
- def fromMongo(js: JsObject): JsResult[Doc] = flag(_.NoId)(
- read(js),
- JsTube.depath(JsTube fromMongoId js) flatMap read
- )
-
- def inColl(c: Coll): JsTubeInColl[Doc] =
- new JsTube[Doc](reader, writer, flags) with InColl[Doc] { def coll = c }
-
- private lazy val flagSet = flags.map(_(JsTube.Flag)).toSet
-
- private def flag[A](f: JsTube.Flag.type => JsTube.Flag)(x: => A, y: => A) =
- flagSet contains f(JsTube.Flag) fold (x, y)
-}
-
-object JsTube {
-
- val json = JsTube[JsObject](
- __.read[JsObject],
- __.write[JsObject],
- Seq(_.NoId) // no need to rename the ID field as we are not mapping
- )
-
- private val toMongoIdOp = Helpers.rename('id, '_id)
- def toMongoId(js: JsValue): JsResult[JsObject] = js transform toMongoIdOp
-
- private val fromMongoIdOp = Helpers.rename('_id, 'id)
- def fromMongoId(js: JsValue): JsResult[JsObject] = js transform fromMongoIdOp
-
- sealed trait Flag
- object Flag {
- case object NoId extends Flag
- }
-
- object Helpers {
-
- // Adds Writes[A].andThen combinator, symmetric to Reads[A].andThen
- // Explodes on failure
- implicit final class LilaTubePimpedWrites[A](writes: Writes[A]) {
- def andThen(transformer: Reads[JsObject]): Writes[A] =
- writes.transform(Writes[JsValue] { origin =>
- origin transform transformer match {
- case err: JsError => throw LilaException("[tube] Cannot transform %s\n%s".format(origin, err))
- case JsSuccess(js, _) => js
- }
- })
- }
-
- def rename(from: Symbol, to: Symbol) = __.json update (
- (__ \ to).json copyFrom (__ \ from).json.pick
- ) andThen (__ \ from).json.prune
-
- def readDate(field: Symbol) =
- (__ \ field).json.update(of[JsObject] map { o =>
- (o \ "$date").toOption err s"Can't read date of $o"
- })
-
- def readDateOpt(field: Symbol) = readDate(field) orElse json.reader
-
- def writeDate(field: Symbol) = (__ \ field).json.update(of[JsNumber] map {
- millis => Json.obj("$date" -> millis)
- })
-
- def writeDateOpt(field: Symbol) = (__ \ field).json.update(of[JsNumber] map {
- millis => Json.obj("$date" -> millis)
- }) orElse json.reader
-
- def merge(obj: JsObject) = __.read[JsObject] map (obj ++)
- }
-
- private def depath[A](r: JsResult[A]): JsResult[A] = r.flatMap(JsSuccess(_))
-}
diff --git a/modules/db/src/main/Util.scala b/modules/db/src/main/Util.scala
index f1d8f2a3ad..794c9349f3 100644
--- a/modules/db/src/main/Util.scala
+++ b/modules/db/src/main/Util.scala
@@ -2,14 +2,14 @@ package lila.db
import reactivemongo.bson._
-import Types.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
}
}
diff --git a/modules/db/src/main/api/count.scala b/modules/db/src/main/api/count.scala
deleted file mode 100644
index 3471244c69..0000000000
--- a/modules/db/src/main/api/count.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter
-import reactivemongo.core.commands.Count
-import Types._
-
-object $count {
-
- def apply[A: InColl](q: JsObject): Fu[Int] =
- implicitly[InColl[A]].coll |> { _.count(JsObjectWriter.write(q).some) }
-
- def apply[A: InColl]: Fu[Int] =
- implicitly[InColl[A]].coll |> { _.count(none) }
-
- def exists[A : InColl](q: JsObject): Fu[Boolean] = apply(q) map (0 !=)
-
- def exists[ID: Writes, A: InColl](id: ID): Fu[Boolean] = exists($select(id))
- def exists[A: InColl](id: String): Fu[Boolean] = exists[String, A](id)
-}
diff --git a/modules/db/src/main/api/cursor.scala b/modules/db/src/main/api/cursor.scala
deleted file mode 100644
index 337794992a..0000000000
--- a/modules/db/src/main/api/cursor.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-package lila.db
-package api
-
-import Implicits._
-import play.api.libs.json._
-import reactivemongo.api._
-import reactivemongo.bson._
-
-object $cursor {
-
- def apply[A: TubeInColl](q: JsObject): Cursor[Option[A]] =
- apply($query(q))
-
- def apply[A: TubeInColl](b: QueryBuilder): Cursor[Option[A]] =
- b.cursor[Option[A]]()
-}
diff --git a/modules/db/src/main/api/enumerate.scala b/modules/db/src/main/api/enumerate.scala
deleted file mode 100644
index 50499fe0a8..0000000000
--- a/modules/db/src/main/api/enumerate.scala
+++ /dev/null
@@ -1,40 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.iteratee._
-import play.api.libs.json._
-import play.modules.reactivemongo.json.ImplicitBSONHandlers._
-import reactivemongo.api.Cursor
-import reactivemongo.bson._
-import scalaz.Monoid
-
-import lila.db.Implicits._
-
-object $enumerate {
-
- def apply[A: BSONDocumentReader](query: QueryBuilder, limit: Int = Int.MaxValue)(op: A => Any): Funit =
- query.cursor[A]().enumerate(limit) run {
- Iteratee.foreach((obj: A) => op(obj))
- }
-
- def over[A: TubeInColl](query: QueryBuilder, limit: Int = Int.MaxValue)(op: A => Funit): Funit =
- query.cursor[Option[A]]().enumerate(limit, stopOnError = false) run {
- Iteratee.foldM(()) {
- case (_, Some(obj)) => op(obj)
- case _ => funit
- }
- }
-
- def bulk[A: BSONDocumentReader](query: QueryBuilder, size: Int, limit: Int = Int.MaxValue)(op: List[A] => Funit): Funit =
- query.batch(size).cursor[A]().enumerateBulks(limit) run {
- Iteratee.foldM(()) {
- case (_, objs) => op(objs.toList)
- }
- }
-
- def fold[A: BSONDocumentReader, B](query: QueryBuilder)(zero: B)(f: (B, A) => B): Fu[B] =
- query.cursor[A]().enumerate() |>>> Iteratee.fold(zero)(f)
-
- def foldMonoid[A: BSONDocumentReader, B: Monoid](query: QueryBuilder)(f: A => B): Fu[B] =
- fold[A, B](query)(Monoid[B].zero) { case (b, a) => f(a) |+| b }
-}
diff --git a/modules/db/src/main/api/find.scala b/modules/db/src/main/api/find.scala
deleted file mode 100644
index 02523195a2..0000000000
--- a/modules/db/src/main/api/find.scala
+++ /dev/null
@@ -1,57 +0,0 @@
-package lila.db
-package api
-
-import Implicits._
-import play.api.libs.json._
-import reactivemongo.bson._
-import reactivemongo.api._
-
-object $find {
-
- def one[A: TubeInColl](
- q: JsObject,
- modifier: QueryBuilder => QueryBuilder = identity): Fu[Option[A]] =
- one(modifier($query(q)))
-
- def one[A: TubeInColl](q: QueryBuilder): Fu[Option[A]] =
- q.one[Option[A]] map (_.flatten)
-
- def byId[ID: Writes, A: TubeInColl](id: ID): Fu[Option[A]] = one($select byId id)
- def byId[A: TubeInColl](id: String): Fu[Option[A]] = byId[String, A](id)
-
- def byIds[ID: Writes, A: TubeInColl](ids: Iterable[ID]): Fu[List[A]] = apply($select byIds ids)
- def byIds[A: TubeInColl](ids: Iterable[String]): Fu[List[A]] = byIds[String, A](ids)
-
- def byOrderedIds[ID: Writes, A <: Identified[ID]: TubeInColl](ids: Iterable[ID]): Fu[List[A]] =
- byIds(ids) map { docs =>
- val docsMap = docs.map(u => u.id -> u).toMap
- ids.flatMap(docsMap.get).toList
- }
- def byOrderedIds[A <: Identified[String]: TubeInColl](ids: Iterable[String]): Fu[List[A]] =
- byOrderedIds[String, A](ids)
-
- def optionsByOrderedIds[ID: Writes, A <: Identified[ID]: TubeInColl](ids: Iterable[ID]): Fu[List[Option[A]]] =
- byIds(ids) map { docs =>
- val docsMap = docs.map(u => u.id -> u).toMap
- ids.map(docsMap.get).toList
- }
- def opByOrderedIds[A <: Identified[String]: TubeInColl](ids: Iterable[String]): Fu[List[Option[A]]] =
- optionsByOrderedIds[String, A](ids)
-
- def all[A: TubeInColl]: Fu[List[A]] = apply($select.all)
-
- def apply[A: TubeInColl](q: JsObject): Fu[List[A]] =
- $query(q).toList[Option[A]](none) map (_.flatten)
-
- def apply[A: TubeInColl](q: JsObject, nb: Int): Fu[List[A]] =
- $query(q).toList[Option[A]](nb.some) map (_.flatten)
-
- def apply[A: TubeInColl](b: QueryBuilder): Fu[List[A]] =
- b.toList[Option[A]](none) map (_.flatten)
-
- def apply[A: TubeInColl](b: QueryBuilder, nb: Int): Fu[List[A]] =
- b.toList[Option[A]](nb.some) map (_.flatten)
-
- def apply[A: TubeInColl](b: QueryBuilder, nb: Int, readPreference: ReadPreference): Fu[List[A]] =
- b.toList[Option[A]](nb.some, readPreference) map (_.flatten)
-}
diff --git a/modules/db/src/main/api/insert.scala b/modules/db/src/main/api/insert.scala
deleted file mode 100644
index c6fbb04104..0000000000
--- a/modules/db/src/main/api/insert.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import reactivemongo.bson._
-import Types.Coll
-
-object $insert {
- import play.modules.reactivemongo.json._
-
- def apply[A: JsTubeInColl](doc: A): Funit =
- (implicitly[JsTube[A]] toMongo doc).fold(e => fufail(e.toString), apply(_))
-
- def apply[A: InColl](js: JsObject): Funit =
- implicitly[InColl[A]].coll insert js void
-
- def bson[A: BsTubeInColl](doc: A): Funit = bson {
- implicitly[BsTube[A]].handler write doc
- }
-
- def bson[A: InColl](bs: BSONDocument): Funit =
- implicitly[InColl[A]].coll insert bs void
-}
diff --git a/modules/db/src/main/api/operator.scala b/modules/db/src/main/api/operator.scala
deleted file mode 100644
index 393a01802b..0000000000
--- a/modules/db/src/main/api/operator.scala
+++ /dev/null
@@ -1,51 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import reactivemongo.bson._
-
-object $operator extends $operator
-trait $operator {
- import play.modules.reactivemongo.json._
-
- def $set[A: Writes](pairs: (String, A)*) = Json.obj("$set" -> Json.obj(wrap(pairs): _*))
- def $set(pairs: (String, Json.JsValueWrapper)*) = Json.obj("$set" -> Json.obj(pairs: _*))
- def $set(pairs: JsObject) = Json.obj("$set" -> pairs)
- def $setBson(pairs: (String, BSONValue)*) = BSONDocument("$set" -> BSONDocument(pairs))
- def $setBson(pairs: BSONDocument) = BSONDocument("$set" -> pairs)
- def $unset(fields: String*) = Json.obj("$unset" -> Json.obj(wrap(fields map (_ -> true)): _*))
- def $inc[A: Writes](pairs: (String, A)*) = Json.obj("$inc" -> Json.obj(wrap(pairs): _*))
- def $incBson(pairs: (String, Int)*) = BSONDocument("$inc" -> BSONDocument(pairs map {
- case (k, v) => k -> BSONInteger(v)
- }))
- def $push[A: Writes](field: String, value: A) = Json.obj("$push" -> Json.obj(field -> value))
- def $pushSlice[A: Writes](field: String, value: A, max: Int) = Json.obj("$push" -> Json.obj(
- field -> Json.obj(
- "$each" -> List(value),
- "$slice" -> max
- )
- ))
- def $pull[A: Writes](field: String, value: A) = Json.obj("$pull" -> Json.obj(field -> value))
-
- def $gt[A: Writes](value: A) = Json.obj("$gt" -> value)
- def $gte[A: Writes](value: A) = Json.obj("$gte" -> value)
- def $lt[A: Writes](value: A) = Json.obj("$lt" -> value)
- def $lte[A: Writes](value: A) = Json.obj("$lte" -> value)
- def $ne[A: Writes](value: A) = Json.obj("$ne" -> value)
-
- def $in[A: Writes](values: Iterable[A]) = Json.obj("$in" -> values)
- def $nin[A: Writes](values: Iterable[A]) = Json.obj("$nin" -> values)
- def $all[A: Writes](values: Iterable[A]) = Json.obj("$all" -> values)
- def $exists(bool: Boolean) = Json.obj("$exists" -> bool)
-
- def $or[A: Writes](conditions: Iterable[A]): JsObject = Json.obj("$or" -> conditions)
-
- def $regex(value: String, flags: String = "") = BSONFormats toJSON BSONRegex(value, flags)
-
- import org.joda.time.DateTime
- def $date(value: DateTime) = BSONFormats toJSON BSONDateTime(value.getMillis)
-
- private def wrap[K, V: Writes](pairs: Seq[(K, V)]): Seq[(K, Json.JsValueWrapper)] = pairs map {
- case (k, v) => k -> Json.toJsFieldJsValueWrapper(v)
- }
-}
diff --git a/modules/db/src/main/api/package.scala b/modules/db/src/main/api/package.scala
deleted file mode 100644
index 136ddbca72..0000000000
--- a/modules/db/src/main/api/package.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-package lila.db
-
-package object api extends api.$operator {
- type JSFunction = String
-}
diff --git a/modules/db/src/main/api/primitive.scala b/modules/db/src/main/api/primitive.scala
deleted file mode 100644
index 08a1fd38cd..0000000000
--- a/modules/db/src/main/api/primitive.scala
+++ /dev/null
@@ -1,39 +0,0 @@
-package lila.db
-package api
-
-import Implicits._
-import play.api.libs.json._
-import reactivemongo.bson._
-
-object $primitive {
- import play.modules.reactivemongo.json._
-
- def apply[A: InColl, B](
- query: JsObject,
- field: String,
- modifier: QueryBuilder => QueryBuilder = identity,
- max: Option[Int] = None,
- hint: BSONDocument = BSONDocument())(extract: JsValue => Option[B]): Fu[List[B]] =
- modifier {
- implicitly[InColl[A]].coll
- .genericQueryBuilder
- .query(query)
- .hint(hint)
- .projection(Json.obj(field -> true))
- } toList[BSONDocument] max map2 { (obj: BSONDocument) =>
- extract(JsObjectReader.read(obj) \ field get)
- } map (_.flatten)
-
- def one[A: InColl, B](
- query: JsObject,
- field: String,
- modifier: QueryBuilder => QueryBuilder = identity)(extract: JsValue => Option[B]): Fu[Option[B]] =
- modifier {
- implicitly[InColl[A]].coll
- .genericQueryBuilder
- .query(query)
- .projection(Json.obj(field -> true))
- }.one[BSONDocument] map2 { (obj: BSONDocument) =>
- (JsObjectReader.read(obj) \ field).toOption flatMap extract
- } map (_.flatten)
-}
diff --git a/modules/db/src/main/api/projection.scala b/modules/db/src/main/api/projection.scala
deleted file mode 100644
index b3e6b56412..0000000000
--- a/modules/db/src/main/api/projection.scala
+++ /dev/null
@@ -1,33 +0,0 @@
-package lila.db
-package api
-
-import Implicits._
-import play.api.libs.json._
-import reactivemongo.bson._
-
-object $projection {
- import play.modules.reactivemongo.json._
-
- def apply[A: InColl, B](
- q: JsObject,
- fields: Seq[String],
- modifier: QueryBuilder => QueryBuilder = identity,
- max: Option[Int] = None)(extract: JsObject => Option[B]): Fu[List[B]] =
- modifier {
- implicitly[InColl[A]].coll.genericQueryBuilder query q projection projector(fields)
- } toList[BSONDocument] max map (list => list map { obj =>
- extract(JsObjectReader read obj)
- } flatten)
-
- def one[A: InColl, B](
- q: JsObject,
- fields: Seq[String],
- modifier: QueryBuilder => QueryBuilder = identity)(extract: JsObject => Option[B]): Fu[Option[B]] =
- modifier(implicitly[InColl[A]].coll.genericQueryBuilder query q projection projector(fields)).one[BSONDocument] map (opt => opt map { obj =>
- extract(JsObjectReader read obj)
- } flatten)
-
- private def projector(fields: Seq[String]): JsObject = Json obj {
- (fields map (_ -> Json.toJsFieldJsValueWrapper(1))): _*
- }
-}
diff --git a/modules/db/src/main/api/query.scala b/modules/db/src/main/api/query.scala
deleted file mode 100644
index f9e1cc498a..0000000000
--- a/modules/db/src/main/api/query.scala
+++ /dev/null
@@ -1,22 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import reactivemongo.bson._
-import Types._
-
-object $query {
- import play.modules.reactivemongo.json._
-
- def all[A: InColl] = builder
-
- def apply[A: InColl](q: JsObject) = builder query q
-
- def apply[A: InColl](q: BSONDocument) = builder query q
-
- def byId[A: InColl, B: Writes](id: B) = apply($select byId id)
-
- def byIds[A: InColl, B: Writes](ids: Iterable[B]) = apply($select byIds ids)
-
- def builder[A: InColl] = implicitly[InColl[A]].coll.genericQueryBuilder
-}
diff --git a/modules/db/src/main/api/remove.scala b/modules/db/src/main/api/remove.scala
deleted file mode 100644
index e172dfdc78..0000000000
--- a/modules/db/src/main/api/remove.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import Types._
-
-object $remove {
- import play.modules.reactivemongo.json._
-
- def apply[A: InColl](selector: JsObject): Funit =
- implicitly[InColl[A]].coll remove selector void
-
- def byId[ID: Writes, A: InColl](id: ID): Funit =
- apply($select(id))
- def byId[A: InColl](id: String): Funit = byId[String, A](id)
-
- def byIds[ID: Writes, A: InColl](ids: Seq[ID]): Funit =
- apply($select byIds ids)
- def byIds[A: InColl](ids: Seq[String]): Funit = byIds[String, A](ids)
-
- def apply[ID: Writes, A <: Identified[ID]: TubeInColl](doc: A): Funit =
- byId(doc.id)
- def apply[A <: Identified[String]: TubeInColl](doc: A): Funit = apply[String, A](doc)
-}
diff --git a/modules/db/src/main/api/save.scala b/modules/db/src/main/api/save.scala
deleted file mode 100644
index 703c832152..0000000000
--- a/modules/db/src/main/api/save.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import Types._
-
-object $save {
- import play.modules.reactivemongo.json._
-
- def apply[ID: Writes, A <: Identified[ID]: JsTubeInColl](doc: A): Funit =
- (implicitly[JsTube[A]] toMongo doc).fold(e => fufail(e.toString),
- js => $update($select(doc.id), js, upsert = true)
- )
-
- def apply[A <: Identified[String]: JsTubeInColl](doc: A): Funit = apply[String, A](doc)
-
- def apply[ID: Writes, A: InColl](id: ID, doc: JsObject): Funit =
- $update($select(id), doc + ("_id" -> Json.toJson(id)), upsert = true, multi = false)
-}
diff --git a/modules/db/src/main/api/select.scala b/modules/db/src/main/api/select.scala
deleted file mode 100644
index 4e52324e86..0000000000
--- a/modules/db/src/main/api/select.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-
-object $select {
-
- def all = Json.obj()
-
- def apply[A: Writes](id: A): JsObject = byId(id)
-
- def byId[A: Writes](id: A) = Json.obj("_id" -> id)
-
- def byIds[A: Writes](ids: Iterable[A]) = Json.obj("_id" -> $in(ids))
-}
diff --git a/modules/db/src/main/api/sort.scala b/modules/db/src/main/api/sort.scala
deleted file mode 100644
index 82b2d6c2c0..0000000000
--- a/modules/db/src/main/api/sort.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import reactivemongo.bson._
-
-sealed trait SortOrder
-
-object SortOrder {
- object Ascending extends SortOrder
- object Descending extends SortOrder
-}
-
-object $sort {
- def asc: SortOrder = SortOrder.Ascending
- def desc: SortOrder = SortOrder.Descending
-
- def asc(field: String): (String, SortOrder) = field -> asc
- def desc(field: String): (String, SortOrder) = field -> desc
-
- val ascId = asc("_id")
- val descId = desc("_id")
-
- val naturalAsc = asc("$natural")
- val naturalDesc = desc("$natural")
- val naturalOrder = naturalDesc
-
- val createdAsc = asc("createdAt")
- val createdDesc = desc("createdAt")
- val updatedDesc = desc("updatedAt")
-}
diff --git a/modules/db/src/main/api/update.scala b/modules/db/src/main/api/update.scala
deleted file mode 100644
index 0d40eebf50..0000000000
--- a/modules/db/src/main/api/update.scala
+++ /dev/null
@@ -1,45 +0,0 @@
-package lila.db
-package api
-
-import play.api.libs.json._
-import reactivemongo.bson._
-import Types._
-
-object $update {
- import play.modules.reactivemongo.json._
-
- def apply[ID: Writes, A <: Identified[ID]: JsTubeInColl](doc: A): Funit =
- (implicitly[JsTube[A]] toMongo doc).fold(e => fufail(e.toString),
- js => apply($select(doc.id), js)
- )
- def apply[A <: Identified[String]: JsTubeInColl](doc: A): Funit = apply[String, A](doc)
-
- def apply[A: InColl, B: BSONDocumentWriter](selector: JsObject, update: B, upsert: Boolean = false, multi: Boolean = false): Funit =
- implicitly[InColl[A]].coll.update(selector, update, upsert = upsert, multi = multi).void
-
- def doc[ID: Writes, A <: Identified[ID]: TubeInColl](id: ID)(op: A => JsObject): Funit =
- $find byId id flatten "[db] cannot update missing doc" flatMap { doc =>
- apply($select(id), op(doc))
- }
-
- def docBson[ID: Writes, A <: Identified[ID]: TubeInColl](id: ID)(op: A => BSONDocument): Funit =
- $find byId id flatten "[db] cannot update missing doc" flatMap { doc =>
- apply($select(id), op(doc))
- }
-
- def field[ID: Writes, A: InColl, B: Writes](id: ID, name: String, value: B, upsert: Boolean = false): Funit =
- apply($select(id), $set(name -> value), upsert = upsert)
-
- def bsonField[ID: Writes, A: InColl](id: ID, name: String, value: BSONValue, upsert: Boolean = false): Funit =
- apply($select(id), BSONDocument("$set" -> BSONDocument(name -> value)), upsert = upsert)
-
- // UNCHECKED
-
- def unchecked[A: InColl, B: BSONDocumentWriter](selector: JsObject, update: B, upsert: Boolean = false, multi: Boolean = false) {
- implicitly[InColl[A]].coll.uncheckedUpdate(selector, update, upsert = upsert, multi = multi)
- }
-
- def fieldUnchecked[ID: Writes, A: InColl, B: Writes](id: ID, name: String, value: B, upsert: Boolean = false) {
- unchecked($select(id), $set(name -> value), upsert = upsert)
- }
-}
diff --git a/modules/db/src/main/dsl.scala b/modules/db/src/main/dsl.scala
new file mode 100644
index 0000000000..b5695b9f37
--- /dev/null
+++ b/modules/db/src/main/dsl.scala
@@ -0,0 +1,379 @@
+// Copyright (C) 2014 Fehmi Can Saglam (@fehmicans) and contributors.
+// See the LICENCE.txt file distributed with this work for additional
+// information regarding copyright ownership.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package lila.db
+
+import ornicar.scalalib.Zero
+import reactivemongo.api._
+import reactivemongo.api.collections.GenericQueryBuilder
+import reactivemongo.bson._
+
+trait dsl {
+
+ type Coll = reactivemongo.api.collections.bson.BSONCollection
+ type Bdoc = BSONDocument
+ type Barr = BSONArray
+
+ type QueryBuilder = GenericQueryBuilder[BSONSerializationPack.type]
+
+ type BSONValueReader[A] = BSONReader[_ <: BSONValue, A]
+ type BSONValueWriter[A] = BSONWriter[A, _ <: BSONValue]
+ type BSONValueHandler[A] = BSONHandler[_ <: BSONValue, A]
+
+ type BSONDocumentHandler[A] = BSONDocumentReader[A] with BSONDocumentWriter[A]
+
+ implicit val LilaBSONDocumentZero: Zero[BSONDocument] =
+ Zero.instance($doc())
+
+ implicit def bsonDocumentToPretty(document: BSONDocument): String = {
+ BSONDocument.pretty(document)
+ }
+
+ //**********************************************************************************************//
+ // Helpers
+ def $empty: BSONDocument = BSONDocument.empty
+
+ def $doc(elements: Producer[BSONElement]*): BSONDocument = BSONDocument(elements: _*)
+
+ def $doc(elements: Traversable[BSONElement]): BSONDocument = BSONDocument(elements)
+
+ def $arr(elements: Producer[BSONValue]*): BSONArray = {
+ BSONArray(elements: _*)
+ }
+
+ def $id[T](id: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): BSONDocument = $doc("_id" -> id)
+
+ def $inIds[T](ids: Iterable[T])(implicit writer: BSONWriter[T, _ <: BSONValue]): BSONDocument =
+ $id($doc("$in" -> ids))
+
+ def $boolean(b: Boolean) = BSONBoolean(b)
+ def $string(s: String) = BSONString(s)
+ def $int(i: Int) = BSONInteger(i)
+
+ // End of Helpers
+ //**********************************************************************************************//
+
+ //**********************************************************************************************//
+ // Top Level Logical Operators
+ def $or(expressions: BSONDocument*): BSONDocument = {
+ $doc("$or" -> expressions)
+ }
+
+ def $and(expressions: BSONDocument*): BSONDocument = {
+ $doc("$and" -> expressions)
+ }
+
+ def $nor(expressions: BSONDocument*): BSONDocument = {
+ $doc("$nor" -> expressions)
+ }
+ // End of Top Level Logical Operators
+ //**********************************************************************************************//
+
+ //**********************************************************************************************//
+ // Top Level Evaluation Operators
+ def $text(search: String): BSONDocument = {
+ $doc("$text" -> $doc("$search" -> search))
+ }
+
+ def $text(search: String, language: String): BSONDocument = {
+ $doc("$text" -> $doc("$search" -> search, "$language" -> language))
+ }
+
+ def $where(expression: String): BSONDocument = {
+ $doc("$where" -> expression)
+ }
+ // End of Top Level Evaluation Operators
+ //**********************************************************************************************//
+
+ //**********************************************************************************************//
+ // Top Level Field Update Operators
+ def $inc(item: Producer[BSONElement], items: Producer[BSONElement]*): BSONDocument = {
+ $doc("$inc" -> $doc((Seq(item) ++ items): _*))
+ }
+ def $inc(items: Iterable[BSONElement]): BSONDocument = {
+ $doc("$inc" -> $doc(items))
+ }
+
+ def $mul(item: Producer[BSONElement]): BSONDocument = {
+ $doc("$mul" -> $doc(item))
+ }
+
+ def $rename(item: (String, String), items: (String, String)*)(implicit writer: BSONWriter[String, _ <: BSONValue]): BSONDocument = {
+ $doc("$rename" -> $doc((Seq(item) ++ items).map(Producer.nameValue2Producer[String]): _*))
+ }
+
+ def $setOnInsert(item: Producer[BSONElement], items: Producer[BSONElement]*): BSONDocument = {
+ $doc("$setOnInsert" -> $doc((Seq(item) ++ items): _*))
+ }
+
+ def $set(item: Producer[BSONElement], items: Producer[BSONElement]*): BSONDocument = {
+ $doc("$set" -> $doc((Seq(item) ++ items): _*))
+ }
+
+ def $unset(field: String, fields: String*): BSONDocument = {
+ $doc("$unset" -> $doc((Seq(field) ++ fields).map(_ -> BSONString(""))))
+ }
+
+ def $min(item: Producer[BSONElement]): BSONDocument = {
+ $doc("$min" -> $doc(item))
+ }
+
+ def $max(item: Producer[BSONElement]): BSONDocument = {
+ $doc("$max" -> $doc(item))
+ }
+
+ // Helpers
+ def $eq[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$eq" -> value)
+
+ def $gt[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$gt" -> value)
+
+ /** Matches values that are greater than or equal to the value specified in the query. */
+ def $gte[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$gte" -> value)
+
+ /** Matches any of the values that exist in an array specified in the query.*/
+ def $in[T](values: T*)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$in" -> values)
+
+ /** Matches values that are less than the value specified in the query. */
+ def $lt[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$lt" -> value)
+
+ /** Matches values that are less than or equal to the value specified in the query. */
+ def $lte[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$lte" -> value)
+
+ /** Matches all values that are not equal to the value specified in the query. */
+ def $ne[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$ne" -> value)
+
+ /** Matches values that do not exist in an array specified to the query. */
+ def $nin[T](values: T*)(implicit writer: BSONWriter[T, _ <: BSONValue]) = $doc("$nin" -> values)
+
+ def $exists(value: Boolean) = $doc("$exists" -> value)
+
+ trait CurrentDateValueProducer[T] {
+ def produce: BSONValue
+ }
+
+ implicit class BooleanCurrentDateValueProducer(value: Boolean) extends CurrentDateValueProducer[Boolean] {
+ def produce: BSONValue = BSONBoolean(value)
+ }
+
+ implicit class StringCurrentDateValueProducer(value: String) extends CurrentDateValueProducer[String] {
+ def isValid: Boolean = Seq("date", "timestamp") contains value
+
+ def produce: BSONValue = {
+ if (!isValid)
+ throw new IllegalArgumentException(value)
+
+ $doc("$type" -> value)
+ }
+ }
+
+ def $currentDate(items: (String, CurrentDateValueProducer[_])*): BSONDocument = {
+ $doc("$currentDate" -> $doc(items.map(item => item._1 -> item._2.produce)))
+ }
+ // End of Top Level Field Update Operators
+ //**********************************************************************************************//
+
+ //**********************************************************************************************//
+ // Top Level Array Update Operators
+ def $addToSet(item: Producer[BSONElement], items: Producer[BSONElement]*): BSONDocument = {
+ $doc("$addToSet" -> $doc((Seq(item) ++ items): _*))
+ }
+
+ def $pop(item: (String, Int)): BSONDocument = {
+ if (item._2 != -1 && item._2 != 1)
+ throw new IllegalArgumentException(s"${item._2} is not equal to: -1 | 1")
+
+ $doc("$pop" -> $doc(item))
+ }
+
+ def $push(item: Producer[BSONElement]): BSONDocument = {
+ $doc("$push" -> $doc(item))
+ }
+
+ def $pushEach[T](field: String, values: T*)(implicit writer: BSONWriter[T, _ <: BSONValue]): BSONDocument = {
+ $doc(
+ "$push" -> $doc(
+ field -> $doc(
+ "$each" -> values
+ )
+ )
+ )
+ }
+
+ def $pull(item: Producer[BSONElement]): BSONDocument = {
+ $doc("$pull" -> $doc(item))
+ }
+ // End ofTop Level Array Update Operators
+ //**********************************************************************************************//
+
+ /**
+ * Represents the inital state of the expression which has only the name of the field.
+ * It does not know the value of the expression.
+ */
+ trait ElementBuilder {
+ def field: String
+ def append(value: BSONDocument): BSONDocument = value
+ }
+
+ /** Represents the state of an expression which has a field and a value */
+ trait Expression[V <: BSONValue] extends ElementBuilder {
+ def value: V
+ def toBdoc(implicit writer: BSONWriter[V, _ <: BSONValue]) = toBSONDocument(this)
+ }
+
+ /*
+ * This type of expressions cannot be cascaded. Examples:
+ *
+ * {{{
+ * "price" $eq 10
+ * "price" $ne 1000
+ * "size" $in ("S", "M", "L")
+ * "size" $nin ("S", "XXL")
+ * }}}
+ *
+ */
+ case class SimpleExpression[V <: BSONValue](field: String, value: V)
+ extends Expression[V]
+
+ /**
+ * Expressions of this type can be cascaded. Examples:
+ *
+ * {{{
+ * "age" $gt 50 $lt 60
+ * "age" $gte 50 $lte 60
+ * }}}
+ *
+ */
+ case class CompositeExpression(field: String, value: BSONDocument)
+ extends Expression[BSONDocument]
+ with ComparisonOperators {
+ override def append(value: BSONDocument): BSONDocument = {
+ this.value ++ value
+ }
+ }
+
+ /** MongoDB comparison operators. */
+ trait ComparisonOperators { self: ElementBuilder =>
+
+ def $eq[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): SimpleExpression[BSONValue] = {
+ SimpleExpression(field, writer.write(value))
+ }
+
+ /** Matches values that are greater than the value specified in the query. */
+ def $gt[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): CompositeExpression = {
+ CompositeExpression(field, append($doc("$gt" -> value)))
+ }
+
+ /** Matches values that are greater than or equal to the value specified in the query. */
+ def $gte[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): CompositeExpression = {
+ CompositeExpression(field, append($doc("$gte" -> value)))
+ }
+
+ /** Matches any of the values that exist in an array specified in the query.*/
+ def $in[T](values: T*)(implicit writer: BSONWriter[T, _ <: BSONValue]): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$in" -> values))
+ }
+
+ /** Matches values that are less than the value specified in the query. */
+ def $lt[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): CompositeExpression = {
+ CompositeExpression(field, append($doc("$lt" -> value)))
+ }
+
+ /** Matches values that are less than or equal to the value specified in the query. */
+ def $lte[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): CompositeExpression = {
+ CompositeExpression(field, append($doc("$lte" -> value)))
+ }
+
+ /** Matches all values that are not equal to the value specified in the query. */
+ def $ne[T](value: T)(implicit writer: BSONWriter[T, _ <: BSONValue]): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$ne" -> value))
+ }
+
+ /** Matches values that do not exist in an array specified to the query. */
+ def $nin[T](values: T*)(implicit writer: BSONWriter[T, _ <: BSONValue]): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$nin" -> values))
+ }
+
+ }
+
+ trait LogicalOperators { self: ElementBuilder =>
+ def $not(f: (String => Expression[BSONDocument])): SimpleExpression[BSONDocument] = {
+ val expression = f(field)
+ SimpleExpression(field, $doc("$not" -> expression.value))
+ }
+ }
+
+ trait ElementOperators { self: ElementBuilder =>
+ def $exists(exists: Boolean): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$exists" -> exists))
+ }
+ }
+
+ trait EvaluationOperators { self: ElementBuilder =>
+ def $mod(divisor: Int, remainder: Int): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$mod" -> BSONArray(divisor, remainder)))
+ }
+
+ def $regex(value: String, options: String): SimpleExpression[BSONRegex] = {
+ SimpleExpression(field, BSONRegex(value, options))
+ }
+ }
+
+ trait ArrayOperators { self: ElementBuilder =>
+ def $all[T](values: T*)(implicit writer: BSONWriter[T, _ <: BSONValue]): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$all" -> values))
+ }
+
+ def $elemMatch(query: Producer[BSONElement]*): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$elemMatch" -> $doc(query: _*)))
+ }
+
+ def $size(size: Int): SimpleExpression[BSONDocument] = {
+ SimpleExpression(field, $doc("$size" -> size))
+ }
+ }
+
+ object $sort {
+
+ def asc(field: String) = $doc(field -> 1)
+ def desc(field: String) = $doc(field -> -1)
+
+ val naturalAsc = asc("$natural")
+ val naturalDesc = desc("$natural")
+ val naturalOrder = naturalDesc
+
+ val createdAsc = asc("createdAt")
+ val createdDesc = desc("createdAt")
+ val updatedDesc = desc("updatedAt")
+ }
+
+ implicit class ElementBuilderLike(val field: String)
+ extends ElementBuilder
+ with ComparisonOperators
+ with ElementOperators
+ with EvaluationOperators
+ with LogicalOperators
+ with ArrayOperators
+
+ implicit def toBSONElement[V <: BSONValue](expression: Expression[V])(implicit writer: BSONWriter[V, _ <: BSONValue]): Producer[BSONElement] = {
+ expression.field -> expression.value
+ }
+
+ implicit def toBSONDocument[V <: BSONValue](expression: Expression[V])(implicit writer: BSONWriter[V, _ <: BSONValue]): BSONDocument =
+ $doc(expression.field -> expression.value)
+
+}
+
+object dsl extends dsl with CollExt with QueryBuilderExt with CursorExt with Handlers
diff --git a/modules/db/src/main/package.scala b/modules/db/src/main/package.scala
index 1cc573d7cf..d27d95285a 100644
--- a/modules/db/src/main/package.scala
+++ b/modules/db/src/main/package.scala
@@ -4,9 +4,6 @@ import reactivemongo.api._
import reactivemongo.api.commands.WriteResult
package object db extends PackageObject with WithPlay {
- type TubeInColl[A] = Tube[A] with InColl[A]
- type JsTubeInColl[A] = JsTube[A] with InColl[A]
- type BsTubeInColl[A] = BsTube[A] with InColl[A]
def recoverDuplicateKey[A](f: WriteResult => A): PartialFunction[Throwable, A] = {
case e: WriteResult if e.code.contains(11000) => f(e)
diff --git a/modules/db/src/main/typesAndImplicits.scala b/modules/db/src/main/typesAndImplicits.scala
deleted file mode 100644
index a312f275fc..0000000000
--- a/modules/db/src/main/typesAndImplicits.scala
+++ /dev/null
@@ -1,60 +0,0 @@
-package lila.db
-
-import play.api.libs.json._
-import reactivemongo.api._
-import reactivemongo.api.collections.GenericQueryBuilder
-import reactivemongo.bson._
-import ornicar.scalalib.Zero
-
-object Types extends Types
-object Implicits extends Implicits
-
-trait Types {
- type Coll = reactivemongo.api.collections.bson.BSONCollection
-
- type QueryBuilder = GenericQueryBuilder[BSONSerializationPack.type]
-
- type Identified[ID] = { def id: ID }
-
- type Sort = Seq[(String, api.SortOrder)]
-
- type BSONValueReader[A] = BSONReader[_ <: BSONValue, A]
- type BSONValueHandler[A] = BSONHandler[_ <: BSONValue, A]
-}
-
-trait Implicits extends Types {
-
- implicit val LilaBSONDocumentZero: Zero[BSONDocument] =
- Zero.instance(BSONDocument())
-
- implicit def docId[ID](doc: Identified[ID]): ID = doc.id
-
- def pimpQB(b: QueryBuilder) = new LilaPimpedQueryBuilder(b)
-
- // hack, this should be in reactivemongo
- implicit final class LilaPimpedQueryBuilder(b: QueryBuilder) {
-
- def sort(sorters: (String, api.SortOrder)*): QueryBuilder =
- if (sorters.size == 0) b
- else b sort {
- BSONDocument(
- (for (sorter ← sorters) yield sorter._1 -> BSONInteger(
- sorter._2 match {
- case api.SortOrder.Ascending => 1
- case api.SortOrder.Descending => -1
- })).toStream)
- }
-
- def skip(nb: Int): QueryBuilder = b.options(b.options skip nb)
-
- def batch(nb: Int): QueryBuilder = b.options(b.options batchSize nb)
-
- def toList[A: BSONDocumentReader](limit: Option[Int], readPreference: ReadPreference = ReadPreference.primary): Fu[List[A]] =
- limit.fold(b.cursor[A](readPreference = readPreference).collect[List]()) { l =>
- batch(l).cursor[A](readPreference = readPreference).collect[List](l)
- }
-
- def toListFlatten[A: Tube](limit: Option[Int]): Fu[List[A]] =
- toList[Option[A]](limit) map (_.flatten)
- }
-}
diff --git a/modules/db/src/test/DateTest.scala b/modules/db/src/test/DateTest.scala
deleted file mode 100644
index 4edbd8932a..0000000000
--- a/modules/db/src/test/DateTest.scala
+++ /dev/null
@@ -1,29 +0,0 @@
-package lila.db
-
-import api._
-import Implicits._
-import org.joda.time.DateTime
-import org.specs2.execute.{ Result, AsResult }
-import org.specs2.mutable._
-import org.specs2.specification._
-import play.api.libs.json._
-import play.api.test._
-import reactivemongo.api._
-import reactivemongo.bson._
-
-class DateTest extends Specification {
-
- val date = DateTime.now
- import play.modules.reactivemongo.json.ImplicitBSONHandlers._
-
- "date conversion" should {
- "js to bson" in {
- val doc = JsObjectWriter.write(Json.obj(
- "ca" -> $gt($date(date))
- ))
- doc.getAsTry[BSONDocument]("ca") flatMap { gt =>
- gt.getAsTry[BSONDateTime]("$gt")
- } must_== scala.util.Success(BSONDateTime(date.getMillis))
- }
- }
-}
diff --git a/modules/db/src/test/DbApiTest.scala b/modules/db/src/test/DbApiTest.scala
deleted file mode 100644
index 0e67dbb2b5..0000000000
--- a/modules/db/src/test/DbApiTest.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-package lila.db
-
-import org.joda.time.DateTime
-import org.specs2.mutable._
-import play.api.libs.json._
-import play.api.test._
-import Types._
-
-class DbApiTest extends Specification {
-
- import api._
-
- val date = DateTime.now
-
- "operators" should {
-
- "$set" in {
- $set("foo" -> "bar") must_== Json.obj(
- "$set" -> Json.obj("foo" -> "bar"))
- }
- // "$set DateTime" in new WithApplication {
- // $set("foo" -> date) must_== Json.obj(
- // "$set" -> Json.obj("foo" -> Json.obj("$date" -> date.getMillis)))
- // }
- }
-}
diff --git a/modules/donation/src/main/DonationApi.scala b/modules/donation/src/main/DonationApi.scala
index ff6d7bca28..723d0ddaa6 100644
--- a/modules/donation/src/main/DonationApi.scala
+++ b/modules/donation/src/main/DonationApi.scala
@@ -7,7 +7,7 @@ import scala.concurrent.duration._
import scala.util.Try
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.Coll
+import lila.db.dsl._
final class DonationApi(
coll: Coll,
@@ -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)
diff --git a/modules/explorer/src/main/Env.scala b/modules/explorer/src/main/Env.scala
index b21d3a209c..ff46306d52 100644
--- a/modules/explorer/src/main/Env.scala
+++ b/modules/explorer/src/main/Env.scala
@@ -5,6 +5,7 @@ import com.typesafe.config.Config
final class Env(
config: Config,
+ gameColl: lila.db.dsl.Coll,
system: ActorSystem) {
private val Endpoint = config getString "endpoint"
@@ -12,6 +13,7 @@ final class Env(
private val IndexFlow = config getBoolean "index_flow"
private lazy val indexer = new ExplorerIndexer(
+ gameColl = gameColl,
endpoint = Endpoint,
massImportEndpoint = MassImportEndpoint)
@@ -41,5 +43,6 @@ object Env {
lazy val current = "explorer" boot new Env(
config = lila.common.PlayApp loadConfig "explorer",
+ gameColl = lila.game.Env.current.gameColl,
system = lila.common.PlayApp.system)
}
diff --git a/modules/explorer/src/main/ExplorerIndexer.scala b/modules/explorer/src/main/ExplorerIndexer.scala
index 2727ca3448..07b9c04d90 100644
--- a/modules/explorer/src/main/ExplorerIndexer.scala
+++ b/modules/explorer/src/main/ExplorerIndexer.scala
@@ -10,14 +10,13 @@ import play.api.libs.iteratee._
import play.api.libs.ws.WS
import play.api.Play.current
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.BSONHandlers.gameBSONHandler
-import lila.game.tube.gameTube
import lila.game.{ Game, GameRepo, Query, PgnDump, Player }
import lila.user.UserRepo
private final class ExplorerIndexer(
+ gameColl: Coll,
endpoint: String,
massImportEndpoint: String) {
@@ -40,16 +39,15 @@ private final class ExplorerIndexer(
def apply(sinceStr: String): Funit =
parseDate(sinceStr).fold(fufail[Unit](s"Invalid date $sinceStr")) { since =>
logger.info(s"Start indexing since $since")
- val query = $query(
+ val query =
Query.createdSince(since) ++
Query.rated ++
Query.finished ++
Query.turnsMoreThan(8) ++
Query.noProvisional ++
Query.bothRatingsGreaterThan(1501)
- )
import reactivemongo.api._
- pimpQB(query)
+ gameColl.find($empty)
.sort(Query.sortChronological)
.cursor[Game](ReadPreference.secondaryPreferred)
.enumerate(maxGames, stopOnError = true) &>
diff --git a/modules/fishnet/src/main/Cleaner.scala b/modules/fishnet/src/main/Cleaner.scala
index 6cb7d77176..8da8af3f4c 100644
--- a/modules/fishnet/src/main/Cleaner.scala
+++ b/modules/fishnet/src/main/Cleaner.scala
@@ -5,7 +5,7 @@ import reactivemongo.bson._
import scala.concurrent.duration._
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits._
+import lila.db.dsl._
private final class Cleaner(
repo: FishnetRepo,
@@ -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 =>
diff --git a/modules/fishnet/src/main/FishnetApi.scala b/modules/fishnet/src/main/FishnetApi.scala
index 56c3bff8be..56f99b5358 100644
--- a/modules/fishnet/src/main/FishnetApi.scala
+++ b/modules/fishnet/src/main/FishnetApi.scala
@@ -7,7 +7,7 @@ import scala.concurrent.Future
import scala.util.{ Try, Success, Failure }
import Client.Skill
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.hub.FutureSequencer
final class FishnetApi(
@@ -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
}
diff --git a/modules/fishnet/src/main/FishnetRepo.scala b/modules/fishnet/src/main/FishnetRepo.scala
index 812bbb0d0a..3f6c72e981 100644
--- a/modules/fishnet/src/main/FishnetRepo.scala
+++ b/modules/fishnet/src/main/FishnetRepo.scala
@@ -5,7 +5,7 @@ import reactivemongo.bson._
import scala.concurrent.duration._
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.memo.AsyncCache
private final class FishnetRepo(
@@ -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)
diff --git a/modules/fishnet/src/main/Limiter.scala b/modules/fishnet/src/main/Limiter.scala
index e81a3e3e3a..eb976d2bbe 100644
--- a/modules/fishnet/src/main/Limiter.scala
+++ b/modules/fishnet/src/main/Limiter.scala
@@ -2,7 +2,7 @@ package lila.fishnet
import reactivemongo.bson._
-import lila.db.Implicits.Coll
+import lila.db.dsl.Coll
private final class Limiter(analysisColl: Coll) {
diff --git a/modules/fishnet/src/main/Monitor.scala b/modules/fishnet/src/main/Monitor.scala
index 242000e93a..d1716e4d24 100644
--- a/modules/fishnet/src/main/Monitor.scala
+++ b/modules/fishnet/src/main/Monitor.scala
@@ -4,7 +4,7 @@ import org.joda.time.DateTime
import reactivemongo.bson._
import scala.concurrent.duration._
-import lila.db.Implicits._
+import lila.db.dsl._
private final class Monitor(
moveDb: MoveDB,
diff --git a/modules/forum/src/main/BSONHandlers.scala b/modules/forum/src/main/BSONHandlers.scala
new file mode 100644
index 0000000000..62d2230d57
--- /dev/null
+++ b/modules/forum/src/main/BSONHandlers.scala
@@ -0,0 +1,9 @@
+package lila.forum
+
+private object BSONHandlers {
+
+ import lila.db.BSON.BSONJodaDateTimeHandler
+ implicit val CategBSONHandler = reactivemongo.bson.Macros.handler[Categ]
+ implicit val TopicBSONHandler = reactivemongo.bson.Macros.handler[Topic]
+ implicit val PostBSONHandler = reactivemongo.bson.Macros.handler[Post]
+}
diff --git a/modules/forum/src/main/Categ.scala b/modules/forum/src/main/Categ.scala
index de0890e936..b73faa8547 100644
--- a/modules/forum/src/main/Categ.scala
+++ b/modules/forum/src/main/Categ.scala
@@ -1,7 +1,7 @@
package lila.forum
case class Categ(
- id: String, // slug
+ _id: String, // slug
name: String,
desc: String,
pos: Int,
@@ -13,6 +13,8 @@ case class Categ(
nbPostsTroll: Int,
lastPostIdTroll: String) {
+ def id = _id
+
def nbTopics(troll: Boolean): Int = troll.fold(nbTopicsTroll, nbTopics)
def nbPosts(troll: Boolean): Int = troll.fold(nbPostsTroll, nbPosts)
def lastPostId(troll: Boolean): String = troll.fold(lastPostIdTroll, lastPostId)
@@ -31,19 +33,3 @@ case class Categ(
def slug = id
}
-
-object Categ {
-
- import lila.db.JsTube
- import JsTube.Helpers._
- import play.api.libs.json._
-
- private implicit def topicTube = Topic.tube
-
- private def defaults = Json.obj("team" -> none[String])
-
- private[forum] lazy val tube = JsTube(
- reader = (__.json update merge(defaults)) andThen Json.reads[Categ],
- writer = Json.writes[Categ]
- )
-}
diff --git a/modules/forum/src/main/CategApi.scala b/modules/forum/src/main/CategApi.scala
index b980e01b0c..426a571b21 100644
--- a/modules/forum/src/main/CategApi.scala
+++ b/modules/forum/src/main/CategApi.scala
@@ -1,14 +1,14 @@
package lila.forum
import lila.common.paginator._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
import lila.user.{ User, UserContext }
-import tube._
private[forum] final class CategApi(env: Env) {
+ import BSONHandlers._
+
def list(teams: Set[String], troll: Boolean): Fu[List[CategView]] = for {
categs ← CategRepo withTeams teams
views ← (categs map { categ =>
@@ -27,7 +27,7 @@ private[forum] final class CategApi(env: Env) {
def makeTeam(slug: String, name: String): Funit =
CategRepo.nextPosition flatMap { position =>
val categ = Categ(
- id = teamSlug(slug),
+ _id = teamSlug(slug),
name = name,
desc = "Forum of the team " + name,
pos = position,
@@ -55,10 +55,10 @@ private[forum] final class CategApi(env: Env) {
hidden = topic.hidden,
lang = "en".some,
categId = categ.id)
- $insert(categ) >>
- $insert(post) >>
- $insert(topic withPost post) >>
- $update(categ withTopic post)
+ env.categColl.insert(categ).void >>
+ env.postColl.insert(post).void >>
+ env.topicColl.insert(topic withPost post).void >>
+ env.categColl.update($id(categ.id), categ withTopic post).void
}
def show(slug: String, page: Int, troll: Boolean): Fu[Option[(Categ, Paginator[TopicView])]] =
@@ -75,17 +75,13 @@ private[forum] final class CategApi(env: Env) {
topicIdsTroll = topicsTroll map (_.id)
nbPostsTroll ← PostRepoTroll countByTopics topicIdsTroll
lastPostTroll ← PostRepoTroll lastByTopics topicIdsTroll
- _ ← $update(categ.copy(
+ _ ← env.categColl.update($id(categ.id), categ.copy(
nbTopics = topics.size,
nbPosts = nbPosts,
lastPostId = lastPost ?? (_.id),
nbTopicsTroll = topicsTroll.size,
nbPostsTroll = nbPostsTroll,
lastPostIdTroll = lastPostTroll ?? (_.id)
- ))
+ )).void
} yield ()
-
- def denormalize: Funit = $find.all[Categ] flatMap { categs =>
- categs.map(denormalize).sequenceFu
- } void
}
diff --git a/modules/forum/src/main/CategRepo.scala b/modules/forum/src/main/CategRepo.scala
index 89c8f33c70..e4ef8bce6f 100644
--- a/modules/forum/src/main/CategRepo.scala
+++ b/modules/forum/src/main/CategRepo.scala
@@ -1,27 +1,25 @@
package lila.forum
-import play.api.libs.json.Json
-
-import lila.db.api._
-import lila.db.Implicits._
-import tube.categTube
+import lila.db.dsl._
object CategRepo {
- def bySlug(slug: String) = $find byId slug
+ import BSONHandlers.CategBSONHandler
+
+ // dirty
+ private val coll = Env.current.categColl
+
+ def bySlug(slug: String) = coll.byId[Categ](slug)
def withTeams(teams: Set[String]): Fu[List[Categ]] =
- $find($query($or(Seq(
- Json.obj("team" -> $exists(false)),
- Json.obj("team" -> $in(teams))
- ))) sort $sort.asc("pos"))
+ coll.find($or(
+ "team" $exists false,
+ $doc("team" -> $doc("$in" -> teams))
+ )).sort($sort asc "pos").cursor[Categ]().gather[List]()
- def nextPosition: Fu[Int] = $primitive.one(
- $select.all,
- "pos",
- _ sort $sort.desc("pos")
- )(_.asOpt[Int]) map (~_ + 1)
+ def nextPosition: Fu[Int] =
+ coll.primitiveOne[Int]($empty, $sort desc "pos", "pos") map (~_ + 1)
def nbPosts(id: String): Fu[Int] =
- $primitive.one($select(id), "nbPosts")(_.asOpt[Int]) map (~_)
+ coll.primitiveOne[Int]($id(id), "nbPosts") map (~_)
}
diff --git a/modules/forum/src/main/Env.scala b/modules/forum/src/main/Env.scala
index 11d3125800..9ae2840591 100644
--- a/modules/forum/src/main/Env.scala
+++ b/modules/forum/src/main/Env.scala
@@ -54,14 +54,6 @@ final class Env(
lazy val forms = new DataForm(hub.actor.captcher)
lazy val recent = new Recent(postApi, RecentTtl, RecentNb, PublicCategIds)
- def cli = new lila.common.Cli {
- import tube._
- def process = {
- case "forum" :: "denormalize" :: Nil =>
- topicApi.denormalize >> categApi.denormalize inject "Forum denormalized"
- }
- }
-
system.actorOf(Props(new Actor {
def receive = {
case MakeTeam(id, name) => categApi.makeTeam(id, name)
diff --git a/modules/forum/src/main/Post.scala b/modules/forum/src/main/Post.scala
index 57dba1547f..1a2d6614b8 100644
--- a/modules/forum/src/main/Post.scala
+++ b/modules/forum/src/main/Post.scala
@@ -6,7 +6,7 @@ import ornicar.scalalib.Random
import lila.user.User
case class Post(
- id: String,
+ _id: String,
topicId: String,
categId: String,
author: Option[String],
@@ -19,6 +19,8 @@ case class Post(
lang: Option[String],
createdAt: DateTime) {
+ def id = _id
+
def showAuthor = (author map (_.trim) filter ("" !=)) | User.anonymous
def showUserIdOrAuthor = userId | showAuthor
@@ -43,7 +45,7 @@ object Post {
lang: Option[String],
troll: Boolean,
hidden: Boolean): Post = Post(
- id = Random nextStringUppercase idSize,
+ _id = Random nextStringUppercase idSize,
topicId = topicId,
author = author,
userId = userId,
@@ -55,13 +57,4 @@ object Post {
hidden = hidden,
createdAt = DateTime.now,
categId = categId)
-
- import lila.db.JsTube
- import JsTube.Helpers._
- import play.api.libs.json._
-
- private[forum] lazy val tube = JsTube(
- (__.json update readDate('createdAt)) andThen Json.reads[Post],
- Json.writes[Post] andThen (__.json update writeDate('createdAt))
- )
}
diff --git a/modules/forum/src/main/PostApi.scala b/modules/forum/src/main/PostApi.scala
index 0f05c6fec4..0cb08bb887 100644
--- a/modules/forum/src/main/PostApi.scala
+++ b/modules/forum/src/main/PostApi.scala
@@ -3,17 +3,14 @@ package lila.forum
import actorApi._
import akka.actor.ActorSelection
import org.joda.time.DateTime
-import play.api.libs.json._
import lila.common.paginator._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
import lila.hub.actorApi.timeline.{ Propagate, ForumPost }
import lila.mod.ModlogApi
import lila.security.{ Granter => MasterGranter }
import lila.user.{ User, UserContext }
-import tube._
final class PostApi(
env: Env,
@@ -24,6 +21,8 @@ final class PostApi(
timeline: ActorSelection,
detectLanguage: lila.common.DetectLanguage) {
+ import BSONHandlers._
+
def makePost(
categ: Categ,
topic: Topic,
@@ -44,11 +43,11 @@ final class PostApi(
PostRepo findDuplicate post flatMap {
case Some(dup) => fuccess(dup)
case _ =>
- $insert(post) >>
- $update(topic withPost post) >> {
+ env.postColl.insert(post) >>
+ env.topicColl.update($id(topic.id), topic withPost post) >> {
shouldHideOnPost(topic) ?? TopicRepo.hide(topic.id, true)
} >>
- $update(categ withTopic post) >>-
+ env.categColl.update($id(categ.id), categ withTopic post) >>-
(indexer ! InsertPost(post)) >>
(env.recent.invalidate inject post) >>-
ctx.userId.?? { userId =>
@@ -89,13 +88,13 @@ final class PostApi(
}
def get(postId: String): Fu[Option[(Topic, Post)]] = for {
- post ← optionT($find.byId[Post](postId))
- topic ← optionT($find.byId[Topic](post.topicId))
+ post ← optionT(env.postColl.byId[Post](postId))
+ topic ← optionT(env.topicColl.byId[Topic](post.topicId))
} yield topic -> post
def views(posts: List[Post]): Fu[List[PostView]] = for {
- topics ← $find.byIds[Topic](posts.map(_.topicId).distinct)
- categs ← $find.byIds[Categ](topics.map(_.categId).distinct)
+ topics ← env.topicColl.byIds[Topic](posts.map(_.topicId).distinct)
+ categs ← env.categColl.byIds[Categ](topics.map(_.categId).distinct)
} yield posts map { post =>
for {
topic ← topics find (_.id == post.topicId)
@@ -104,13 +103,13 @@ final class PostApi(
} flatten
def viewsFromIds(postIds: Seq[String]): Fu[List[PostView]] =
- $find.byOrderedIds[Post](postIds) flatMap views
+ env.postColl.byOrderedIds[Post](postIds)(_.id) flatMap views
def view(post: Post): Fu[Option[PostView]] =
views(List(post)) map (_.headOption)
def liteViews(posts: List[Post]): Fu[List[PostLiteView]] = for {
- topics ← $find.byIds[Topic](posts.map(_.topicId).distinct)
+ topics ← env.topicColl.byIds[Topic](posts.map(_.topicId).distinct)
} yield posts flatMap { post =>
topics find (_.id == post.topicId) map { topic =>
PostLiteView(post, topic)
@@ -121,7 +120,7 @@ final class PostApi(
liteViews(List(post)) map (_.headOption)
def miniPosts(posts: List[Post]): Fu[List[MiniForumPost]] = for {
- topics ← $find.byIds[Topic](posts.map(_.topicId).distinct)
+ topics ← env.topicColl.byIds[Topic](posts.map(_.topicId).distinct)
} yield posts flatMap { post =>
topics find (_.id == post.topicId) map { topic =>
MiniForumPost(
@@ -135,15 +134,17 @@ final class PostApi(
}
def lastNumberOf(topic: Topic): Fu[Int] =
- PostRepo lastByTopics List(topic) map { _ ?? (_.number) }
+ PostRepo lastByTopics List(topic.id) map { _ ?? (_.number) }
def lastPageOf(topic: Topic) =
math.ceil(topic.nbPosts / maxPerPage.toFloat).toInt
def paginator(topic: Topic, page: Int, troll: Boolean): Fu[Paginator[Post]] = Paginator(
new Adapter(
- selector = PostRepo(troll) selectTopic topic,
- sort = PostRepo.sortQuery :: Nil),
+ collection = env.postColl,
+ selector = PostRepo(troll) selectTopic topic.id,
+ projection = $empty,
+ sort = PostRepo.sortQuery),
currentPage = page,
maxPerPage = maxPerPage)
@@ -154,17 +155,17 @@ final class PostApi(
first ← PostRepo.isFirstPost(view.topic.id, view.post.id)
_ ← first.fold(
env.topicApi.delete(view.categ, view.topic),
- $remove[Post](view.post) >>
+ env.postColl.remove(view.post) >>
(env.topicApi denormalize view.topic) >>
(env.categApi denormalize view.categ) >>
env.recent.invalidate >>-
- (indexer ! RemovePost(post)))
- _ ← MasterGranter(_.ModerateForum)(mod) ?? modLog.deletePost(mod, post.userId, post.author, post.ip,
+ (indexer ! RemovePost(post.id)))
+ _ ← MasterGranter(_.ModerateForum)(mod) ?? modLog.deletePost(mod.id, post.userId, post.author, post.ip,
text = "%s / %s / %s".format(view.categ.name, view.topic.name, post.text))
} yield true.some)
} yield ()).run.void
- def nbByUser(userId: String) = $count[Post](Json.obj("userId" -> userId))
+ def nbByUser(userId: String) = env.postColl.countSel($doc("userId" -> userId))
def userIds(topic: Topic) = PostRepo userIdsByTopicId topic.id
}
diff --git a/modules/forum/src/main/PostRepo.scala b/modules/forum/src/main/PostRepo.scala
index a5d565e48d..a924ae7a06 100644
--- a/modules/forum/src/main/PostRepo.scala
+++ b/modules/forum/src/main/PostRepo.scala
@@ -1,12 +1,7 @@
package lila.forum
-import play.api.libs.json.Json
-
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import org.joda.time.DateTime
-import reactivemongo.bson.BSONDocument
-import tube.postTube
object PostRepo extends PostRepo(false) {
@@ -17,57 +12,55 @@ object PostRepoTroll extends PostRepo(true)
sealed abstract class PostRepo(troll: Boolean) {
- private lazy val trollFilter = troll.fold(
- Json.obj(),
- Json.obj("troll" -> false)
- )
+ import BSONHandlers.PostBSONHandler
+
+ // dirty
+ private val coll = Env.current.postColl
+
+ private val trollFilter = troll.fold($empty, $doc("troll" -> false))
def byCategAndId(categSlug: String, id: String): Fu[Option[Post]] =
- $find.one(selectCateg(categSlug) ++ $select(id))
+ coll.uno[Post](selectCateg(categSlug) ++ $id(id))
def countBeforeNumber(topicId: String, number: Int): Fu[Int] =
- $count(selectTopic(topicId) ++ Json.obj("number" -> $lt(number)))
+ coll.countSel(selectTopic(topicId) ++ $doc("number" -> $lt(number)))
def isFirstPost(topicId: String, postId: String): Fu[Boolean] =
- $primitive.one(
- selectTopic(topicId),
- "_id",
- _ sort $sort.createdAsc
- )(_.asOpt[String]) map { _.??(postId ==) }
+ coll.primitiveOne[String](selectTopic(topicId), $sort.createdAsc, "_id") map { _ contains postId }
def countByTopics(topics: List[String]): Fu[Int] =
- $count(selectTopics(topics))
+ coll.countSel(selectTopics(topics))
def lastByTopics(topics: List[String]): Fu[Option[Post]] =
- $find.one($query(selectTopics(topics)) sort $sort.createdDesc)
+ coll.find(selectTopics(topics)).sort($sort.createdDesc).uno[Post]
def recentInCategs(nb: Int)(categIds: List[String], langs: List[String]): Fu[List[Post]] =
- $find($query(
+ coll.find(
selectCategs(categIds) ++ selectLangs(langs) ++ selectNotHidden
- ) sort $sort.createdDesc, nb)
+ ).sort($sort.createdDesc).cursor[Post]().gather[List](nb)
- def removeByTopic(topicId: String): Fu[Unit] =
- $remove(selectTopic(topicId))
+ def removeByTopic(topicId: String): Funit =
+ coll.remove(selectTopic(topicId)).void
- def hideByTopic(topicId: String, value: Boolean): Fu[Unit] = $update(
+ def hideByTopic(topicId: String, value: Boolean): Funit = coll.update(
selectTopic(topicId),
- BSONDocument("$set" -> BSONDocument("hidden" -> value)),
- multi = true)
+ $set("hidden" -> value),
+ multi = true).void
- def selectTopic(topicId: String) = Json.obj("topicId" -> topicId) ++ trollFilter
- def selectTopics(topicIds: List[String]) = Json.obj("topicId" -> $in(topicIds)) ++ trollFilter
+ def selectTopic(topicId: String) = $doc("topicId" -> topicId) ++ trollFilter
+ def selectTopics(topicIds: List[String]) = $doc("topicId" $in (topicIds: _*)) ++ trollFilter
- def selectCateg(categId: String) = Json.obj("categId" -> categId) ++ trollFilter
- def selectCategs(categIds: List[String]) = Json.obj("categId" -> $in(categIds)) ++ trollFilter
+ def selectCateg(categId: String) = $doc("categId" -> categId) ++ trollFilter
+ def selectCategs(categIds: List[String]) = $doc("categId" $in (categIds: _*)) ++ trollFilter
- val selectNotHidden = Json.obj("hidden" -> false)
+ val selectNotHidden = $doc("hidden" -> false)
def selectLangs(langs: List[String]) =
- if (langs.isEmpty) Json.obj()
- else Json.obj("lang" -> $in(langs))
+ if (langs.isEmpty) $empty
+ else $doc("lang" $in (langs: _*))
- def findDuplicate(post: Post): Fu[Option[Post]] = $find.one(Json.obj(
- "createdAt" -> $gt($date(DateTime.now.minusHours(1))),
+ def findDuplicate(post: Post): Fu[Option[Post]] = coll.uno[Post]($doc(
+ "createdAt" $gt DateTime.now.minusHours(1),
"userId" -> ~post.userId,
"text" -> post.text
))
@@ -75,8 +68,14 @@ sealed abstract class PostRepo(troll: Boolean) {
def sortQuery = $sort.createdAsc
def userIdsByTopicId(topicId: String): Fu[List[String]] =
- postTube.coll.distinct("userId", BSONDocument("topicId" -> topicId).some) map lila.db.BSON.asStrings
+ coll.distinct("userId", $doc("topicId" -> topicId).some) map lila.db.BSON.asStrings
def idsByTopicId(topicId: String): Fu[List[String]] =
- postTube.coll.distinct("_id", BSONDocument("topicId" -> topicId).some) map lila.db.BSON.asStrings
+ coll.distinct("_id", $doc("topicId" -> topicId).some) map lila.db.BSON.asStrings
+
+ import reactivemongo.api.ReadPreference
+ def cursor(
+ selector: Bdoc,
+ readPreference: ReadPreference = ReadPreference.secondaryPreferred) =
+ coll.find(selector).cursor[Post](readPreference)
}
diff --git a/modules/forum/src/main/Topic.scala b/modules/forum/src/main/Topic.scala
index fcaed0d024..56d04ae0fb 100644
--- a/modules/forum/src/main/Topic.scala
+++ b/modules/forum/src/main/Topic.scala
@@ -4,7 +4,7 @@ import org.joda.time.DateTime
import ornicar.scalalib.Random
case class Topic(
- id: String,
+ _id: String,
categId: String,
slug: String,
name: String,
@@ -20,6 +20,8 @@ case class Topic(
closed: Boolean,
hidden: Boolean) {
+ def id = _id
+
def updatedAt(troll: Boolean): DateTime = troll.fold(updatedAtTroll, updatedAt)
def nbPosts(troll: Boolean): Int = troll.fold(nbPostsTroll, nbPosts)
def nbReplies(troll: Boolean): Int = nbPosts(troll) - 1
@@ -54,7 +56,7 @@ object Topic {
name: String,
troll: Boolean,
featured: Boolean): Topic = Topic(
- id = Random nextString idSize,
+ _id = Random nextString idSize,
categId = categId,
slug = slug,
name = name,
@@ -69,26 +71,4 @@ object Topic {
troll = troll,
closed = false,
hidden = !featured)
-
- import lila.db.JsTube
- import JsTube.Helpers._
- import play.api.libs.json._
-
- private implicit def postTube = Post.tube
-
- private def defaults = Json.obj("closed" -> false)
-
- private[forum] lazy val tube = JsTube(
- (__.json update (
- merge(defaults) andThen
- readDate('createdAt) andThen
- readDate('updatedAt) andThen
- readDate('updatedAtTroll)
- )) andThen Json.reads[Topic],
- Json.writes[Topic] andThen (__.json update (
- writeDate('createdAt) andThen
- writeDate('updatedAt) andThen
- writeDate('updatedAtTroll)
- ))
- )
}
diff --git a/modules/forum/src/main/TopicApi.scala b/modules/forum/src/main/TopicApi.scala
index 67d659bde6..a898c7e046 100644
--- a/modules/forum/src/main/TopicApi.scala
+++ b/modules/forum/src/main/TopicApi.scala
@@ -4,13 +4,11 @@ import actorApi._
import akka.actor.ActorSelection
import lila.common.paginator._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
import lila.hub.actorApi.timeline.{ Propagate, ForumPost }
import lila.security.{ Granter => MasterGranter }
import lila.user.{ User, UserContext }
-import tube._
private[forum] final class TopicApi(
env: Env,
@@ -21,6 +19,8 @@ private[forum] final class TopicApi(
timeline: ActorSelection,
detectLanguage: lila.common.DetectLanguage) {
+ import BSONHandlers._
+
def show(categSlug: String, slug: String, page: Int, troll: Boolean): Fu[Option[(Categ, Topic, Paginator[Post])]] =
for {
data ← (for {
@@ -57,9 +57,9 @@ private[forum] final class TopicApi(
lang = lang map (_.language),
number = 1,
categId = categ.id)
- $insert(post) >>
- $insert(topic withPost post) >>
- $update(categ withTopic post) >>-
+ env.postColl.insert(post) >>
+ env.topicColl.insert(topic withPost post) >>
+ env.categColl.update($id(categ.id), categ withTopic post) >>-
(indexer ! InsertPost(post)) >>
env.recent.invalidate >>-
ctx.userId.?? { userId =>
@@ -79,10 +79,12 @@ private[forum] final class TopicApi(
def paginator(categ: Categ, page: Int, troll: Boolean): Fu[Paginator[TopicView]] = Paginator(
adapter = new Adapter[Topic](
+ collection = env.topicColl,
selector = TopicRepo(troll) byCategQuery categ,
- sort = Seq($sort.updatedDesc)
+ projection = $empty,
+ sort = $sort.updatedDesc
) mapFuture { topic =>
- $find.byId[Post](topic lastPostId troll) map { post =>
+ env.postColl.byId[Post](topic lastPostId troll) map { post =>
TopicView(categ, topic, post, env.postApi lastPageOf topic, troll)
}
},
@@ -91,7 +93,7 @@ private[forum] final class TopicApi(
def delete(categ: Categ, topic: Topic): Funit =
PostRepo.idsByTopicId(topic.id) flatMap { postIds =>
- (PostRepo removeByTopic topic.id zip $remove(topic)) >>
+ (PostRepo removeByTopic topic.id zip env.topicColl.remove($id(topic.id))) >>
(env.categApi denormalize categ) >>-
(indexer ! RemovePosts(postIds)) >>
env.recent.invalidate
@@ -100,33 +102,29 @@ private[forum] final class TopicApi(
def toggleClose(categ: Categ, topic: Topic, mod: User): Funit =
TopicRepo.close(topic.id, topic.open) >> {
MasterGranter(_.ModerateForum)(mod) ??
- modLog.toggleCloseTopic(mod, categ.name, topic.name, topic.open)
+ modLog.toggleCloseTopic(mod.id, categ.name, topic.name, topic.open)
}
def toggleHide(categ: Categ, topic: Topic, mod: User): Funit =
TopicRepo.hide(topic.id, topic.visibleOnHome) >> {
MasterGranter(_.ModerateForum)(mod) ?? {
PostRepo.hideByTopic(topic.id, topic.visibleOnHome) zip
- modLog.toggleHideTopic(mod, categ.name, topic.name, topic.visibleOnHome)
+ modLog.toggleHideTopic(mod.id, categ.name, topic.name, topic.visibleOnHome)
} >> env.recent.invalidate
}
def denormalize(topic: Topic): Funit = for {
- nbPosts ← PostRepo countByTopics List(topic)
- lastPost ← PostRepo lastByTopics List(topic)
- nbPostsTroll ← PostRepoTroll countByTopics List(topic)
- lastPostTroll ← PostRepoTroll lastByTopics List(topic)
- _ ← $update(topic.copy(
+ nbPosts ← PostRepo countByTopics List(topic.id)
+ lastPost ← PostRepo lastByTopics List(topic.id)
+ nbPostsTroll ← PostRepoTroll countByTopics List(topic.id)
+ lastPostTroll ← PostRepoTroll lastByTopics List(topic.id)
+ _ ← env.topicColl.update($id(topic.id), topic.copy(
nbPosts = nbPosts,
lastPostId = lastPost ?? (_.id),
updatedAt = lastPost.fold(topic.updatedAt)(_.createdAt),
nbPostsTroll = nbPostsTroll,
lastPostIdTroll = lastPostTroll ?? (_.id),
updatedAtTroll = lastPostTroll.fold(topic.updatedAtTroll)(_.createdAt)
- ))
+ )).void
} yield ()
-
- def denormalize: Funit = $find.all[Topic] flatMap { topics =>
- topics.map(denormalize).sequenceFu
- } void
}
diff --git a/modules/forum/src/main/TopicRepo.scala b/modules/forum/src/main/TopicRepo.scala
index 550866fe50..1f546eaf43 100644
--- a/modules/forum/src/main/TopicRepo.scala
+++ b/modules/forum/src/main/TopicRepo.scala
@@ -1,11 +1,6 @@
package lila.forum
-import play.api.libs.json.Json
-import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter
-
-import lila.db.api._
-import lila.db.Implicits._
-import tube.topicTube
+import lila.db.dsl._
object TopicRepo extends TopicRepo(false) {
@@ -16,22 +11,24 @@ object TopicRepoTroll extends TopicRepo(true)
sealed abstract class TopicRepo(troll: Boolean) {
- private lazy val trollFilter = troll.fold(
- Json.obj(),
- Json.obj("troll" -> false)
- )
+ import BSONHandlers.TopicBSONHandler
+
+ // dirty
+ private val coll = Env.current.topicColl
+
+ private val trollFilter = troll.fold($empty, $doc("troll" -> false))
def close(id: String, value: Boolean): Funit =
- $update.field(id, "closed", value)
+ coll.updateField($id(id), "closed", value).void
def hide(id: String, value: Boolean): Funit =
- $update.field(id, "hidden", value)
+ coll.updateField($id(id), "hidden", value).void
def byCateg(categ: Categ): Fu[List[Topic]] =
- $find(byCategQuery(categ))
+ coll.list[Topic](byCategQuery(categ))
def byTree(categSlug: String, slug: String): Fu[Option[Topic]] =
- $find.one(Json.obj("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)
@@ -45,7 +42,7 @@ sealed abstract class TopicRepo(troll: Boolean) {
}
def incViews(topic: Topic): Funit =
- $update($select(topic.id), $inc("views" -> 1))
+ coll.update($id(topic.id), $inc("views" -> 1)).void
- def byCategQuery(categ: Categ) = Json.obj("categId" -> categ.slug) ++ trollFilter
+ def byCategQuery(categ: Categ) = $doc("categId" -> categ.slug) ++ trollFilter
}
diff --git a/modules/forum/src/main/package.scala b/modules/forum/src/main/package.scala
index 843f03a884..e7df8ee029 100644
--- a/modules/forum/src/main/package.scala
+++ b/modules/forum/src/main/package.scala
@@ -1,23 +1,6 @@
package lila
-import play.api.libs.json._
-
-import lila.db.JsTube
-import JsTube.Helpers._
-
package object forum extends PackageObject with WithPlay {
- object tube {
-
- private[forum] implicit lazy val categTube =
- Categ.tube inColl Env.current.categColl
-
- private[forum] implicit lazy val topicTube =
- Topic.tube inColl Env.current.topicColl
-
- implicit lazy val postTube =
- Post.tube inColl Env.current.postColl
- }
-
private[forum] def teamSlug(id: String) = "team-" + id
}
diff --git a/modules/forumSearch/src/main/ForumSearchApi.scala b/modules/forumSearch/src/main/ForumSearchApi.scala
index a8e5c89c1f..47571ed57f 100644
--- a/modules/forumSearch/src/main/ForumSearchApi.scala
+++ b/modules/forumSearch/src/main/ForumSearchApi.scala
@@ -1,7 +1,7 @@
package lila.forumSearch
import lila.forum.actorApi._
-import lila.forum.{ Post, PostView, PostLiteView, PostApi }
+import lila.forum.{ Post, PostView, PostLiteView, PostApi, PostRepo }
import lila.search._
import play.api.libs.json._
@@ -36,13 +36,14 @@ final class ForumSearchApi(
def reset = client match {
case c: ESClientHttp => c.putMapping >> {
lila.log("forumSearch").info(s"Index to ${c.index.name}")
- import lila.db.api._
- import lila.forum.tube.postTube
- $enumerate.bulk[Option[Post]]($query[Post](Json.obj()), 500) { postOptions =>
- (postApi liteViews postOptions.flatten) flatMap { views =>
- c.storeBulk(views map (v => Id(v.post.id) -> toDoc(v)))
+ import lila.db.dsl._
+ import play.api.libs.iteratee._
+ PostRepo.cursor($empty).enumerateBulks(Int.MaxValue) |>>>
+ Iteratee.foldM[Iterator[Post], Unit](()) {
+ case (_, posts) => (postApi liteViews posts.toList) flatMap { views =>
+ c.storeBulk(views map (v => Id(v.post.id) -> toDoc(v)))
+ }
}
- }
}
case _ => funit
}
diff --git a/modules/game/src/main/Cached.scala b/modules/game/src/main/Cached.scala
index 039cc32418..0ef62886d0 100644
--- a/modules/game/src/main/Cached.scala
+++ b/modules/game/src/main/Cached.scala
@@ -2,18 +2,18 @@ package lila.game
import scala.concurrent.duration._
-import org.joda.time.DateTime
-import play.api.libs.json.JsObject
import chess.variant.Variant
+import org.joda.time.DateTime
+import reactivemongo.bson.BSONDocument
-import lila.db.api.$count
import lila.db.BSON._
+import lila.db.dsl._
import lila.memo.{ AsyncCache, MongoCache, ExpireSetMemo, Builder }
import lila.user.{ User, UidNb }
-import tube.gameTube
import UidNb.UidNbBSONHandler
final class Cached(
+ coll: Coll,
mongoCache: MongoCache.Builder,
defaultTtl: FiniteDuration) {
@@ -34,13 +34,13 @@ final class Cached(
// (nb: Int) => GameRepo.activePlayersSince(DateTime.now minusDays 1, nb),
// timeToLive = 1 hour)
- private val countShortTtl = AsyncCache[JsObject, Int](
- f = (o: JsObject) => $count(o),
+ private val countShortTtl = AsyncCache[BSONDocument, Int](
+ f = (o: BSONDocument) => coll countSel o,
timeToLive = 5.seconds)
private val count = mongoCache(
prefix = "game:count",
- f = (o: JsObject) => $count(o),
+ f = (o: BSONDocument) => coll countSel o,
timeToLive = defaultTtl)
object Divider {
diff --git a/modules/game/src/main/Captcher.scala b/modules/game/src/main/Captcher.scala
index 363d2a31d1..f4cbaa1c9d 100644
--- a/modules/game/src/main/Captcher.scala
+++ b/modules/game/src/main/Captcher.scala
@@ -10,9 +10,7 @@ import chess.{ Game => ChessGame, Color }
import scalaz.{ NonEmptyList, OptionT }
import lila.common.Captcha, Captcha._
-import lila.db.api.$find
import lila.hub.actorApi.captcha._
-import tube.gameTube
// only works with standard chess (not chess960)
private final class Captcher extends Actor {
@@ -65,7 +63,7 @@ private final class Captcher extends Actor {
GameRepo findRandomStandardCheckmate distribution
private def getFromDb(id: String): Fu[Option[Captcha]] =
- optionT($find byId id) flatMap fromGame
+ optionT(GameRepo game id) flatMap fromGame
private def fromGame(game: Game): OptionT[Fu, Captcha] =
optionT(GameRepo getOptionPgn game.id) flatMap { makeCaptcha(game, _) }
diff --git a/modules/game/src/main/Cli.scala b/modules/game/src/main/Cli.scala
index f7616fb6df..fad2f3c80f 100644
--- a/modules/game/src/main/Cli.scala
+++ b/modules/game/src/main/Cli.scala
@@ -2,13 +2,10 @@ package lila.game
import scala.concurrent.duration._
-import lila.db.api._
+import lila.db.dsl._
import lila.user.UserRepo
-import tube.gameTube
-private[game] final class Cli(
- db: lila.db.Env,
- system: akka.actor.ActorSystem) extends lila.common.Cli {
+private[game] final class Cli(coll: Coll) extends lila.common.Cli {
def process = {
@@ -16,21 +13,5 @@ private[game] final class Cli(
GameRepo nbPerDay {
(days.headOption flatMap parseIntOption) | 30
} map (_ mkString " ")
-
- case "game" :: "typecheck" :: Nil =>
- logger.info("Counting games...")
- val size = $count($select.all).await
- var nb = 0
- val bulkSize = 1000
- ($enumerate.bulk[Option[Game]]($query.all, bulkSize) { gameOptions =>
- val nbGames = gameOptions.flatten.size
- if (nbGames != bulkSize)
- logger.warn("Built %d of %d games".format(nbGames, bulkSize))
- nb = nb + nbGames
- logger.info("Typechecked %d of %d games".format(nb, size))
- funit
- }).await(2.hours)
- fuccess("Done")
}
-
}
diff --git a/modules/game/src/main/CrosstableApi.scala b/modules/game/src/main/CrosstableApi.scala
index 80704deb60..89f60acd4a 100644
--- a/modules/game/src/main/CrosstableApi.scala
+++ b/modules/game/src/main/CrosstableApi.scala
@@ -1,14 +1,13 @@
package lila.game
-import play.api.libs.json.JsObject
-import reactivemongo.bson.{ BSONDocument, BSONInteger }
import reactivemongo.core.commands._
-import lila.common.PimpedJson._
-import lila.db.Types._
+import lila.db.dsl._
import lila.user.UserRepo
-final class CrosstableApi(coll: Coll) {
+final class CrosstableApi(
+ coll: Coll,
+ gameColl: Coll) {
import Crosstable.Result
@@ -20,14 +19,14 @@ final class CrosstableApi(coll: Coll) {
}
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 {
+ $doc("n" -> true)
+ ).uno[Bdoc] map {
~_.flatMap(_.getAs[Int]("n"))
}
@@ -35,22 +34,22 @@ final class CrosstableApi(coll: Coll) {
case List(u1, u2) =>
val result = Result(game.id, game.winnerUserId)
val bsonResult = Crosstable.crosstableBSONHandler.writeResult(result, u1)
- val bson = BSONDocument(
- "$inc" -> BSONDocument(
- Crosstable.BSONFields.nbGames -> BSONInteger(1),
- "s1" -> BSONInteger(game.winnerUserId match {
+ val bson = $doc(
+ "$inc" -> $doc(
+ Crosstable.BSONFields.nbGames -> $int(1),
+ "s1" -> $int(game.winnerUserId match {
case Some(u) if u == u1 => 10
case None => 5
case _ => 0
}),
- "s2" -> BSONInteger(game.winnerUserId match {
+ "s2" -> $int(game.winnerUserId match {
case Some(u) if u == u2 => 10
case None => 5
case _ => 0
})
)
- ) ++ BSONDocument("$push" -> BSONDocument(
- Crosstable.BSONFields.results -> BSONDocument(
+ ) ++ $doc("$push" -> $doc(
+ Crosstable.BSONFields.results -> $doc(
"$each" -> List(bsonResult),
"$slice" -> -maxGames
)))
@@ -64,21 +63,20 @@ final class CrosstableApi(coll: Coll) {
private def create(x1: String, x2: String): Fu[Option[Crosstable]] =
UserRepo.orderByGameCount(x1, x2) map (_ -> List(x1, x2).sorted) flatMap {
case (Some((u1, u2)), List(su1, su2)) =>
- val gameColl = tube.gameTube.coll
- val selector = BSONDocument(
- Game.BSONFields.playerUids -> BSONDocument("$all" -> List(u1, u2)),
- Game.BSONFields.status -> BSONDocument("$gte" -> chess.Status.Mate.id))
+ val selector = $doc(
+ Game.BSONFields.playerUids -> $doc("$all" -> List(u1, u2)),
+ Game.BSONFields.status -> $doc("$gte" -> chess.Status.Mate.id))
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework.{ Match, SumValue, GroupField }
import reactivemongo.api.ReadPreference
for {
localResults <- gameColl.find(selector,
- BSONDocument(Game.BSONFields.winnerId -> true)
- ).sort(BSONDocument(Game.BSONFields.createdAt -> -1))
- .cursor[BSONDocument](readPreference = ReadPreference.secondaryPreferred)
- .collect[List](maxGames).map {
+ $doc(Game.BSONFields.winnerId -> true)
+ ).sort($doc(Game.BSONFields.createdAt -> -1))
+ .cursor[Bdoc](readPreference = ReadPreference.secondaryPreferred)
+ .gather[List](maxGames).map {
_.flatMap { doc =>
doc.getAs[String](Game.BSONFields.id).map { id =>
Result(id, doc.getAs[String](Game.BSONFields.winnerId))
@@ -104,5 +102,5 @@ final class CrosstableApi(coll: Coll) {
}
private def select(u1: String, u2: String) =
- BSONDocument("_id" -> Crosstable.makeKey(u1, u2))
+ $doc("_id" -> Crosstable.makeKey(u1, u2))
}
diff --git a/modules/game/src/main/Env.scala b/modules/game/src/main/Env.scala
index e6ad0b7454..fbca52d97f 100644
--- a/modules/game/src/main/Env.scala
+++ b/modules/game/src/main/Env.scala
@@ -33,17 +33,21 @@ final class Env(
}
import settings._
- private[game] lazy val gameColl = db(CollectionGame)
+ lazy val gameColl = db(CollectionGame)
+
+ lazy val playTime = new PlayTime(gameColl)
lazy val pdfExport = PdfExport(PdfExecPath) _
lazy val pngExport = PngExport(PngExecPath) _
lazy val cached = new Cached(
+ coll = gameColl,
mongoCache = mongoCache,
defaultTtl = CachedNbTtl)
lazy val paginator = new PaginatorBuilder(
+ coll = gameColl,
cached = cached,
maxPerPage = PaginatorMaxPerPage)
@@ -57,7 +61,9 @@ final class Env(
netBaseUrl = netBaseUrl,
getLightUser = getLightUser)
- lazy val crosstableApi = new CrosstableApi(db(CollectionCrosstable))
+ lazy val crosstableApi = new CrosstableApi(
+ coll = db(CollectionCrosstable),
+ gameColl = gameColl)
// load captcher actor
private val captcher = system.actorOf(Props(new Captcher), name = CaptcherName)
@@ -66,7 +72,7 @@ final class Env(
captcher -> actorApi.NewCaptcha
}
- def cli = new Cli(db, system = system)
+ def cli = new Cli(gameColl)
def onStart(gameId: String) = GameRepo game gameId foreach {
_ foreach { game =>
diff --git a/modules/game/src/main/Game.scala b/modules/game/src/main/Game.scala
index 79ff43af92..4047ef7c08 100644
--- a/modules/game/src/main/Game.scala
+++ b/modules/game/src/main/Game.scala
@@ -542,8 +542,6 @@ object Game {
analysed = false),
createdAt = DateTime.now)
- private[game] lazy val tube = lila.db.BsTube(BSONHandlers.gameBSONHandler)
-
object BSONFields {
val id = "_id"
diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala
index 691613fd4e..0865621a6e 100644
--- a/modules/game/src/main/GameRepo.scala
+++ b/modules/game/src/main/GameRepo.scala
@@ -5,41 +5,39 @@ import scala.util.Random
import chess.format.Forsyth
import chess.{ Color, Status }
import org.joda.time.DateTime
-import play.api.libs.json._
-import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter
-import reactivemongo.bson.{ BSONDocument, BSONArray, BSONBinary, BSONInteger }
+import reactivemongo.api.ReadPreference
+import reactivemongo.bson.BSONBinary
-import lila.common.PimpedJson._
-import lila.db.api._
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.db.ByteArray
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.user.{ User, UidNb }
object GameRepo {
- import tube.gameTube
+ // dirty
+ val coll = Env.current.gameColl
type ID = String
import BSONHandlers._
import Game.{ BSONFields => F }
- def game(gameId: ID): Fu[Option[Game]] = $find byId gameId
+ def game(gameId: ID): Fu[Option[Game]] = coll.byId[Game](gameId)
- def games(gameIds: Seq[ID]): Fu[List[Game]] = $find byOrderedIds gameIds
+ def games(gameIds: Seq[ID]): Fu[List[Game]] = coll.byOrderedIds[Game](gameIds)(_.id)
def gameOptions(gameIds: Seq[ID]): Fu[Seq[Option[Game]]] =
- $find optionsByOrderedIds gameIds
+ coll.optionsByOrderedIds[Game](gameIds)(_.id)
def finished(gameId: ID): Fu[Option[Game]] =
- $find.one($select(gameId) ++ Query.finished)
+ coll.uno[Game]($id(gameId) ++ Query.finished)
def player(gameId: ID, color: Color): Fu[Option[Player]] =
- $find byId gameId map2 { (game: Game) => game player color }
+ game(gameId) map2 { (game: Game) => game player color }
def player(gameId: ID, playerId: ID): Fu[Option[Player]] =
- $find byId gameId map { gameOption =>
+ game(gameId) map { gameOption =>
gameOption flatMap { _ player playerId }
}
@@ -47,13 +45,13 @@ object GameRepo {
player(playerRef.gameId, playerRef.playerId)
def pov(gameId: ID, color: Color): Fu[Option[Pov]] =
- $find byId gameId map2 { (game: Game) => Pov(game, game player color) }
+ game(gameId) map2 { (game: Game) => Pov(game, game player color) }
def pov(gameId: ID, color: String): Fu[Option[Pov]] =
Color(color) ?? (pov(gameId, _))
def pov(playerRef: PlayerRef): Fu[Option[Pov]] =
- $find byId playerRef.gameId map { gameOption =>
+ coll.byId[Game](playerRef.gameId) map { gameOption =>
gameOption flatMap { game =>
game player playerRef.playerId map { Pov(game, _) }
}
@@ -63,67 +61,76 @@ object GameRepo {
def pov(ref: PovRef): Fu[Option[Pov]] = pov(ref.gameId, ref.color)
- def remove(id: ID) = $remove($select(id))
-
- def recentByUser(userId: String): Fu[List[Game]] = $find(
- $query(Query user userId) sort Query.sortCreated
- )
+ def remove(id: ID) = coll.remove($id(id)).void
def userPovsByGameIds(gameIds: List[String], user: User): Fu[List[Pov]] =
- $find.byOrderedIds(gameIds) map { _.flatMap(g => Pov(g, user)) }
+ coll.byOrderedIds[Game](gameIds)(_.id) map { _.flatMap(g => Pov(g, user)) }
- def recentPovsByUser(user: User, nb: Int): Fu[List[Pov]] = $find(
- $query(Query user user) sort Query.sortCreated, nb
- ) 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]().gather[List](nb)
+ .map { _.flatMap(g => Pov(g, user)) }
- def gamesForAssessment(userId: String, nb: Int): Fu[List[Game]] = $find(
- $query(Query.finished
+ def gamesForAssessment(userId: String, nb: Int): Fu[List[Game]] = coll.find(
+ Query.finished
++ Query.rated
++ Query.user(userId)
++ Query.analysed(true)
++ Query.turnsMoreThan(20)
- ++ Query.clock(true)) sort ($sort asc F.createdAt), nb
- )
+ ++ Query.clock(true))
+ .sort($sort asc F.createdAt)
+ .cursor[Game]()
+ .gather[List](nb)
+
+ def cursor(
+ selector: Bdoc,
+ readPreference: ReadPreference = ReadPreference.secondaryPreferred) =
+ coll.find(selector).cursor[Game](readPreference)
+
+ def sortedCursor(
+ selector: Bdoc,
+ sort: Bdoc,
+ readPreference: ReadPreference = ReadPreference.secondaryPreferred) =
+ coll.find(selector).sort(sort).cursor[Game](readPreference)
def unrate(gameId: String) =
- $update($select(gameId), BSONDocument("$unset" -> BSONDocument(
+ coll.update($id(gameId), $doc("$unset" -> $doc(
F.rated -> true,
s"${F.whitePlayer}.${Player.BSONFields.ratingDiff}" -> true,
s"${F.blackPlayer}.${Player.BSONFields.ratingDiff}" -> true
)))
def goBerserk(pov: Pov): Funit =
- $update($select(pov.gameId), BSONDocument("$set" -> BSONDocument(
+ coll.update($id(pov.gameId), $set(
s"${pov.color.fold(F.whitePlayer, F.blackPlayer)}.${Player.BSONFields.berserk}" -> true
- )))
+ )).void
def save(progress: Progress): Funit =
GameDiff(progress.origin, progress.game) match {
case (Nil, Nil) => funit
- case (sets, unsets) => gameTube.coll.update(
- $select(progress.origin.id),
- nonEmptyMod("$set", BSONDocument(sets)) ++ nonEmptyMod("$unset", BSONDocument(unsets))
+ case (sets, unsets) => coll.update(
+ $id(progress.origin.id),
+ nonEmptyMod("$set", $doc(sets)) ++ nonEmptyMod("$unset", $doc(unsets))
).void
}
- private def nonEmptyMod(mod: String, doc: BSONDocument) =
- if (doc.isEmpty) BSONDocument() else BSONDocument(mod -> doc)
+ private def nonEmptyMod(mod: String, doc: Bdoc) =
+ if (doc.isEmpty) $empty else $doc(mod -> doc)
def setRatingDiffs(id: ID, white: Int, black: Int) =
- $update($select(id), BSONDocument("$set" -> BSONDocument(
- s"${F.whitePlayer}.${Player.BSONFields.ratingDiff}" -> BSONInteger(white),
- s"${F.blackPlayer}.${Player.BSONFields.ratingDiff}" -> BSONInteger(black))))
+ coll.update($id(id), $set(
+ s"${F.whitePlayer}.${Player.BSONFields.ratingDiff}" -> white,
+ s"${F.blackPlayer}.${Player.BSONFields.ratingDiff}" -> black))
// used by RatingFest
def setRatingAndDiffs(id: ID, white: (Int, Int), black: (Int, Int)) =
- $update($select(id), BSONDocument("$set" -> BSONDocument(
- s"${F.whitePlayer}.${Player.BSONFields.rating}" -> BSONInteger(white._1),
- s"${F.blackPlayer}.${Player.BSONFields.rating}" -> BSONInteger(black._1),
- s"${F.whitePlayer}.${Player.BSONFields.ratingDiff}" -> BSONInteger(white._2),
- s"${F.blackPlayer}.${Player.BSONFields.ratingDiff}" -> BSONInteger(black._2))))
+ coll.update($id(id), $set(
+ s"${F.whitePlayer}.${Player.BSONFields.rating}" -> white._1,
+ s"${F.blackPlayer}.${Player.BSONFields.rating}" -> black._1,
+ s"${F.whitePlayer}.${Player.BSONFields.ratingDiff}" -> white._2,
+ s"${F.blackPlayer}.${Player.BSONFields.ratingDiff}" -> black._2))
def urgentGames(user: User): Fu[List[Pov]] =
- $find(Query nowPlaying user.id, 100) map { games =>
+ coll.list[Game](Query nowPlaying user.id, 100) map { games =>
val povs = games flatMap { Pov(_, user) }
try {
povs sortWith Pov.priority
@@ -139,61 +146,64 @@ object GameRepo {
// gets last recently played move game in progress
def lastPlayedPlaying(user: User): Fu[Option[Pov]] =
- $find.one($query(Query recentlyPlaying user.id) sort Query.sortUpdatedNoIndex) map {
- _ flatMap { Pov(_, user) }
- }
+ coll.find(Query recentlyPlaying user.id)
+ .sort(Query.sortUpdatedNoIndex)
+ .uno[Game]
+ .map { _ flatMap { Pov(_, user) } }
def lastPlayed(user: User): Fu[Option[Pov]] =
- $find($query(Query user user.id) sort ($sort desc F.createdAt), 20) map {
- _.sortBy(_.updatedAt).lastOption flatMap { Pov(_, user) }
- }
+ coll.find(Query user user.id)
+ .sort($sort desc F.createdAt)
+ .cursor[Game]()
+ .gather[List](20).map {
+ _.sortBy(_.updatedAt).lastOption flatMap { Pov(_, user) }
+ }
- def lastFinishedRatedNotFromPosition(user: User): Fu[Option[Game]] = $find.one {
- $query {
- Query.user(user.id) ++
- Query.rated ++
- Query.finished ++
- Query.turnsMoreThan(2) ++
- Query.notFromPosition
- } sort Query.sortAntiChronological
- }
+ def lastFinishedRatedNotFromPosition(user: User): Fu[Option[Game]] = coll.find(
+ Query.user(user.id) ++
+ Query.rated ++
+ Query.finished ++
+ Query.turnsMoreThan(2) ++
+ Query.notFromPosition
+ ).sort(Query.sortAntiChronological).uno[Game]
def setTv(id: ID) {
- $update.fieldUnchecked(id, F.tvAt, $date(DateTime.now))
+ coll.updateFieldUnchecked($id(id), F.tvAt, DateTime.now)
}
- def onTv(nb: Int): Fu[List[Game]] = $find($query(Json.obj(F.tvAt -> $exists(true))) sort $sort.desc(F.tvAt), nb)
+ def onTv(nb: Int): Fu[List[Game]] = coll.find($doc(F.tvAt $exists true))
+ .sort($sort desc F.tvAt)
+ .cursor[Game]()
+ .gather[List](nb)
def setAnalysed(id: ID) {
- $update.fieldUnchecked(id, F.analysed, true)
+ coll.updateFieldUnchecked($id(id), F.analysed, true)
}
def setUnanalysed(id: ID) {
- $update.fieldUnchecked(id, F.analysed, false)
+ coll.updateFieldUnchecked($id(id), F.analysed, false)
}
def isAnalysed(id: ID): Fu[Boolean] =
- $count.exists($select(id) ++ Query.analysed(true))
+ coll.exists($id(id) ++ Query.analysed(true))
def filterAnalysed(ids: Seq[String]): Fu[Set[String]] =
- gameTube.coll.distinct("_id", BSONDocument(
- "_id" -> BSONDocument("$in" -> ids),
+ coll.distinct("_id", $doc(
+ "_id" -> $doc("$in" -> ids),
F.analysed -> true
).some) map lila.db.BSON.asStringSet
- def exists(id: String) = gameTube.coll.count(BSONDocument("_id" -> id).some).map(0<)
+ def exists(id: String) = coll.exists($doc("_id" -> id))
def incBookmarks(id: ID, value: Int) =
- $update($select(id), $incBson(F.bookmarks -> value))
+ coll.update($id(id), $inc(F.bookmarks -> value)).void
def setHoldAlert(pov: Pov, mean: Int, sd: Int, ply: Option[Int] = None) = {
import Player.holdAlertBSONHandler
- $update(
- $select(pov.gameId),
- BSONDocument(
- "$set" -> BSONDocument(
- s"p${pov.color.fold(0, 1)}.${Player.BSONFields.holdAlert}" ->
- Player.HoldAlert(ply = ply | pov.game.turns, mean = mean, sd = sd)
- )
+ coll.update(
+ $id(pov.gameId),
+ $set(
+ s"p${pov.color.fold(0, 1)}.${Player.BSONFields.holdAlert}" ->
+ Player.HoldAlert(ply = ply | pov.game.turns, mean = mean, sd = sd)
)
).void
}
@@ -204,7 +214,7 @@ object GameRepo {
winnerColor: Option[Color],
winnerId: Option[String],
status: Status) = {
- val partialUnsets = BSONDocument(
+ val partialUnsets = $doc(
F.positionHashes -> true,
F.playingUids -> true,
("p0." + Player.BSONFields.lastDrawOffer) -> true,
@@ -216,21 +226,22 @@ object GameRepo {
// keep the checkAt field when game is aborted,
// so it gets deleted in 24h
val unsets =
- if (status >= Status.Mate) partialUnsets ++ BSONDocument(F.checkAt -> true)
+ if (status >= Status.Mate) partialUnsets ++ $doc(F.checkAt -> true)
else partialUnsets
- $update(
- $select(id),
- nonEmptyMod("$set", BSONDocument(
+ coll.update(
+ $id(id),
+ nonEmptyMod("$set", $doc(
F.winnerId -> winnerId,
F.winnerColor -> winnerColor.map(_.white)
- )) ++ BSONDocument("$unset" -> unsets)
+ )) ++ $doc("$unset" -> unsets)
)
}
- def findRandomStandardCheckmate(distribution: Int): Fu[Option[Game]] = $find.one(
- Query.mate ++ Json.obj("v" -> $exists(false)),
- _ sort Query.sortCreated skip (Random nextInt distribution)
- )
+ def findRandomStandardCheckmate(distribution: Int): Fu[Option[Game]] = coll.find(
+ Query.mate ++ $doc("v" $exists false)
+ ).sort(Query.sortCreated)
+ .skip(Random nextInt distribution)
+ .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)
@@ -242,12 +253,12 @@ object GameRepo {
.option(Forsyth >> g2.toChess)
.filter(Forsyth.initial !=)
}
- val bson = (gameTube.handler write g2) ++ BSONDocument(
+ val bson = (gameBSONHandler write g2) ++ $doc(
F.initialFen -> fen,
F.checkAt -> (!g2.isPgnImport).option(DateTime.now plusHours g2.hasClock.fold(1, 10 * 24)),
F.playingUids -> (g2.started && userIds.nonEmpty).option(userIds)
)
- $insert bson bson
+ coll insert bson void
} >>- {
lila.mon.game.create.variant(g.variant.key)()
lila.mon.game.create.source(g.source.fold("unknown")(_.name))()
@@ -256,32 +267,32 @@ object GameRepo {
}
def removeRecentChallengesOf(userId: String) =
- $remove(Query.created ++ Query.friend ++ Query.user(userId) ++
+ coll.remove(Query.created ++ Query.friend ++ Query.user(userId) ++
Query.createdSince(DateTime.now minusHours 1))
def setCheckAt(g: Game, at: DateTime) =
- $update($select(g.id), BSONDocument("$set" -> BSONDocument(F.checkAt -> at)))
+ coll.update($id(g.id), $doc("$set" -> $doc(F.checkAt -> at)))
def unsetCheckAt(g: Game) =
- $update($select(g.id), BSONDocument("$unset" -> BSONDocument(F.checkAt -> true)))
+ coll.update($id(g.id), $doc("$unset" -> $doc(F.checkAt -> true)))
def unsetPlayingUids(g: Game): Unit =
- $update.unchecked($select(g.id), BSONDocument("$unset" -> BSONDocument(F.playingUids -> true)))
+ coll.uncheckedUpdate($id(g.id), $unset(F.playingUids))
// used to make a compound sparse index
def setImportCreatedAt(g: Game) =
- $update($select(g.id), BSONDocument(
- "$set" -> BSONDocument("pgni.ca" -> g.createdAt)
- ))
+ coll.update($id(g.id), $set("pgni.ca" -> g.createdAt)).void
- def saveNext(game: Game, nextId: ID): Funit = $update(
- $select(game.id),
+ def saveNext(game: Game, nextId: ID): Funit = coll.update(
+ $id(game.id),
$set(F.next -> nextId) ++
- $unset("p0." + Player.BSONFields.isOfferingRematch, "p1." + Player.BSONFields.isOfferingRematch)
- )
+ $unset(
+ "p0." + Player.BSONFields.isOfferingRematch,
+ "p1." + Player.BSONFields.isOfferingRematch)
+ ).void
def initialFen(gameId: ID): Fu[Option[String]] =
- $primitive.one($select(gameId), F.initialFen)(_.asOpt[String])
+ coll.primitiveOne[String]($id(gameId), F.initialFen)
def initialFen(game: Game): Fu[Option[String]] =
if (game.imported || !game.variant.standardInitialPosition) initialFen(game.id) map {
@@ -290,38 +301,38 @@ object GameRepo {
}
else fuccess(none)
- def featuredCandidates: Fu[List[Game]] = $find(
- Query.playable ++ Query.clock(true) ++ Json.obj(
- F.createdAt -> $gt($date(DateTime.now minusMinutes 5)),
- F.updatedAt -> $gt($date(DateTime.now minusSeconds 40))
- ) ++ $or(Seq(
- Json.obj(s"${F.whitePlayer}.${Player.BSONFields.rating}" -> $gt(1200)),
- Json.obj(s"${F.blackPlayer}.${Player.BSONFields.rating}" -> $gt(1200))
- ))
+ def featuredCandidates: Fu[List[Game]] = coll.list[Game](
+ Query.playable ++ Query.clock(true) ++ $doc(
+ F.createdAt $gt (DateTime.now minusMinutes 5),
+ F.updatedAt $gt (DateTime.now minusSeconds 40)
+ ) ++ $or(
+ s"${F.whitePlayer}.${Player.BSONFields.rating}" $gt 1200,
+ s"${F.blackPlayer}.${Player.BSONFields.rating}" $gt 1200
+ )
)
- def count(query: Query.type => JsObject): Fu[Int] = $count(query(Query))
+ def count(query: Query.type => Bdoc): Fu[Int] = coll countSel query(Query)
def nbPerDay(days: Int): Fu[List[Int]] =
((days to 1 by -1).toList map { day =>
val from = DateTime.now.withTimeAtStartOfDay minusDays day
val to = from plusDays 1
- $count(Json.obj(F.createdAt -> ($gte($date(from)) ++ $lt($date(to)))))
+ coll.countSel($doc(F.createdAt -> ($gte(from) ++ $lt(to))))
}).sequenceFu
// #TODO expensive stuff, run on DB replica
// Can't be done on reactivemongo 0.11.9 :(
def bestOpponents(userId: String, limit: Int): Fu[List[(String, Int)]] = {
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
- gameTube.coll.aggregate(Match(BSONDocument(F.playerUids -> userId)), List(
- Match(BSONDocument(F.playerUids -> BSONDocument("$size" -> 2))),
+ coll.aggregate(Match($doc(F.playerUids -> userId)), List(
+ Match($doc(F.playerUids -> $doc("$size" -> 2))),
Sort(Descending(F.createdAt)),
Limit(1000), // only look in the last 1000 games
- Project(BSONDocument(
+ Project($doc(
F.playerUids -> true,
F.id -> false)),
Unwind(F.playerUids),
- Match(BSONDocument(F.playerUids -> BSONDocument("$ne" -> userId))),
+ Match($doc(F.playerUids -> $doc("$ne" -> userId))),
GroupField(F.playerUids)("gs" -> SumValue(1)),
Sort(Descending("gs")),
Limit(limit))).map(_.documents.flatMap { obj =>
@@ -331,31 +342,27 @@ object GameRepo {
})
}
- def random: Fu[Option[Game]] = $find.one(
- $query.all sort Query.sortCreated skip (Random nextInt 1000))
-
- def random(nb: Int): Fu[List[Game]] = $find(
- $query.all sort Query.sortCreated skip (Random nextInt 1000), nb)
-
- def findMirror(game: Game): Fu[Option[Game]] = $find.one($query(
- BSONDocument(
- F.id -> BSONDocument("$ne" -> game.id),
- F.playerUids -> BSONDocument("$in" -> game.userIds),
- F.status -> Status.Started.id,
- F.createdAt -> BSONDocument("$gt" -> (DateTime.now minusMinutes 15)),
- F.updatedAt -> BSONDocument("$gt" -> (DateTime.now minusMinutes 5)),
- "$or" -> BSONArray(
- BSONDocument(s"${F.whitePlayer}.ai" -> BSONDocument("$exists" -> true)),
- BSONDocument(s"${F.blackPlayer}.ai" -> BSONDocument("$exists" -> true))
- ),
- F.binaryPieces -> game.binaryPieces
- )
+ def random: Fu[Option[Game]] = coll.find($empty)
+ .sort(Query.sortCreated)
+ .skip(Random nextInt 1000)
+ .uno[Game]
+
+ 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,
+ F.createdAt -> $doc("$gt" -> (DateTime.now minusMinutes 15)),
+ F.updatedAt -> $doc("$gt" -> (DateTime.now minusMinutes 5)),
+ "$or" -> $arr(
+ $doc(s"${F.whitePlayer}.ai" -> $doc("$exists" -> true)),
+ $doc(s"${F.blackPlayer}.ai" -> $doc("$exists" -> true))
+ ),
+ F.binaryPieces -> game.binaryPieces
))
- def findPgnImport(pgn: String): Fu[Option[Game]] =
- gameTube.coll.find(
- BSONDocument(s"${F.pgnImport}.h" -> PgnImport.hash(pgn))
- ).one[Game]
+ def findPgnImport(pgn: String): Fu[Option[Game]] = coll.uno[Game](
+ $doc(s"${F.pgnImport}.h" -> PgnImport.hash(pgn))
+ )
def getPgn(id: ID): Fu[PgnMoves] = getOptionPgn(id) map (~_)
@@ -363,27 +370,26 @@ object GameRepo {
getOptionPgn(id) map (_ filter (_.nonEmpty))
def getOptionPgn(id: ID): Fu[Option[PgnMoves]] =
- gameTube.coll.find(
- $select(id), Json.obj(
+ coll.find(
+ $id(id), $doc(
F.id -> false,
F.binaryPgn -> true
)
- ).one[BSONDocument] map { _ flatMap extractPgnMoves }
+ ).uno[Bdoc] map { _ flatMap extractPgnMoves }
- def lastGameBetween(u1: String, u2: String, since: DateTime): Fu[Option[Game]] = {
- $find.one(Json.obj(
- F.playerUids -> Json.obj("$all" -> List(u1, u2)),
- F.createdAt -> Json.obj("$gt" -> $date(since))
+ def lastGameBetween(u1: String, u2: String, since: DateTime): Fu[Option[Game]] =
+ coll.uno[Game]($doc(
+ F.playerUids.$all(u1, u2),
+ F.createdAt $gt since
))
- }
def getUserIds(id: ID): Fu[List[String]] =
- gameTube.coll.find(
- $select(id), BSONDocument(
+ coll.find(
+ $id(id), $doc(
F.id -> false,
F.playerUids -> true
)
- ).one[BSONDocument] 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]] = {
@@ -397,13 +403,13 @@ object GameRepo {
Unwind
}
- gameTube.coll.aggregate(Match(BSONDocument(
- F.createdAt -> BSONDocument("$gt" -> since),
- F.status -> BSONDocument("$gte" -> chess.Status.Mate.id),
- s"${F.playerUids}.0" -> BSONDocument("$exists" -> true)
+ coll.aggregate(Match($doc(
+ F.createdAt $gt since,
+ F.status $gte chess.Status.Mate.id,
+ s"${F.playerUids}.0" $exists true
)), List(Unwind(F.playerUids),
- Match(BSONDocument(
- F.playerUids -> BSONDocument("$ne" -> "")
+ Match($doc(
+ F.playerUids -> $doc("$ne" -> "")
)),
GroupField(F.playerUids)("nb" -> SumValue(1)),
Sort(Descending("nb")),
@@ -414,7 +420,7 @@ object GameRepo {
})
}
- private def extractPgnMoves(doc: BSONDocument) =
+ private def extractPgnMoves(doc: Bdoc) =
doc.getAs[BSONBinary](F.binaryPgn) map { bin =>
BinaryFormat.pgn read { ByteArray.ByteArrayBSONHandler read bin }
}
diff --git a/modules/game/src/main/PaginatorBuilder.scala b/modules/game/src/main/PaginatorBuilder.scala
index f8dbebaa35..386ee24098 100644
--- a/modules/game/src/main/PaginatorBuilder.scala
+++ b/modules/game/src/main/PaginatorBuilder.scala
@@ -1,21 +1,23 @@
package lila.game
-import play.api.libs.json._
-
import chess.Status
import lila.common.paginator._
+import lila.db.dsl._
import lila.db.paginator._
-import lila.db.Types.Sort
-import tube.gameTube
-private[game] final class PaginatorBuilder(cached: Cached, maxPerPage: Int) {
+private[game] final class PaginatorBuilder(
+ coll: Coll,
+ cached: Cached,
+ maxPerPage: Int) {
private val readPreference = reactivemongo.api.ReadPreference.secondaryPreferred
- def recentlyCreated(selector: JsObject, nb: Option[Int] = None) =
- apply(selector, Seq(Query.sortCreated), nb) _
+ import BSONHandlers.gameBSONHandler
+
+ def recentlyCreated(selector: Bdoc, nb: Option[Int] = None) =
+ apply(selector, Query.sortCreated, nb) _
- def apply(selector: JsObject, sort: Sort, nb: Option[Int] = None)(page: Int): Fu[Paginator[Game]] =
+ def apply(selector: Bdoc, sort: Bdoc, nb: Option[Int] = None)(page: Int): Fu[Paginator[Game]] =
apply(nb.fold(noCacheAdapter(selector, sort)) { cached =>
cacheAdapter(selector, sort, fuccess(cached))
})(page)
@@ -23,14 +25,16 @@ private[game] final class PaginatorBuilder(cached: Cached, maxPerPage: Int) {
private def apply(adapter: AdapterLike[Game])(page: Int): Fu[Paginator[Game]] =
paginator(adapter, page)
- private def cacheAdapter(selector: JsObject, sort: Sort, nbResults: Fu[Int]): AdapterLike[Game] =
+ private def cacheAdapter(selector: Bdoc, sort: Bdoc, nbResults: Fu[Int]): AdapterLike[Game] =
new CachedAdapter(
adapter = noCacheAdapter(selector, sort),
nbResults = nbResults)
- private def noCacheAdapter(selector: JsObject, sort: Sort): AdapterLike[Game] =
- new Adapter(
+ private def noCacheAdapter(selector: Bdoc, sort: Bdoc): AdapterLike[Game] =
+ new Adapter[Game](
+ collection = coll,
selector = selector,
+ projection = $empty,
sort = sort,
readPreference = readPreference)
diff --git a/modules/game/src/main/PlayTime.scala b/modules/game/src/main/PlayTime.scala
index 66ace37217..88c8b3e94a 100644
--- a/modules/game/src/main/PlayTime.scala
+++ b/modules/game/src/main/PlayTime.scala
@@ -1,15 +1,14 @@
package lila.game
-import lila.db.api._
+import lila.db.dsl._
import lila.db.ByteArray
import lila.user.{ User, UserRepo }
import org.joda.time.Period
-import tube.gameTube
import play.api.libs.iteratee.Iteratee
import reactivemongo.bson._
-object PlayTime {
+final class PlayTime(gameColl: Coll) {
private val moveTimeField = Game.BSONFields.moveTimes
private val tvField = Game.BSONFields.tvAt
@@ -17,16 +16,16 @@ object PlayTime {
def apply(user: User): Fu[User.PlayTime] = user.playTime match {
case Some(pt) => fuccess(pt)
case None => {
- gameTube.coll
- .find(BSONDocument(
+ gameColl
+ .find($doc(
Game.BSONFields.playerUids -> user.id,
- Game.BSONFields.status -> BSONDocument("$gte" -> chess.Status.Mate.id)
+ Game.BSONFields.status -> $doc("$gte" -> chess.Status.Mate.id)
))
- .projection(BSONDocument(
+ .projection($doc(
moveTimeField -> true,
tvField -> true
))
- .cursor[BSONDocument]()
+ .cursor[Bdoc]()
.enumerate() |>>> (Iteratee.fold(User.PlayTime(0, 0)) {
case (pt, doc) =>
val t = doc.getAs[ByteArray](moveTimeField) ?? { times =>
diff --git a/modules/game/src/main/Query.scala b/modules/game/src/main/Query.scala
index e496d8a9af..520c130291 100644
--- a/modules/game/src/main/Query.scala
+++ b/modules/game/src/main/Query.scala
@@ -2,106 +2,107 @@ package lila.game
import chess.{ Color, Status }
import org.joda.time.DateTime
-import play.api.libs.json._
+import reactivemongo.bson._
-import lila.db.api._
+import lila.db.BSON.BSONJodaDateTimeHandler
+import lila.db.dsl._
import lila.user.User
object Query {
import Game.{ BSONFields => F }
- val all: JsObject = $select.all
+ val rated: Bdoc = F.rated $eq true
- val rated: JsObject = Json.obj(F.rated -> true)
+ def rated(u: String): Bdoc = user(u) ++ rated
- def rated(u: String): JsObject = user(u) ++ rated
+ def status(s: Status) = F.status $eq s.id
- def status(s: Status) = Json.obj(F.status -> s.id)
+ val created: Bdoc = F.status $eq Status.Created.id
- val created: JsObject = Json.obj(F.status -> Status.Created.id)
+ val started: Bdoc = F.status $gte Status.Started.id
- val started: JsObject = Json.obj(F.status -> $gte(Status.Started.id))
+ def started(u: String): Bdoc = user(u) ++ started
- def started(u: String): JsObject = user(u) ++ started
+ val playable: Bdoc = F.status $lt Status.Aborted.id
- val playable = Json.obj(F.status -> $lt(Status.Aborted.id))
+ val mate: Bdoc = status(Status.Mate)
- val mate = status(Status.Mate)
+ val draw: Bdoc = F.status $in (Status.Draw.id, Status.Stalemate.id)
- val draw: JsObject = Json.obj(F.status -> $in(Seq(Status.Draw.id, Status.Stalemate.id)))
+ def draw(u: String): Bdoc = user(u) ++ draw
- def draw(u: String): JsObject = user(u) ++ draw
+ val finished: Bdoc = F.status $gte Status.Mate.id
- val finished = Json.obj(F.status -> $gte(Status.Mate.id))
+ val notFinished: Bdoc = F.status $lte Status.Started.id
- val notFinished: JsObject = Json.obj(F.status -> $lte(Status.Started.id))
+ def analysed(an: Boolean): Bdoc = F.analysed $eq an
- def analysed(an: Boolean): JsObject = Json.obj(F.analysed -> an)
+ def turnsMoreThan(length: Int): Bdoc = F.turns $eq $gte(length)
- def turnsMoreThan(length: Int): JsObject = Json.obj(F.turns -> $gte(length))
+ val frozen: Bdoc = F.status $gte Status.Mate.id
- val frozen = Json.obj(F.status -> $gte(Status.Mate.id))
+ def imported(u: String): Bdoc = s"${F.pgnImport}.user" $eq u
- def imported(u: String): JsObject = Json.obj(s"${F.pgnImport}.user" -> u)
+ val friend: Bdoc = s"${F.source}" $eq Source.Friend.id
- val friend = Json.obj(s"${F.source}" -> Source.Friend.id)
+ def clock(c: Boolean): Bdoc = F.clock $exists c
- def clock(c: Boolean) = Json.obj(F.clock -> $exists(c))
+ def user(u: String): Bdoc = F.playerUids $eq u
+ def user(u: User): Bdoc = F.playerUids $eq u.id
+ def users(u: Seq[String]) = F.playerUids $in (u: _*)
- def user(u: String) = Json.obj(F.playerUids -> u)
- def users(u: Seq[String]) = Json.obj(F.playerUids -> $in(u))
+ val noAi: Bdoc = $doc(
+ "p0.ai" $exists false,
+ "p1.ai" $exists false)
- val noAi = Json.obj(
- "p0.ai" -> $exists(false),
- "p1.ai" -> $exists(false))
-
- def nowPlaying(u: String) = Json.obj(F.playingUids -> u)
+ def nowPlaying(u: String) = $doc(F.playingUids -> u)
def recentlyPlaying(u: String) =
- nowPlaying(u) ++ Json.obj(
- F.updatedAt -> $gt($date(DateTime.now minusMinutes 5))
- )
+ nowPlaying(u) ++ $doc(F.updatedAt $gt DateTime.now.minusMinutes(5))
// use the us index
- def win(u: String) = user(u) ++ Json.obj(F.winnerId -> u)
+ def win(u: String) = user(u) ++ $doc(F.winnerId -> u)
- def loss(u: String) = user(u) ++
- Json.obj(F.status -> $in(Status.finishedWithWinner map (_.id))) ++
- Json.obj(F.winnerId -> ($ne(u) ++ $exists(true)))
+ def loss(u: String) = user(u) ++ $doc(
+ F.status $in (Status.finishedWithWinner.map(_.id): _*),
+ F.winnerId -> $exists(true).++($ne(u))
+ )
def opponents(u1: User, u2: User) =
- Json.obj(F.playerUids -> $all(List(u1, u2).sortBy(_.count.game).map(_.id)))
+ $doc(F.playerUids.$all(List(u1, u2).sortBy(_.count.game).map(_.id):_*))
- val noProvisional = Json.obj("p0.p" -> $exists(false), "p1.p" -> $exists(false))
+ val noProvisional: Bdoc = $doc(
+ "p0.p" $exists false,
+ "p1.p" $exists false)
- def bothRatingsGreaterThan(v: Int) = Json.obj("p0.e" -> $gt(v), "p1.e" -> $gt(v))
+ def bothRatingsGreaterThan(v: Int) = $doc("p0.e" $gt v, "p1.e" $gt v)
- def turnsGt(nb: Int) = Json.obj(F.turns -> $gt(nb))
+ def turnsGt(nb: Int) = F.turns $gt nb
- def checkable = Json.obj(F.checkAt -> $lt($date(DateTime.now)))
+ def checkable = F.checkAt $lt DateTime.now
def variant(v: chess.variant.Variant) =
- Json.obj(F.variant -> v.standard.fold($exists(false), v.id))
+ $doc(F.variant -> v.standard.fold[BSONValue]($exists(false), $int(v.id)))
- lazy val notHordeOrSincePawnsAreWhite = $or(Seq(
- Json.obj(F.variant -> $ne(chess.variant.Horde.id)),
+ lazy val notHordeOrSincePawnsAreWhite: Bdoc = $or(
+ F.variant $ne chess.variant.Horde.id,
sinceHordePawnsAreWhite
- ))
+ )
- lazy val sinceHordePawnsAreWhite =
- Json.obj(F.createdAt -> $gt($date(hordeWhitePawnsSince)))
+ lazy val sinceHordePawnsAreWhite: Bdoc =
+ F.createdAt $gt hordeWhitePawnsSince
val hordeWhitePawnsSince = new DateTime(2015, 4, 11, 10, 0)
- def notFromPosition =
- Json.obj(F.variant -> $ne(chess.variant.FromPosition.id))
+ val notFromPosition: Bdoc =
+ F.variant $ne chess.variant.FromPosition.id
- def createdSince(d: DateTime) =
- Json.obj(F.createdAt -> $gt($date(d)))
+ def createdSince(d: DateTime): Bdoc =
+ F.createdAt $gt d
- val sortCreated = $sort desc F.createdAt
- val sortChronological = $sort asc F.createdAt
- val sortAntiChronological = $sort desc F.createdAt
- val sortUpdatedNoIndex = $sort desc F.updatedAt
+ val sortCreated: Bdoc = $sort desc F.createdAt
+ val sortChronological: Bdoc = $sort asc F.createdAt
+ val sortAntiChronological: Bdoc = $sort desc F.createdAt
+ val sortUpdatedNoIndex: Bdoc = $sort desc F.updatedAt
}
diff --git a/modules/game/src/main/package.scala b/modules/game/src/main/package.scala
index 06db34c9e6..84c94e51f3 100644
--- a/modules/game/src/main/package.scala
+++ b/modules/game/src/main/package.scala
@@ -1,15 +1,8 @@
package lila
-import lila.db.{ JsTube, InColl }
-
package object game extends PackageObject with WithPlay {
type PgnMoves = List[String]
- object tube {
-
- implicit lazy val gameTube = Game.tube inColl Env.current.gameColl
- }
-
private[game] def logger = lila.log("game")
}
diff --git a/modules/gameSearch/src/main/GameSearchApi.scala b/modules/gameSearch/src/main/GameSearchApi.scala
index be648d1f3b..909e1dc885 100644
--- a/modules/gameSearch/src/main/GameSearchApi.scala
+++ b/modules/gameSearch/src/main/GameSearchApi.scala
@@ -7,6 +7,7 @@ import play.api.libs.json._
import scala.util.{ Try, Success, Failure }
import lila.common.PimpedJson._
+import lila.db.dsl._
import lila.game.actorApi._
import lila.game.{ Game, GameRepo }
import lila.search._
@@ -17,9 +18,7 @@ final class GameSearchApi(client: ESClient) extends SearchReadApi[Game, Query] {
def search(query: Query, from: From, size: Size) =
client.search(query, from, size) flatMap { res =>
- import lila.db.api.$find
- import lila.game.tube.gameTube
- $find.byOrderedIds[lila.game.Game](res.ids)
+ GameRepo games res.ids
}
def count(query: Query) =
@@ -106,10 +105,10 @@ final class GameSearchApi(client: ESClient) extends SearchReadApi[Game, Query] {
val maxGames = Int.MaxValue
// val maxGames = 10 * 1000 * 1000
- lila.game.tube.gameTube.coll.find(BSONDocument(
- "ca" -> BSONDocument("$gt" -> since)
- )).sort(BSONDocument("ca" -> 1))
- .cursor[Game](ReadPreference.secondaryPreferred)
+ GameRepo.sortedCursor(
+ selector = $doc("ca" $gt since),
+ sort = $doc("ca" -> 1),
+ readPreference = ReadPreference.secondaryPreferred)
.enumerate(maxGames, stopOnError = true) &>
Enumeratee.grouped(Iteratee takeUpTo batchSize) |>>>
Enumeratee.mapM[Seq[Game]].apply[(Seq[Game], Set[String])] { games =>
diff --git a/modules/history/src/main/HistoryApi.scala b/modules/history/src/main/HistoryApi.scala
index ca42aefc97..2dc6ef478a 100644
--- a/modules/history/src/main/HistoryApi.scala
+++ b/modules/history/src/main/HistoryApi.scala
@@ -4,8 +4,7 @@ import org.joda.time.{ DateTime, Days }
import reactivemongo.bson._
import chess.Speed
-import lila.db.api._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.game.Game
import lila.user.{ User, Perfs }
@@ -46,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]
}
diff --git a/modules/hub/src/main/actorApi.scala b/modules/hub/src/main/actorApi.scala
index 98765691ed..70eae82f75 100644
--- a/modules/hub/src/main/actorApi.scala
+++ b/modules/hub/src/main/actorApi.scala
@@ -104,21 +104,6 @@ case class GameEnd(playerId: String, opponent: Option[String], win: Option[Boole
case class SimulCreate(userId: String, simulId: String, simulName: String) extends Atom(s"simulCreate", true)
case class SimulJoin(userId: String, simulId: String, simulName: String) extends Atom(s"simulJoin", true)
-object atomFormat {
- implicit val followFormat = Json.format[Follow]
- implicit val teamJoinFormat = Json.format[TeamJoin]
- implicit val teamCreateFormat = Json.format[TeamCreate]
- implicit val forumPostFormat = Json.format[ForumPost]
- implicit val noteCreateFormat = Json.format[NoteCreate]
- implicit val tourJoinFormat = Json.format[TourJoin]
- implicit val qaQuestionFormat = Json.format[QaQuestion]
- implicit val qaAnswerFormat = Json.format[QaAnswer]
- implicit val qaCommentFormat = Json.format[QaComment]
- implicit val gameEndFormat = Json.format[GameEnd]
- implicit val simulCreateFormat = Json.format[SimulCreate]
- implicit val simulJoinFormat = Json.format[SimulJoin]
-}
-
object propagation {
sealed trait Propagation
case class Users(users: List[String]) extends Propagation
diff --git a/modules/i18n/src/main/DataForm.scala b/modules/i18n/src/main/DataForm.scala
index 2ee3bf5038..7124ca1eb5 100644
--- a/modules/i18n/src/main/DataForm.scala
+++ b/modules/i18n/src/main/DataForm.scala
@@ -5,10 +5,8 @@ import play.api.data._
import play.api.data.Forms._
import play.api.mvc.Request
-import lila.db.api.$insert
-import tube.translationTube
-
final class DataForm(
+ repo: TranslationRepo,
keys: I18nKeys,
val captcher: akka.actor.ActorSelection,
callApi: CallApi) extends lila.hub.CaptchedForm {
@@ -28,7 +26,7 @@ final class DataForm(
}).toList collect {
case (key, Some(value)) => key -> value
}
- messages.nonEmpty ?? TranslationRepo.nextId flatMap { id =>
+ messages.nonEmpty ?? repo.nextId flatMap { id =>
val sorted = (keys.keys map { key =>
messages find (_._1 == key.key)
}).flatten
@@ -41,7 +39,7 @@ final class DataForm(
comment = metadata.comment,
author = user.some,
createdAt = DateTime.now)
- $insert(translation) >>- callApi.submit(code)
+ repo.insert(translation).void >>- callApi.submit(code)
}
}
diff --git a/modules/i18n/src/main/Env.scala b/modules/i18n/src/main/Env.scala
index d1b3260c8d..d23e2efa49 100644
--- a/modules/i18n/src/main/Env.scala
+++ b/modules/i18n/src/main/Env.scala
@@ -31,7 +31,7 @@ final class Env(
def hideCallsCookieName = HideCallsCookieName
def hideCallsCookieMaxAge = HideCallsCookieMaxAge
- private[i18n] lazy val translationColl = db(CollectionTranslation)
+ private val translationColl = db(CollectionTranslation)
lazy val pool = new I18nPool(
langs = Lang.availables(play.api.Play.current).toSet,
@@ -63,7 +63,10 @@ final class Env(
messages = messages,
keys = keys)
+ lazy val repo = new TranslationRepo(translationColl)
+
lazy val forms = new DataForm(
+ repo = repo,
keys = keys,
captcher = captcher,
callApi = callApi)
@@ -85,8 +88,7 @@ final class Env(
def call = callApi.apply _
def jsonFromVersion(v: Int): Fu[JsValue] = {
- import tube.translationTube
- TranslationRepo findFrom v map { ts => Json toJson ts }
+ repo findFrom v map { ts => Json toJson ts }
}
def cli = new lila.common.Cli {
diff --git a/modules/i18n/src/main/Translation.scala b/modules/i18n/src/main/Translation.scala
index c937ec6c82..8f53642110 100644
--- a/modules/i18n/src/main/Translation.scala
+++ b/modules/i18n/src/main/Translation.scala
@@ -17,18 +17,7 @@ private[i18n] case class Translation(
private[i18n] object Translation {
- import lila.db.JsTube
- import JsTube.Helpers._
import play.api.libs.json._
- private def defaults = Json.obj(
- "author" -> none[String],
- "comment" -> none[String])
-
- private[i18n] val tube = JsTube(
- (__.json update (
- merge(defaults) andThen readDate('createdAt)
- )) andThen Json.reads[Translation],
- Json.writes[Translation] andThen (__.json update writeDate('createdAt))
- )
+ private[i18n] implicit val translationI18nFormat = Json.format[Translation]
}
diff --git a/modules/i18n/src/main/TranslationRepo.scala b/modules/i18n/src/main/TranslationRepo.scala
index 94b830693a..f02e254bca 100644
--- a/modules/i18n/src/main/TranslationRepo.scala
+++ b/modules/i18n/src/main/TranslationRepo.scala
@@ -2,20 +2,20 @@ package lila.i18n
import play.api.libs.json.Json
-import lila.db.api._
-import lila.db.Implicits._
-import tube.translationTube
+import lila.db.dsl._
+import lila.db.BSON.BSONJodaDateTimeHandler
-private[i18n] object TranslationRepo {
+private[i18n] final class TranslationRepo(coll: Coll) {
- type ID = Int
+ private implicit val TranslationBSONHandler = reactivemongo.bson.Macros.handler[Translation]
- def nextId: Fu[ID] = $primitive.one(
- $select.all,
- "_id",
- _ sort $sort.descId
- )(_.asOpt[Int]) map (opt => ~opt + 1)
+ def nextId: Fu[Int] = coll.primitiveOne[Int](
+ selector = $empty,
+ sort = $sort desc "_id",
+ "_id") map (opt => ~opt + 1)
- def findFrom(id: ID): Fu[List[Translation]] =
- $find($query(Json.obj("_id" -> $gte(id))) sort $sort.ascId)
+ def findFrom(id: Int): Fu[List[Translation]] =
+ coll.find($doc("_id" $gte id)).sort($sort asc "_id").cursor[Translation]().gather[List]()
+
+ def insert(t: Translation) = coll insert t
}
diff --git a/modules/i18n/src/main/UpstreamFetch.scala b/modules/i18n/src/main/UpstreamFetch.scala
index b698d6374d..d480a8fcf9 100644
--- a/modules/i18n/src/main/UpstreamFetch.scala
+++ b/modules/i18n/src/main/UpstreamFetch.scala
@@ -5,12 +5,13 @@ import play.api.libs.json._
import lila.common.PimpedJson._
import play.api.libs.ws.WS
import play.api.Play.current
-import tube.translationTube
private[i18n] final class UpstreamFetch(upstreamUrl: Int => String) {
private type Fetched = Fu[List[Translation]]
+ import Translation.translationI18nFormat
+
def apply(from: Int): Fetched =
fetch(upstreamUrl(from)) map parse flatMap {
_.fold(e => fufail(e.toString), fuccess(_))
diff --git a/modules/i18n/src/main/package.scala b/modules/i18n/src/main/package.scala
index 10357e188e..44c810abea 100644
--- a/modules/i18n/src/main/package.scala
+++ b/modules/i18n/src/main/package.scala
@@ -4,12 +4,6 @@ package object i18n extends PackageObject with WithPlay {
type Messages = Map[String, Map[String, String]]
- object tube {
-
- private[i18n] implicit lazy val translationTube =
- Translation.tube inColl Env.current.translationColl
- }
-
import scala.concurrent.Future
private[i18n] def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit): Funit = Future {
diff --git a/modules/importer/src/main/Importer.scala b/modules/importer/src/main/Importer.scala
index ea82b7b6ff..6f292e8cdd 100644
--- a/modules/importer/src/main/Importer.scala
+++ b/modules/importer/src/main/Importer.scala
@@ -8,7 +8,7 @@ import akka.pattern.{ ask, after }
import chess.{ Color, MoveOrDrop, Status, Situation }
import makeTimeout.large
-import lila.db.api._
+import lila.db.dsl._
import lila.game.{ Game, GameRepo, Pov }
import lila.hub.actorApi.map.Tell
import lila.round.actorApi.round._
diff --git a/modules/insight/src/main/AggregationPipeline.scala b/modules/insight/src/main/AggregationPipeline.scala
index 6322d19431..e87b96ea49 100644
--- a/modules/insight/src/main/AggregationPipeline.scala
+++ b/modules/insight/src/main/AggregationPipeline.scala
@@ -4,7 +4,7 @@ import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework
import reactivemongo.bson._
import scalaz.NonEmptyList
-import lila.db.Implicits._
+import lila.db.dsl._
private final class AggregationPipeline {
@@ -14,20 +14,20 @@ private final class AggregationPipeline {
private lazy val movetimeIdDispatcher =
MovetimeRange.reversedNoInf.foldLeft[BSONValue](BSONInteger(MovetimeRange.MTRInf.id)) {
- case (acc, mtr) => BSONDocument(
- "$cond" -> BSONArray(
- BSONDocument("$lte" -> BSONArray("$" + F.moves("t"), mtr.tenths.last)),
+ case (acc, mtr) => $doc(
+ "$cond" -> $arr(
+ $doc("$lte" -> $arr("$" + F.moves("t"), mtr.tenths.last)),
mtr.id,
acc))
}
- private lazy val materialIdDispatcher = BSONDocument(
- "$cond" -> BSONArray(
- BSONDocument("$eq" -> BSONArray("$" + F.moves("i"), 0)),
+ private lazy val materialIdDispatcher = $doc(
+ "$cond" -> $arr(
+ $doc("$eq" -> $arr("$" + F.moves("i"), 0)),
MaterialRange.Equal.id,
MaterialRange.reversedButEqualAndLast.foldLeft[BSONValue](BSONInteger(MaterialRange.Up4.id)) {
- case (acc, mat) => BSONDocument(
- "$cond" -> BSONArray(
- BSONDocument(mat.negative.fold("$lt", "$lte") -> BSONArray("$" + F.moves("i"), mat.imbalance)),
+ case (acc, mat) => $doc(
+ "$cond" -> $arr(
+ $doc(mat.negative.fold("$lt", "$lte") -> $arr("$" + F.moves("i"), mat.imbalance)),
mat.id,
acc))
}))
@@ -48,7 +48,7 @@ private final class AggregationPipeline {
"nb" -> SumValue(1),
"ids" -> AddToSet("_id")
).some
- private def groupMulti(d: Dimension[_], metricDbKey: String) = Group(BSONDocument(
+ private def groupMulti(d: Dimension[_], metricDbKey: String) = Group($doc(
"dimension" -> dimensionGroupId(d),
"metric" -> ("$" + metricDbKey)))(
"v" -> SumValue(1),
@@ -60,17 +60,17 @@ private final class AggregationPipeline {
"stack" -> PushMulti(
"metric" -> "_id.metric",
"v" -> "v")).some
- private val sliceIds = Project(BSONDocument(
+ private val sliceIds = Project($doc(
"_id" -> true,
"v" -> true,
"nb" -> true,
- "ids" -> BSONDocument("$slice" -> BSONArray("$ids", 4))
+ "ids" -> $doc("$slice" -> $arr("$ids", 4))
)).some
- private val sliceStackedIds = Project(BSONDocument(
+ private val sliceStackedIds = Project($doc(
"_id" -> true,
"nb" -> true,
"stack" -> true,
- "ids" -> BSONDocument("$slice" -> BSONArray("$ids", 4))
+ "ids" -> $doc("$slice" -> $arr("$ids", 4))
)).some
def apply(question: Question[_], userId: String): NonEmptyList[PipelineOperator] = {
@@ -78,11 +78,11 @@ private final class AggregationPipeline {
val gameMatcher = combineDocs(question.filters.collect {
case f if f.dimension.isInGame => f.matcher
})
- def matchMoves(extraMatcher: BSONDocument = BSONDocument()) =
+ def matchMoves(extraMatcher: Bdoc = $empty) =
combineDocs(extraMatcher :: question.filters.collect {
case f if f.dimension.isInMove => f.matcher
}).some.filterNot(_.isEmpty) map Match
- def projectForMove = Project(BSONDocument({
+ def projectForMove = Project($doc({
metric.dbKey :: dimension.dbKey :: filters.collect {
case Filter(d, _) if d.isInMove => d.dbKey
}
@@ -92,10 +92,10 @@ private final class AggregationPipeline {
Match(
selectUserId(userId) ++
gameMatcher ++
- (dimension == Dimension.Opening).??(BSONDocument(F.eco -> BSONDocument("$exists" -> true))) ++
- Metric.requiresAnalysis(metric).??(BSONDocument(F.analysed -> true)) ++
+ (dimension == Dimension.Opening).??($doc(F.eco -> $doc("$exists" -> true))) ++
+ Metric.requiresAnalysis(metric).??($doc(F.analysed -> true)) ++
(Metric.requiresStableRating(metric) || Dimension.requiresStableRating(dimension)).?? {
- BSONDocument(F.provisional -> BSONDocument("$ne" -> true))
+ $doc(F.provisional -> $doc("$ne" -> true))
}
),
/* sortDate :: */ sampleGames :: ((metric match {
@@ -118,15 +118,15 @@ private final class AggregationPipeline {
case M.Opportunism => List(
projectForMove,
unwindMoves,
- matchMoves(BSONDocument(F.moves("o") -> BSONDocument("$exists" -> true))),
+ matchMoves($doc(F.moves("o") -> $doc("$exists" -> true))),
sampleMoves,
- group(dimension, GroupFunction("$push", BSONDocument(
- "$cond" -> BSONArray("$" + F.moves("o"), 1, 0)
+ group(dimension, GroupFunction("$push", $doc(
+ "$cond" -> $arr("$" + F.moves("o"), 1, 0)
))),
sliceIds,
- Project(BSONDocument(
+ Project($doc(
"_id" -> true,
- "v" -> BSONDocument("$multiply" -> BSONArray(100, BSONDocument("$avg" -> "$v"))),
+ "v" -> $doc("$multiply" -> $arr(100, $doc("$avg" -> "$v"))),
"nb" -> true,
"ids" -> true
)).some
@@ -134,15 +134,15 @@ private final class AggregationPipeline {
case M.Luck => List(
projectForMove,
unwindMoves,
- matchMoves(BSONDocument(F.moves("l") -> BSONDocument("$exists" -> true))),
+ matchMoves($doc(F.moves("l") -> $doc("$exists" -> true))),
sampleMoves,
- group(dimension, GroupFunction("$push", BSONDocument(
- "$cond" -> BSONArray("$" + F.moves("l"), 1, 0)
+ group(dimension, GroupFunction("$push", $doc(
+ "$cond" -> $arr("$" + F.moves("l"), 1, 0)
))),
sliceIds,
- Project(BSONDocument(
+ Project($doc(
"_id" -> true,
- "v" -> BSONDocument("$multiply" -> BSONArray(100, BSONDocument("$avg" -> "$v"))),
+ "v" -> $doc("$multiply" -> $arr(100, $doc("$avg" -> "$v"))),
"nb" -> true,
"ids" -> true
)).some
@@ -153,17 +153,17 @@ private final class AggregationPipeline {
matchMoves(),
sampleMoves,
group(dimension, SumValue(1)),
- Project(BSONDocument(
+ Project($doc(
"v" -> true,
"ids" -> true,
- "nb" -> BSONDocument("$size" -> "$ids")
+ "nb" -> $doc("$size" -> "$ids")
)).some,
- Project(BSONDocument(
- "v" -> BSONDocument(
- "$divide" -> BSONArray("$v", "$nb")
+ Project($doc(
+ "v" -> $doc(
+ "$divide" -> $arr("$v", "$nb")
),
"nb" -> true,
- "ids" -> BSONDocument("$slice" -> BSONArray("$ids", 4))
+ "ids" -> $doc("$slice" -> $arr("$ids", 4))
)).some
)
case M.Movetime => List(
@@ -172,7 +172,7 @@ private final class AggregationPipeline {
matchMoves(),
sampleMoves,
group(dimension, GroupFunction("$avg",
- BSONDocument("$divide" -> BSONArray("$" + F.moves("t"), 10))
+ $doc("$divide" -> $arr("$" + F.moves("t"), 10))
)),
sliceIds
)
diff --git a/modules/insight/src/main/BSONHandlers.scala b/modules/insight/src/main/BSONHandlers.scala
index 1c3a8b1b03..abd653d2b9 100644
--- a/modules/insight/src/main/BSONHandlers.scala
+++ b/modules/insight/src/main/BSONHandlers.scala
@@ -5,8 +5,7 @@ import reactivemongo.bson.Macros
import chess.{ Role, Color }
import lila.db.BSON
-import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.BSONHandlers.StatusBSONHandler
import lila.rating.PerfType
@@ -61,7 +60,7 @@ private object BSONHandlers {
def write(e: MaterialRange) = BSONInteger(e.id)
}
implicit def MoveBSONHandler = new BSON[Move] {
- def reads(r: Reader) = Move(
+ def reads(r: BSON.Reader) = Move(
phase = r.get[Phase]("p"),
tenths = r.get[Int]("t"),
role = r.get[Role]("r"),
@@ -71,7 +70,7 @@ private object BSONHandlers {
material = r.int("i"),
opportunism = r.boolO("o"),
luck = r.boolO("l"))
- def writes(w: Writer, b: Move) = BSONDocument(
+ def writes(w: BSON.Writer, b: Move) = BSONDocument(
"p" -> b.phase,
"t" -> b.tenths,
"r" -> b.role,
@@ -85,7 +84,7 @@ private object BSONHandlers {
implicit def EntryBSONHandler = new BSON[Entry] {
import Entry.BSONFields._
- def reads(r: Reader) = Entry(
+ def reads(r: BSON.Reader) = Entry(
id = r.str(id),
number = r.int(number),
userId = r.str(userId),
@@ -104,7 +103,7 @@ private object BSONHandlers {
analysed = r.boolD(analysed),
provisional = r.boolD(provisional),
date = r.date(date))
- def writes(w: Writer, e: Entry) = BSONDocument(
+ def writes(w: BSON.Writer, e: Entry) = BSONDocument(
id -> e.id,
number -> e.number,
userId -> e.userId,
diff --git a/modules/insight/src/main/Dimension.scala b/modules/insight/src/main/Dimension.scala
index 84a44984e8..8f01437828 100644
--- a/modules/insight/src/main/Dimension.scala
+++ b/modules/insight/src/main/Dimension.scala
@@ -4,7 +4,7 @@ import play.twirl.api.Html
import reactivemongo.bson._
import chess.{ Color, Role }
-import lila.db.Types._
+import lila.db.dsl._
import lila.rating.PerfType
sealed abstract class Dimension[A: BSONValueHandler](
@@ -141,15 +141,15 @@ object Dimension {
case MaterialRange => v.id
}).toString
- def filtersOf[X](d: Dimension[X], selected: List[X]): BSONDocument = d match {
+ def filtersOf[X](d: Dimension[X], selected: List[X]): Bdoc = d match {
case Dimension.MovetimeRange => selected match {
- case Nil => BSONDocument()
- case xs => BSONDocument(d.dbKey -> BSONDocument("$in" -> xs.flatMap(_.tenths.list)))
+ case Nil => $empty
+ case xs => $doc(d.dbKey -> $doc("$in" -> xs.flatMap(_.tenths.list)))
}
case _ => selected map d.bson.write match {
- case Nil => BSONDocument()
- case List(x) => BSONDocument(d.dbKey -> x)
- case xs => BSONDocument(d.dbKey -> BSONDocument("$in" -> BSONArray(xs)))
+ case Nil => $empty
+ case List(x) => $doc(d.dbKey -> x)
+ case xs => $doc(d.dbKey -> $doc("$in" -> BSONArray(xs)))
}
}
}
diff --git a/modules/insight/src/main/Indexer.scala b/modules/insight/src/main/Indexer.scala
index 8df5ec8467..aff2a683df 100644
--- a/modules/insight/src/main/Indexer.scala
+++ b/modules/insight/src/main/Indexer.scala
@@ -3,15 +3,12 @@ package lila.insight
import akka.actor.ActorRef
import org.joda.time.DateTime
import play.api.libs.iteratee._
-import play.api.libs.json.Json
import reactivemongo.bson._
-import lila.db.api._
-import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.dsl._
+import lila.db.dsl._
import lila.game.BSONHandlers.gameBSONHandler
-import lila.game.tube.gameTube
-import lila.game.{ Game, Query }
+import lila.game.{ Game, GameRepo, Query }
import lila.hub.Sequencer
import lila.rating.PerfType
import lila.user.User
@@ -55,10 +52,15 @@ private final class Indexer(storage: Storage, sequencer: ActorRef) {
private def fetchFirstGame(user: User): Fu[Option[Game]] =
if (user.count.rated == 0) fuccess(none)
else {
- (user.count.rated >= maxGames) ??
- pimpQB($query(gameQuery(user))).sort(Query.sortCreated).skip(maxGames - 1).one[Game]
- } orElse
- pimpQB($query(gameQuery(user))).sort(Query.sortChronological).one[Game]
+ (user.count.rated >= maxGames) ?? GameRepo.coll
+ .find(gameQuery(user))
+ .sort(Query.sortCreated)
+ .skip(maxGames - 1)
+ .uno[Game]
+ } orElse GameRepo.coll
+ .find(gameQuery(user))
+ .sort(Query.sortCreated)
+ .uno[Game]
private def computeFrom(user: User, from: DateTime, fromNumber: Int): Funit = {
storage nbByPerf user.id flatMap { nbs =>
@@ -71,10 +73,8 @@ private final class Indexer(storage: Storage, sequencer: ActorRef) {
e.printStackTrace
} map (_.toOption)
}
- val query = $query(gameQuery(user) ++ Json.obj(Game.BSONFields.createdAt -> $gte($date(from))))
- pimpQB(query)
- .sort(Query.sortChronological)
- .cursor[Game]()
+ val query = gameQuery(user) ++ $doc(Game.BSONFields.createdAt $gte from)
+ GameRepo.sortedCursor(query, Query.sortChronological)
.enumerate(maxGames, stopOnError = true) &>
Enumeratee.grouped(Iteratee takeUpTo 4) &>
Enumeratee.mapM[Seq[Game]].apply[Seq[Entry]] { games =>
diff --git a/modules/insight/src/main/InsightApi.scala b/modules/insight/src/main/InsightApi.scala
index 1c4bb559eb..c5ae973e4e 100644
--- a/modules/insight/src/main/InsightApi.scala
+++ b/modules/insight/src/main/InsightApi.scala
@@ -4,7 +4,7 @@ import org.joda.time.DateTime
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
import reactivemongo.bson._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.{ Game, GameRepo, Pov }
import lila.user.User
@@ -39,7 +39,7 @@ final class InsightApi(
def userStatus(user: User): Fu[UserStatus] =
GameRepo lastFinishedRatedNotFromPosition user flatMap {
case None => fuccess(UserStatus.NoGame)
- case Some(game) => storage fetchLast user map {
+ case Some(game) => storage fetchLast user.id map {
case None => UserStatus.Empty
case Some(entry) if entry.date isBefore game.createdAt => UserStatus.Stale
case _ => UserStatus.Fresh
diff --git a/modules/insight/src/main/Storage.scala b/modules/insight/src/main/Storage.scala
index 0607e4ad24..9a503c5841 100644
--- a/modules/insight/src/main/Storage.scala
+++ b/modules/insight/src/main/Storage.scala
@@ -7,8 +7,7 @@ import reactivemongo.bson._
import scala.concurrent.duration._
import scalaz.NonEmptyList
-import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.user.UserRepo
import lila.rating.PerfType
@@ -22,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)
@@ -42,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
diff --git a/modules/insight/src/main/UserCache.scala b/modules/insight/src/main/UserCache.scala
index 73cd309807..c3995786c4 100644
--- a/modules/insight/src/main/UserCache.scala
+++ b/modules/insight/src/main/UserCache.scala
@@ -1,13 +1,10 @@
package lila.insight
import org.joda.time.DateTime
-import play.api.libs.iteratee._
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
import reactivemongo.bson._
-import reactivemongo.bson.Macros
-import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.dsl._
case class UserCache(
_id: String, // user id
@@ -22,11 +19,9 @@ private final class UserCacheApi(coll: Coll) {
private implicit val userCacheBSONHandler = Macros.handler[UserCache]
- def find(id: String) = coll.find(selectId(id)).one[UserCache]
+ def find(id: String) = coll.uno[UserCache]($id(id))
- def save(u: UserCache) = coll.update(selectId(u.id), u, upsert = true).void
+ def save(u: UserCache) = coll.update($id(u.id), u, upsert = true).void
- def remove(id: String) = coll.remove(selectId(id)).void
-
- private def selectId(id: String) = BSONDocument("_id" -> id)
+ def remove(id: String) = coll.remove($id(id)).void
}
diff --git a/modules/lobby/src/main/Env.scala b/modules/lobby/src/main/Env.scala
index 84374fc0fb..7bc75024fd 100644
--- a/modules/lobby/src/main/Env.scala
+++ b/modules/lobby/src/main/Env.scala
@@ -59,7 +59,7 @@ final class Env(
hub = hub,
lobby = lobby,
socket = socket,
- blocking = blocking)
+ blocking = blocking)(system)
lazy val history = new History[actorApi.Messadata](ttl = MessageTtl)
diff --git a/modules/lobby/src/main/Lobby.scala b/modules/lobby/src/main/Lobby.scala
index 6b0f3ccf04..3d1a8811d7 100644
--- a/modules/lobby/src/main/Lobby.scala
+++ b/modules/lobby/src/main/Lobby.scala
@@ -6,7 +6,7 @@ import akka.actor._
import akka.pattern.{ ask, pipe }
import actorApi._
-import lila.db.api._
+import lila.db.dsl._
import lila.game.GameRepo
import lila.hub.actorApi.{ GetUids, SocketUids }
import lila.socket.actorApi.Broom
diff --git a/modules/lobby/src/main/SeekApi.scala b/modules/lobby/src/main/SeekApi.scala
index 7fdd69d51d..237cb30df5 100644
--- a/modules/lobby/src/main/SeekApi.scala
+++ b/modules/lobby/src/main/SeekApi.scala
@@ -1,13 +1,11 @@
package lila.lobby
import org.joda.time.DateTime
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONRegex, BSONArray, BSONBoolean }
import reactivemongo.core.commands._
import scala.concurrent.duration._
import actorApi.LobbyUser
-import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.memo.AsyncCache
import lila.user.{ User, UserRepo }
@@ -23,14 +21,14 @@ final class SeekApi(
private object ForUser extends CacheKey
private def allCursor =
- coll.find(BSONDocument())
- .sort(BSONDocument("createdAt" -> -1))
+ coll.find($empty)
+ .sort($doc("createdAt" -> -1))
.cursor[Seek]()
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)
@@ -58,7 +56,7 @@ final class SeekApi(
}._1.reverse
def find(id: String): Fu[Option[Seek]] =
- coll.find(BSONDocument("_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,27 +65,27 @@ final class SeekApi(
} >> cache.clear
def findByUser(userId: String): Fu[List[Seek]] =
- coll.find(BSONDocument("user.id" -> userId))
- .sort(BSONDocument("createdAt" -> -1))
- .cursor[Seek]().collect[List]()
+ coll.find($doc("user.id" -> userId))
+ .sort($doc("createdAt" -> -1))
+ .cursor[Seek]().gather[List]()
def remove(seek: Seek) =
- coll.remove(BSONDocument("_id" -> seek.id)).void >> cache.clear
+ coll.remove($doc("_id" -> seek.id)).void >> cache.clear
def archive(seek: Seek, gameId: String) = {
- val archiveDoc = Seek.seekBSONHandler.write(seek) ++ BSONDocument(
+ val archiveDoc = Seek.seekBSONHandler.write(seek) ++ $doc(
"gameId" -> gameId,
"archivedAt" -> DateTime.now)
- coll.remove(BSONDocument("_id" -> seek.id)).void >>
+ coll.remove($doc("_id" -> seek.id)).void >>
cache.clear >>
archiveColl.insert(archiveDoc)
}
def findArchived(gameId: String): Fu[Option[Seek]] =
- archiveColl.find(BSONDocument("gameId" -> gameId)).one[Seek]
+ archiveColl.find($doc("gameId" -> gameId)).uno[Seek]
def removeBy(seekId: String, userId: String) =
- coll.remove(BSONDocument(
+ coll.remove($doc(
"_id" -> seekId,
"user.id" -> userId
)).void >> cache.clear
diff --git a/modules/lobby/src/main/Socket.scala b/modules/lobby/src/main/Socket.scala
index c5d385f7f5..af84fd63fb 100644
--- a/modules/lobby/src/main/Socket.scala
+++ b/modules/lobby/src/main/Socket.scala
@@ -5,7 +5,6 @@ import scala.concurrent.Future
import akka.actor._
import akka.pattern.ask
-import play.api.libs.iteratee._
import play.api.libs.json._
import play.twirl.api.Html
@@ -16,7 +15,7 @@ import lila.game.AnonCookie
import lila.hub.actorApi.game.ChangeFeatured
import lila.hub.actorApi.lobby._
import lila.hub.actorApi.timeline._
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.{ SocketActor, History, Historical }
import makeTimeout.short
@@ -52,11 +51,7 @@ private[lobby] final class Socket(
}
}
- case Join(uid, user, blocks, mobile) =>
- val (enumerator, channel) = Concurrent.broadcast[JsValue]
- val member = Member(channel, user, blocks, uid, mobile)
- addMember(uid, member)
- sender ! Connected(enumerator, member)
+ case AddMember(uid, member) => addMember(uid, member)
case ReloadTournaments(html) => notifyAllAsync(makeMessage("tournaments", html))
diff --git a/modules/lobby/src/main/SocketHandler.scala b/modules/lobby/src/main/SocketHandler.scala
index 63bd02b21f..edf3109d8f 100644
--- a/modules/lobby/src/main/SocketHandler.scala
+++ b/modules/lobby/src/main/SocketHandler.scala
@@ -2,14 +2,13 @@ package lila.lobby
import akka.actor._
import akka.pattern.{ ask, pipe }
-import play.api.libs.iteratee._
import play.api.libs.json._
import scala.concurrent.duration._
import actorApi._
import lila.common.PimpedJson._
import lila.hub.actorApi.lobby._
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.Handler
import lila.user.User
import makeTimeout.short
@@ -18,7 +17,7 @@ private[lobby] final class SocketHandler(
hub: lila.hub.Env,
lobby: ActorRef,
socket: ActorRef,
- blocking: String => Fu[Set[String]]) {
+ blocking: String => Fu[Set[String]])(implicit system: ActorSystem) {
private def controller(
socket: ActorRef,
@@ -40,12 +39,14 @@ private[lobby] final class SocketHandler(
} lobby ! CancelSeek(id, user)
}
- def apply(uid: String, user: Option[User], mobile: Boolean): Fu[JsSocketHandler] =
- (user ?? (u => blocking(u.id))) flatMap { blockedUserIds =>
- val join = Join(uid = uid, user = user, blocking = blockedUserIds, mobile = mobile)
- Handler(hub, socket, uid, join, user map (_.id)) {
- case Connected(enum, member) =>
- (controller(socket, uid, member), enum, member)
+ def apply(uid: String, user: Option[User], mobile: Boolean): Fu[JsFlow] =
+ (user ?? (u => blocking(u.id))) map { blockedUserIds =>
+ Handler.actorRef { out =>
+ val member = Member(out, user, blockedUserIds, uid, mobile)
+ socket ! AddMember(uid, member)
+ Handler.props(hub, socket, member, uid, user.map(_.id)) {
+ controller(socket, uid, member)
+ }
}
}
}
diff --git a/modules/lobby/src/main/actorApi.scala b/modules/lobby/src/main/actorApi.scala
index 8b79e18197..f36d17cdb4 100644
--- a/modules/lobby/src/main/actorApi.scala
+++ b/modules/lobby/src/main/actorApi.scala
@@ -1,6 +1,8 @@
package lila.lobby
package actorApi
+import akka.actor.ActorRef
+
import lila.game.Game
import lila.socket.SocketMember
import lila.user.User
@@ -29,7 +31,7 @@ private[lobby] object LobbyUser {
}
private[lobby] case class Member(
- channel: JsChannel,
+ out: ActorRef,
user: Option[LobbyUser],
uid: String,
mobile: Boolean) extends SocketMember {
@@ -40,8 +42,8 @@ private[lobby] case class Member(
private[lobby] object Member {
- def apply(channel: JsChannel, user: Option[User], blocking: Set[String], uid: String, mobile: Boolean): Member = Member(
- channel = channel,
+ def apply(out: ActorRef, user: Option[User], blocking: Set[String], uid: String, mobile: Boolean): Member = Member(
+ out = out,
user = user map { LobbyUser.make(_, blocking) },
uid = uid,
mobile = mobile)
@@ -51,7 +53,6 @@ private[lobby] case class HookMeta(hookId: Option[String] = None)
private[lobby] case class Messadata(hook: Option[Hook] = None)
-private[lobby] case class Connected(enumerator: JsEnumerator, member: Member)
private[lobby] case class WithHooks(op: Iterable[String] => Unit)
private[lobby] case class SaveHook(msg: AddHook)
private[lobby] case class SaveSeek(msg: AddSeek)
@@ -64,7 +65,7 @@ private[lobby] case class BiteHook(hookId: String, uid: String, user: Option[Lob
private[lobby] case class BiteSeek(seekId: String, user: LobbyUser)
private[lobby] case class JoinHook(uid: String, hook: Hook, game: Game, creatorColor: chess.Color)
private[lobby] case class JoinSeek(userId: String, seek: Seek, game: Game, creatorColor: chess.Color)
-private[lobby] case class Join(uid: String, user: Option[User], blocking: Set[String], mobile: Boolean)
+private[lobby] case class AddMember(uid: String, member: Member)
private[lobby] case object Resync
private[lobby] case class HookIds(ids: Vector[String])
diff --git a/modules/memo/src/main/Env.scala b/modules/memo/src/main/Env.scala
index 2bcb677459..9cf9652661 100644
--- a/modules/memo/src/main/Env.scala
+++ b/modules/memo/src/main/Env.scala
@@ -1,7 +1,7 @@
package lila.memo
import com.typesafe.config.Config
-import lila.db.Types._
+import lila.db.dsl._
final class Env(config: Config, db: lila.db.Env) {
diff --git a/modules/memo/src/main/MongoCache.scala b/modules/memo/src/main/MongoCache.scala
index fac348308e..91e2600da4 100644
--- a/modules/memo/src/main/MongoCache.scala
+++ b/modules/memo/src/main/MongoCache.scala
@@ -7,7 +7,7 @@ import scala.concurrent.duration._
import spray.caching.{ LruCache, Cache }
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types._
+import lila.db.dsl._
final class MongoCache[K, V: MongoCache.Handler] private (
prefix: String,
@@ -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
diff --git a/modules/message/src/main/Api.scala b/modules/message/src/main/Api.scala
index b4c836cd13..ad9cf03937 100644
--- a/modules/message/src/main/Api.scala
+++ b/modules/message/src/main/Api.scala
@@ -3,37 +3,39 @@ package lila.message
import akka.pattern.pipe
import lila.common.paginator._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
import lila.hub.actorApi.message._
import lila.hub.actorApi.SendTo
import lila.security.Granter
import lila.user.{ User, UserRepo }
-import tube.threadTube
final class Api(
+ coll: Coll,
unreadCache: UnreadCache,
shutup: akka.actor.ActorSelection,
maxPerPage: Int,
blocks: (String, String) => Fu[Boolean],
bus: lila.common.Bus) {
+ import Thread.ThreadBSONHandler
+
def inbox(me: User, page: Int): Fu[Paginator[Thread]] = Paginator(
adapter = new Adapter(
+ collection = coll,
selector = ThreadRepo visibleByUserQuery me.id,
- sort = Seq(ThreadRepo.recentSort)
- ),
+ projection = $empty,
+ sort = ThreadRepo.recentSort),
currentPage = page,
maxPerPage = maxPerPage
)
def preview(userId: String): Fu[List[Thread]] = unreadCache(userId) flatMap { ids =>
- $find byOrderedIds ids
+ coll.byOrderedIds[Thread](ids)(_.id)
}
def thread(id: String, me: User): Fu[Option[Thread]] = for {
- threadOption ← $find.byId(id) map (_ filter (_ hasUser me))
+ threadOption ← coll.byId[Thread](id) map (_ filter (_ hasUser me))
_ ← threadOption.filter(_ isUnReadBy me).??(thread =>
(ThreadRepo setRead thread) >>- updateUser(me)
)
@@ -70,9 +72,9 @@ final class Api(
invitedId = lt.to), fromMod = false) >> unreadCache.clear(lt.to)
private def sendUnlessBlocked(thread: Thread, fromMod: Boolean): Funit =
- if (fromMod) $insert(thread)
+ if (fromMod) coll.insert(thread).void
else blocks(thread.invitedId, thread.creatorId) flatMap {
- !_ ?? $insert(thread)
+ !_ ?? coll.insert(thread).void
}
def makePost(thread: Thread, text: String, me: User): Fu[Thread] = {
@@ -84,7 +86,7 @@ final class Api(
case true => fuccess(thread)
case false =>
val newThread = thread + post
- $update[ThreadRepo.ID, Thread](newThread) >>- {
+ coll.update($id(newThread.id), newThread) >>- {
UserRepo.named(thread receiverOf post) foreach {
_ foreach updateUser
}
@@ -104,7 +106,7 @@ final class Api(
val unreadIds = unreadCache apply _
def updateUser(user: lila.user.User) {
- if (!user.kid) (unreadCache refresh user) mapTo manifest[List[String]] foreach { ids =>
+ if (!user.kid) (unreadCache refresh user.id) mapTo manifest[List[String]] foreach { ids =>
bus.publish(SendTo(user.id, "nbm", ids.size), 'users)
}
}
diff --git a/modules/message/src/main/Env.scala b/modules/message/src/main/Env.scala
index 61079d8034..553380980d 100644
--- a/modules/message/src/main/Env.scala
+++ b/modules/message/src/main/Env.scala
@@ -29,6 +29,7 @@ final class Env(
lazy val forms = new DataForm(security = security)
lazy val api = new Api(
+ coll = threadColl,
unreadCache = unreadCache,
shutup = shutup,
maxPerPage = ThreadMaxPerPage,
diff --git a/modules/message/src/main/Post.scala b/modules/message/src/main/Post.scala
index 9947020f02..0fee0b65b4 100644
--- a/modules/message/src/main/Post.scala
+++ b/modules/message/src/main/Post.scala
@@ -30,12 +30,6 @@ object Post {
isRead = false,
createdAt = DateTime.now)
- import lila.db.JsTube
- import JsTube.Helpers._
- import play.api.libs.json._
-
- private[message] lazy val tube = JsTube(
- (__.json update readDate('createdAt)) andThen Json.reads[Post],
- Json.writes[Post].andThen(__.json update writeDate('createdAt))
- )
+ import lila.db.dsl.BSONJodaDateTimeHandler
+ private[message] implicit val PostBSONHandler = reactivemongo.bson.Macros.handler[Post]
}
diff --git a/modules/message/src/main/Thread.scala b/modules/message/src/main/Thread.scala
index a48a225630..7c32772ff7 100644
--- a/modules/message/src/main/Thread.scala
+++ b/modules/message/src/main/Thread.scala
@@ -6,7 +6,7 @@ import ornicar.scalalib.Random
import lila.user.User
case class Thread(
- id: String,
+ _id: String,
name: String,
createdAt: DateTime,
updatedAt: DateTime,
@@ -19,6 +19,8 @@ case class Thread(
posts = posts :+ post,
updatedAt = post.createdAt)
+ def id = _id
+
def isCreator(user: User) = creatorId == user.id
def isReadBy(user: User) = nbUnreadBy(user) == 0
@@ -67,7 +69,7 @@ object Thread {
text: String,
creatorId: String,
invitedId: String): Thread = Thread(
- id = Random nextStringUppercase idSize,
+ _id = Random nextStringUppercase idSize,
name = name,
createdAt = DateTime.now,
updatedAt = DateTime.now,
@@ -79,18 +81,10 @@ object Thread {
invitedId = invitedId,
visibleByUserIds = List(creatorId, invitedId))
- import lila.db.JsTube
- import JsTube.Helpers._
- import play.api.libs.json._
-
- private[message] lazy val tube = Post.tube |> { implicit pt =>
- JsTube(
- (__.json update (
- readDate('createdAt) andThen readDate('updatedAt)
- )) andThen Json.reads[Thread],
- Json.writes[Thread] andThen (__.json update (
- writeDate('createdAt) andThen writeDate('updatedAt)
- ))
- )
- }
+ import lila.db.dsl.BSONJodaDateTimeHandler
+ import Post.PostBSONHandler
+ private[message] implicit val ThreadBSONHandler =
+ lila.db.BSON.LoggingHandler(lila.log("message")) {
+ reactivemongo.bson.Macros.handler[Thread]
+ }
}
diff --git a/modules/message/src/main/ThreadRepo.scala b/modules/message/src/main/ThreadRepo.scala
index 23ace30ca6..c8570e9f96 100644
--- a/modules/message/src/main/ThreadRepo.scala
+++ b/modules/message/src/main/ThreadRepo.scala
@@ -1,81 +1,75 @@
package lila.message
-import scala.concurrent.Future
+import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
+import reactivemongo.bson._
-import play.api.libs.json.Json
-
-import lila.common.PimpedJson._
-import lila.db.api._
-import lila.db.Implicits._
-import tube.threadTube
+import lila.db.dsl._
object ThreadRepo {
- import play.modules.reactivemongo.json._
+
+ // dirty
+ private val coll = Env.current.threadColl
type ID = String
def byUser(user: ID): Fu[List[Thread]] =
- $find($query(userQuery(user)) sort recentSort)
+ coll.find(userQuery(user)).sort(recentSort).cursor[Thread]().gather[List]()
def visibleByUser(user: ID): Fu[List[Thread]] =
- $find($query(visibleByUserQuery(user)) sort recentSort)
+ coll.find(visibleByUserQuery(user)).sort(recentSort).cursor[Thread]().gather[List]()
def visibleByUser(user: ID, nb: Int): Fu[List[Thread]] =
- $find($query(visibleByUserQuery(user)) sort recentSort, nb)
-
- def userUnreadIds(userId: String): Fu[List[String]] = {
- import reactivemongo.bson._
- import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
- threadTube.coll.aggregate(
- Match(BSONDocument(
- "visibleByUserIds" -> userId,
+ coll.find(visibleByUserQuery(user)).sort(recentSort).cursor[Thread]().gather[List](nb)
+
+ def userUnreadIds(userId: String): Fu[List[String]] = coll.aggregate(
+ Match($doc(
+ "visibleByUserIds" -> userId,
+ "posts.isRead" -> false
+ )),
+ List(
+ Project($doc(
+ "m" -> $doc("$eq" -> BSONArray("$creatorId", userId)),
+ "posts.isByCreator" -> true,
+ "posts.isRead" -> true
+ )),
+ Unwind("posts"),
+ Match($doc(
"posts.isRead" -> false
)),
- List(
- Project(BSONDocument(
- "m" -> BSONDocument("$eq" -> BSONArray("$creatorId", userId)),
- "posts.isByCreator" -> true,
- "posts.isRead" -> true
- )),
- Unwind("posts"),
- Match(BSONDocument(
- "posts.isRead" -> false
- )),
- Project(BSONDocument(
- "u" -> BSONDocument("$ne" -> BSONArray("$posts.isByCreator", "$m"))
- )),
- Match(BSONDocument(
- "u" -> true
- )),
- Group(BSONBoolean(true))("ids" -> AddToSet("_id"))
- ),
- allowDiskUse = false
- ).map {
- _.documents.headOption ?? { ~_.getAs[List[String]]("ids") }
- }
- }
+ Project($doc(
+ "u" -> $doc("$ne" -> BSONArray("$posts.isByCreator", "$m"))
+ )),
+ Match($doc(
+ "u" -> true
+ )),
+ Group(BSONBoolean(true))("ids" -> AddToSet("_id"))
+ ),
+ allowDiskUse = false
+ ).map {
+ _.documents.headOption ?? { ~_.getAs[List[String]]("ids") }
+ }
def setRead(thread: Thread): Funit = {
List.fill(thread.nbUnread) {
- $update(
- $select(thread.id) ++ Json.obj("posts.isRead" -> false),
+ coll.update(
+ $id(thread.id) ++ $doc("posts.isRead" -> false),
$set("posts.$.isRead" -> true)
- )
+ ).void
}
}.sequenceFu.void
def deleteFor(user: ID)(thread: ID) =
- $update($select(thread), $pull("visibleByUserIds", user))
+ coll.update($id(thread), $pull("visibleByUserIds", user)).void
- def reallyDeleteByCreatorId(user: ID) = $remove(Json.obj("creatorId" -> user))
+ def reallyDeleteByCreatorId(user: ID) = coll.remove($doc("creatorId" -> user))
def visibleByUserContainingExists(user: ID, containing: String): Fu[Boolean] =
- $count.exists(visibleByUserQuery(user) ++ Json.obj(
- "posts.0.text" -> $regex(containing)))
+ coll.exists(visibleByUserQuery(user) ++ $doc(
+ "posts.0.text".$regex(containing, "")))
- def userQuery(user: String) = Json.obj("userIds" -> user)
+ def userQuery(user: String) = $doc("userIds" -> user)
- def visibleByUserQuery(user: String) = Json.obj("visibleByUserIds" -> user)
+ def visibleByUserQuery(user: String) = $doc("visibleByUserIds" -> user)
val recentSort = $sort desc "updatedAt"
}
diff --git a/modules/message/src/main/package.scala b/modules/message/src/main/package.scala
index 11df4bd54d..8d108ac8d9 100644
--- a/modules/message/src/main/package.scala
+++ b/modules/message/src/main/package.scala
@@ -1,10 +1,3 @@
package lila
-package object message extends PackageObject with WithPlay {
-
- object tube {
-
- private[message] implicit lazy val threadTube =
- Thread.tube inColl Env.current.threadColl
- }
-}
+package object message extends PackageObject with WithPlay
diff --git a/modules/mod/src/main/AssessApi.scala b/modules/mod/src/main/AssessApi.scala
index 2822fbab0b..b8c01d469c 100644
--- a/modules/mod/src/main/AssessApi.scala
+++ b/modules/mod/src/main/AssessApi.scala
@@ -3,7 +3,7 @@ package lila.mod
import akka.actor.ActorSelection
import lila.analyse.{ Analysis, AnalysisRepo }
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.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)
diff --git a/modules/mod/src/main/Boosting.scala b/modules/mod/src/main/Boosting.scala
index f392475744..1ad9146e39 100644
--- a/modules/mod/src/main/Boosting.scala
+++ b/modules/mod/src/main/Boosting.scala
@@ -2,7 +2,7 @@ package lila.mod
import chess.Color
import chess.variant
-import lila.db.Types.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) ?? {
diff --git a/modules/mod/src/main/Env.scala b/modules/mod/src/main/Env.scala
index 220f10077a..4b89d9bbcf 100644
--- a/modules/mod/src/main/Env.scala
+++ b/modules/mod/src/main/Env.scala
@@ -3,7 +3,7 @@ package lila.mod
import akka.actor._
import com.typesafe.config.Config
-import lila.db.Types.Coll
+import lila.db.dsl.Coll
import lila.security.{ Firewall, UserSpy }
final class Env(
@@ -31,7 +31,7 @@ final class Env(
private[mod] lazy val logColl = db(CollectionModlog)
- lazy val logApi = new ModlogApi
+ lazy val logApi = new ModlogApi(logColl)
lazy val api = new ModApi(
logApi = logApi,
diff --git a/modules/mod/src/main/Gamify.scala b/modules/mod/src/main/Gamify.scala
index 501fd6cbf8..1364004ae6 100644
--- a/modules/mod/src/main/Gamify.scala
+++ b/modules/mod/src/main/Gamify.scala
@@ -6,7 +6,7 @@ import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework
import reactivemongo.bson._
import scala.concurrent.duration._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.memo.AsyncCache
final class Gamify(
@@ -19,10 +19,10 @@ final class Gamify(
def history(orCompute: Boolean = true): Fu[List[HistoryMonth]] = {
val until = DateTime.now minusMonths 1 withDayOfMonth 1
val lastId = HistoryMonth.makeId(until.getYear, until.getMonthOfYear)
- historyColl.find(BSONDocument()).sort(BSONDocument(
+ 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)
@@ -49,7 +49,7 @@ final class Gamify(
}.toList
}.toList.sequenceFu.map(_.flatten).flatMap {
_.map { month =>
- historyColl.update(BSONDocument("_id" -> month._id), month, upsert = true)
+ historyColl.update($doc("_id" -> month._id), month, upsert = true).void
}.sequenceFu
}.void
@@ -73,12 +73,12 @@ final class Gamify(
}
private def dateRange(from: DateTime, toOption: Option[DateTime]) =
- BSONDocument("$gte" -> from) ++ toOption.?? { to => BSONDocument("$lt" -> to) }
+ $doc("$gte" -> from) ++ toOption.?? { to => $doc("$lt" -> to) }
- private val notLichess = BSONDocument("$ne" -> "lichess")
+ private val notLichess = $doc("$ne" -> "lichess")
private def actionLeaderboard(after: DateTime, before: Option[DateTime]): Fu[List[ModCount]] =
- logColl.aggregate(Match(BSONDocument(
+ logColl.aggregate(Match($doc(
"date" -> dateRange(after, before),
"mod" -> notLichess
)), List(
@@ -91,7 +91,7 @@ final class Gamify(
private def reportLeaderboard(after: DateTime, before: Option[DateTime]): Fu[List[ModCount]] =
reportColl.aggregate(
- Match(BSONDocument(
+ Match($doc(
"createdAt" -> dateRange(after, before),
"processedBy" -> notLichess
)), List(
diff --git a/modules/mod/src/main/ModApi.scala b/modules/mod/src/main/ModApi.scala
index 4d90c4f613..44fa7ae965 100644
--- a/modules/mod/src/main/ModApi.scala
+++ b/modules/mod/src/main/ModApi.scala
@@ -1,9 +1,8 @@
package lila.mod
import chess.Color
-import lila.db.api._
+import lila.db.dsl._
import lila.security.{ Firewall, UserSpy, Store => SecurityStore }
-import lila.user.tube.userTube
import lila.user.{ User, UserRepo, LightUserApi }
final class ModApi(
@@ -58,7 +57,7 @@ final class ModApi(
val changed = value != u.troll
val user = u.copy(troll = value)
changed ?? {
- UserRepo.updateTroll(user) >>-
+ UserRepo.updateTroll(user).void >>-
logApi.troll(mod, user.id, user.troll)
} >>-
(reporter ! lila.hub.actorApi.report.MarkTroll(user.id, mod)) inject user.troll
diff --git a/modules/mod/src/main/Modlog.scala b/modules/mod/src/main/Modlog.scala
index 724860eb2b..72324bba23 100644
--- a/modules/mod/src/main/Modlog.scala
+++ b/modules/mod/src/main/Modlog.scala
@@ -66,18 +66,4 @@ object Modlog {
val streamConfig = "streamConfig"
val deleteTeam = "deleteTeam"
val terminateTournament = "terminateTournament "
-
- import lila.db.JsTube
- import JsTube.Helpers._
- import play.api.libs.json._
-
- private[mod] lazy val tube = JsTube[Modlog](
- (__.json update (
- merge(defaults) andThen readDate('date)
- )) andThen Json.reads[Modlog],
- Json.writes[Modlog] andThen (__.json update writeDate('date)),
- flags = Seq(_.NoId)
- )
-
- private def defaults = Json.obj("details" -> none[String])
}
diff --git a/modules/mod/src/main/ModlogApi.scala b/modules/mod/src/main/ModlogApi.scala
index 944fa638bf..41dee7dc80 100644
--- a/modules/mod/src/main/ModlogApi.scala
+++ b/modules/mod/src/main/ModlogApi.scala
@@ -1,11 +1,11 @@
package lila.mod
-import lila.db.api._
-import lila.db.Implicits._
-import tube.modlogTube
-import play.api.libs.json.Json
+import lila.db.dsl._
-final class ModlogApi {
+final class ModlogApi(coll: Coll) {
+
+ import lila.db.BSON.BSONJodaDateTimeHandler
+ private implicit val ModlogBSONHandler = reactivemongo.bson.Macros.handler[Modlog]
def streamConfig(mod: String) = add {
Modlog(mod, none, Modlog.streamConfig)
@@ -86,24 +86,24 @@ final class ModlogApi {
Modlog(mod, none, Modlog.terminateTournament, details = name.some)
}
- def recent = $find($query($select.all) sort $sort.naturalDesc, 100)
+ def recent = coll.find($empty).sort($sort naturalDesc).cursor[Modlog]().gather[List](100)
- def wasUnengined(userId: String) = $count.exists(Json.obj(
+ def wasUnengined(userId: String) = coll.exists($doc(
"user" -> userId,
"action" -> Modlog.unengine
))
- def wasUnbooster(userId: String) = $count.exists(Json.obj(
+ def wasUnbooster(userId: String) = coll.exists($doc(
"user" -> userId,
"action" -> Modlog.unbooster
))
def userHistory(userId: String): Fu[List[Modlog]] =
- $find($query(Json.obj("user" -> userId)) sort $sort.desc("date"), 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()
lila.log("mod").info(m.toString)
- $insert(m)
+ coll.insert(m).void
}
}
diff --git a/modules/mod/src/main/package.scala b/modules/mod/src/main/package.scala
index 6380d96d18..3f7717389d 100644
--- a/modules/mod/src/main/package.scala
+++ b/modules/mod/src/main/package.scala
@@ -1,12 +1,3 @@
package lila
-import lila.db.JsTube
-
-package object mod extends PackageObject with WithPlay {
-
- object tube {
-
- private[mod] implicit lazy val modlogTube =
- Modlog.tube inColl Env.current.logColl
- }
-}
+package object mod extends PackageObject with WithPlay
diff --git a/modules/opening/src/main/Finisher.scala b/modules/opening/src/main/Finisher.scala
index ff73245ec4..b5dcea003e 100644
--- a/modules/opening/src/main/Finisher.scala
+++ b/modules/opening/src/main/Finisher.scala
@@ -2,9 +2,8 @@ package lila.opening
import org.goochjs.glicko2._
import org.joda.time.DateTime
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONDouble }
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.rating.{ Glicko, Perf }
import lila.user.{ User, UserRepo }
@@ -34,13 +33,13 @@ private[opening] final class Finisher(
userRatingDiff = userPerf.intRating - user.perfs.opening.intRating)
((api.attempt add a) >> {
openingColl.update(
- BSONDocument("_id" -> opening.id),
- BSONDocument("$inc" -> BSONDocument(
- Opening.BSONFields.attempts -> BSONInteger(1),
- Opening.BSONFields.wins -> BSONInteger(win ? 1 | 0)
- )) ++ BSONDocument("$set" -> BSONDocument(
- Opening.BSONFields.perf -> Perf.perfBSONHandler.write(openingPerf)
- ))) zip UserRepo.setPerf(user.id, "opening", userPerf)
+ $id(opening.id),
+ $inc(
+ Opening.BSONFields.attempts -> $int(1),
+ Opening.BSONFields.wins -> $int(win ? 1 | 0)
+ ) ++ $set(
+ Opening.BSONFields.perf -> Perf.perfBSONHandler.write(openingPerf)
+ )) zip UserRepo.setPerf(user.id, "opening", userPerf)
}) recover lila.db.recoverDuplicateKey(_ => ()) inject (a -> none)
}
}
diff --git a/modules/opening/src/main/OpeningApi.scala b/modules/opening/src/main/OpeningApi.scala
index 918f3bcdfd..0b8ad8acd0 100644
--- a/modules/opening/src/main/OpeningApi.scala
+++ b/modules/opening/src/main/OpeningApi.scala
@@ -3,11 +3,10 @@ package lila.opening
import scala.util.{ Try, Success, Failure }
import org.joda.time.DateTime
-import play.api.libs.json.JsValue
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONRegex, BSONArray, BSONBoolean }
+import reactivemongo.bson.BSONArray
import reactivemongo.core.commands._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
private[opening] final class OpeningApi(
@@ -21,39 +20,20 @@ private[opening] final class OpeningApi(
object opening {
def find(id: Opening.ID): Fu[Option[Opening]] =
- openingColl.find(BSONDocument("_id" -> id)).one[Opening]
-
- def importOne(json: JsValue, token: String): Fu[Opening.ID] =
- if (token != apiToken) fufail("Invalid API token")
- else {
- import Generated.generatedJSONRead
- Try(json.as[Generated]) match {
- case Failure(err) => fufail(err.getMessage)
- case Success(generated) => generated.toOpening.future flatMap insertOpening
- }
- }
-
- def insertOpening(opening: Opening.ID => Opening): Fu[Opening.ID] =
- lila.db.Util findNextId openingColl flatMap { id =>
- val o = opening(id)
- openingColl.count(BSONDocument("fen" -> o.fen).some) flatMap {
- case 0 => openingColl insert o inject o.id
- case _ => fufail("Duplicate opening")
- }
- }
+ openingColl.byId[Opening](id)
}
object attempt {
def find(openingId: Opening.ID, userId: String): Fu[Option[Attempt]] =
- attemptColl.find(BSONDocument(
+ attemptColl.find($doc(
Attempt.BSONFields.id -> Attempt.makeId(openingId, userId)
- )).one[Attempt]
+ )).uno[Attempt]
def add(a: Attempt) = attemptColl insert a void
def hasPlayed(user: User, opening: Opening): Fu[Boolean] =
- attemptColl.count(BSONDocument(
+ attemptColl.count($doc(
Attempt.BSONFields.id -> Attempt.makeId(opening.id, user.id)
).some) map (0!=)
@@ -62,9 +42,9 @@ private[opening] final class OpeningApi(
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework.{ Group, Limit, Match, Push }
val playedIdsGroup =
- Group(BSONBoolean(true))("ids" -> Push(Attempt.BSONFields.openingId))
+ Group($boolean(true))("ids" -> Push(Attempt.BSONFields.openingId))
- col.aggregate(Match(BSONDocument(Attempt.BSONFields.userId -> user.id)),
+ col.aggregate(Match($doc(Attempt.BSONFields.userId -> user.id)),
List(Limit(max), playedIdsGroup)).map(_.documents.headOption.flatMap(
_.getAs[BSONArray]("ids")).getOrElse(BSONArray()))
}
@@ -72,9 +52,9 @@ private[opening] final class OpeningApi(
object identify {
def apply(fen: String, max: Int): Fu[List[String]] = nameColl.find(
- BSONDocument("_id" -> fen),
- BSONDocument("_id" -> false)
- ).one[BSONDocument] map { obj =>
+ $doc("_id" -> fen),
+ $doc("_id" -> false)
+ ).uno[Bdoc] map { obj =>
~obj.??(_.getAs[List[String]]("names"))
}
}
diff --git a/modules/opening/src/main/Selector.scala b/modules/opening/src/main/Selector.scala
index 2f829d4ba3..f710cce5a1 100644
--- a/modules/opening/src/main/Selector.scala
+++ b/modules/opening/src/main/Selector.scala
@@ -1,12 +1,10 @@
package lila.opening
+import reactivemongo.bson.BSONArray
import scala.concurrent.duration._
import scala.util.Random
-import reactivemongo.api.QueryOpts
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONArray }
-
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.User
private[opening] final class Selector(
@@ -20,9 +18,9 @@ private[opening] final class Selector(
def apply(me: Option[User]): Fu[Opening] = (me match {
case None =>
- openingColl.find(BSONDocument())
- .options(QueryOpts(skipN = Random nextInt anonSkipMax))
- .one[Opening] flatten "Can't find a opening for anon player!"
+ openingColl.find($empty)
+ .skip(Random nextInt anonSkipMax)
+ .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 {
@@ -31,16 +29,15 @@ 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.find(BSONDocument(
- Opening.BSONFields.id -> BSONDocument("$nin" -> ids),
- Opening.BSONFields.rating -> BSONDocument(
- "$gt" -> BSONInteger(user.perfs.opening.intRating - tolerance),
- "$lt" -> BSONInteger(user.perfs.opening.intRating + tolerance)
- )
- )).one[Opening] flatMap {
- case Some(opening) => fuccess(opening)
- case None => if ((tolerance + toleranceStep) <= toleranceMax)
- tryRange(user, tolerance + toleranceStep, ids)
- else fufail(s"Can't find a opening for user $user!")
- }
+ openingColl.uno[Opening]($doc(
+ Opening.BSONFields.id -> $doc("$nin" -> ids),
+ Opening.BSONFields.rating $gt
+ (user.perfs.opening.intRating - tolerance) $lt
+ (user.perfs.opening.intRating + tolerance)
+ )) flatMap {
+ case Some(opening) => fuccess(opening)
+ case None => if ((tolerance + toleranceStep) <= toleranceMax)
+ tryRange(user, tolerance + toleranceStep, ids)
+ else fufail(s"Can't find a opening for user $user!")
+ }
}
diff --git a/modules/opening/src/main/UserInfos.scala b/modules/opening/src/main/UserInfos.scala
index 3af25628d7..5cb1060f02 100644
--- a/modules/opening/src/main/UserInfos.scala
+++ b/modules/opening/src/main/UserInfos.scala
@@ -2,9 +2,8 @@ package lila.opening
import play.api.libs.json._
import reactivemongo.bson._
-import reactivemongo.bson.Macros
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.rating.Glicko
import lila.user.User
@@ -35,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)
diff --git a/modules/perfStat/src/main/PerfStatIndexer.scala b/modules/perfStat/src/main/PerfStatIndexer.scala
index 00b3806541..d523e84252 100644
--- a/modules/perfStat/src/main/PerfStatIndexer.scala
+++ b/modules/perfStat/src/main/PerfStatIndexer.scala
@@ -3,9 +3,8 @@ package lila.perfStat
import akka.actor.ActorRef
import play.api.libs.iteratee._
-import lila.db.api._
-import lila.db.Implicits._
-import lila.game.{ Game, Pov, Query }
+import lila.db.dsl._
+import lila.game.{ Game, GameRepo, Pov, Query }
import lila.hub.Sequencer
import lila.rating.PerfType
import lila.user.User
@@ -21,16 +20,12 @@ final class PerfStatIndexer(storage: PerfStatStorage, sequencer: ActorRef) {
}
private def compute(user: User, perfType: PerfType): Funit = {
- import lila.game.tube.gameTube
- import lila.game.BSONHandlers.gameBSONHandler
- pimpQB($query {
+ GameRepo.sortedCursor(
Query.user(user.id) ++
Query.finished ++
Query.turnsMoreThan(2) ++
- Query.variant(PerfType variantOf perfType)
-
- }).sort(Query.sortChronological)
- .cursor[Game]()
+ Query.variant(PerfType variantOf perfType),
+ Query.sortChronological)
.enumerate(Int.MaxValue, stopOnError = true) |>>>
Iteratee.fold[Game, PerfStat](PerfStat.init(user.id, perfType)) {
case (perfStat, game) if game.perfType.contains(perfType) =>
diff --git a/modules/perfStat/src/main/PerfStatStorage.scala b/modules/perfStat/src/main/PerfStatStorage.scala
index 27b4d47fac..2cbe3b358c 100644
--- a/modules/perfStat/src/main/PerfStatStorage.scala
+++ b/modules/perfStat/src/main/PerfStatStorage.scala
@@ -2,17 +2,13 @@ package lila.perfStat
import org.joda.time.DateTime
import reactivemongo.bson._
-import reactivemongo.bson.Macros
import scala.concurrent.duration._
-import lila.db.BSON._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.rating.PerfType
final class PerfStatStorage(coll: Coll) {
- import lila.db.BSON.BSONJodaDateTimeHandler
- import reactivemongo.bson.Macros
implicit val PerfTypeBSONHandler = new BSONHandler[BSONInteger, PerfType] {
def read(b: BSONInteger) = PerfType.byId get b.value err s"Invalid perf type id ${b.value}"
def write(p: PerfType) = BSONInteger(p.id)
@@ -33,10 +29,10 @@ final class PerfStatStorage(coll: Coll) {
private implicit val PerfStatBSONHandler = Macros.handler[PerfStat]
def find(userId: String, perfType: PerfType): Fu[Option[PerfStat]] =
- coll.find(BSONDocument("_id" -> PerfStat.makeId(userId, perfType))).one[PerfStat]
+ coll.byId[PerfStat](PerfStat.makeId(userId, perfType))
def update(perfStat: PerfStat): Funit =
- coll.update(BSONDocument("_id" -> perfStat.id), perfStat).void
+ coll.update($id(perfStat.id), perfStat).void
def insert(perfStat: PerfStat): Funit =
coll.insert(perfStat).void
diff --git a/modules/playban/src/main/PlaybanApi.scala b/modules/playban/src/main/PlaybanApi.scala
index 934db021c4..7d4e12fb3d 100644
--- a/modules/playban/src/main/PlaybanApi.scala
+++ b/modules/playban/src/main/PlaybanApi.scala
@@ -8,7 +8,7 @@ import scala.concurrent.duration._
import chess.Color
import lila.db.BSON._
-import lila.db.Types.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)
)
diff --git a/modules/pref/src/main/Pref.scala b/modules/pref/src/main/Pref.scala
index 5ad36c7164..d9b4e68186 100644
--- a/modules/pref/src/main/Pref.scala
+++ b/modules/pref/src/main/Pref.scala
@@ -1,7 +1,5 @@
package lila.pref
-import lila.db.JsTube
-import lila.db.JsTube.Helpers._
import lila.user.User
case class Pref(
diff --git a/modules/pref/src/main/PrefApi.scala b/modules/pref/src/main/PrefApi.scala
index 20791731e5..2edba2529b 100644
--- a/modules/pref/src/main/PrefApi.scala
+++ b/modules/pref/src/main/PrefApi.scala
@@ -4,7 +4,7 @@ import play.api.libs.json.Json
import scala.concurrent.duration.Duration
import lila.db.BSON
-import lila.db.Types._
+import lila.db.dsl._
import lila.hub.actorApi.SendTo
import lila.memo.AsyncCache
import lila.user.User
@@ -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
}
diff --git a/modules/pref/src/main/package.scala b/modules/pref/src/main/package.scala
index ea62cd2383..0830ba1a17 100644
--- a/modules/pref/src/main/package.scala
+++ b/modules/pref/src/main/package.scala
@@ -1,5 +1,3 @@
package lila
-import lila.db.JsTube
-
package object pref extends PackageObject with WithPlay
diff --git a/modules/push/src/main/DeviceApi.scala b/modules/push/src/main/DeviceApi.scala
index 03c586b333..c2bc851717 100644
--- a/modules/push/src/main/DeviceApi.scala
+++ b/modules/push/src/main/DeviceApi.scala
@@ -3,8 +3,7 @@ package lila.push
import org.joda.time.DateTime
import reactivemongo.bson._
-import lila.db.BSON._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.User
private final class DeviceApi(coll: Coll) {
@@ -12,20 +11,20 @@ private final class DeviceApi(coll: Coll) {
private implicit val DeviceBSONHandler = Macros.handler[Device]
private[push] def findByDeviceId(deviceId: String): Fu[Option[Device]] =
- coll.find(BSONDocument("_id" -> deviceId)).one[Device]
+ coll.find($id(deviceId)).uno[Device]
private[push] def findByUserId(userId: String): Fu[List[Device]] =
- coll.find(BSONDocument("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(BSONDocument(
+ coll.find($doc(
"platform" -> platform,
"userId" -> userId
- )).sort(BSONDocument("seenAt" -> -1)).one[Device]
+ )).sort($doc("seenAt" -> -1)).uno[Device]
def register(user: User, platform: String, deviceId: String) = {
lila.mon.push.register.in(platform)()
- coll.update(BSONDocument("_id" -> deviceId), Device(
+ coll.update($id(deviceId), Device(
_id = deviceId,
platform = platform,
userId = user.id,
@@ -35,6 +34,6 @@ private final class DeviceApi(coll: Coll) {
def unregister(user: User) = {
lila.mon.push.register.out()
- coll.remove(BSONDocument("userId" -> user.id)).void
+ coll.remove($doc("userId" -> user.id)).void
}
}
diff --git a/modules/puzzle/src/main/Daily.scala b/modules/puzzle/src/main/Daily.scala
index 677a285b50..ce0ed51286 100644
--- a/modules/puzzle/src/main/Daily.scala
+++ b/modules/puzzle/src/main/Daily.scala
@@ -5,10 +5,8 @@ import scala.concurrent.duration._
import akka.actor.{ ActorSelection, Scheduler }
import akka.pattern.ask
import org.joda.time.DateTime
-import reactivemongo.bson.BSONDocument
-import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.Coll
+import lila.db.dsl._
private[puzzle] final class Daily(
coll: Coll,
@@ -45,15 +43,15 @@ private[puzzle] final class Daily(
}
private def findCurrent = coll.find(
- BSONDocument("day" -> BSONDocument("$gt" -> DateTime.now.minusMinutes(24 * 60 - 15)))
- ).one[Puzzle]
+ $doc("day" -> $doc("$gt" -> DateTime.now.minusMinutes(24 * 60 - 15)))
+ ).uno[Puzzle]
private def findNew = coll.find(
- BSONDocument("day" -> BSONDocument("$exists" -> false))
- ).sort(BSONDocument("vote.sum" -> -1)).one[Puzzle] flatMap {
+ $doc("day" -> $doc("$exists" -> false))
+ ).sort($doc("vote.sum" -> -1)).uno[Puzzle] flatMap {
case Some(puzzle) => coll.update(
- BSONDocument("_id" -> puzzle.id),
- BSONDocument("$set" -> BSONDocument("day" -> DateTime.now))
+ $doc("_id" -> puzzle.id),
+ $doc("$set" -> $doc("day" -> DateTime.now))
) inject puzzle.some
case None => fuccess(none)
}
diff --git a/modules/puzzle/src/main/Finisher.scala b/modules/puzzle/src/main/Finisher.scala
index d2587c4328..43c51c560a 100644
--- a/modules/puzzle/src/main/Finisher.scala
+++ b/modules/puzzle/src/main/Finisher.scala
@@ -2,9 +2,8 @@ package lila.puzzle
import org.goochjs.glicko2._
import org.joda.time.DateTime
-import reactivemongo.bson.{ BSONDocument, BSONInteger }
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.rating.{ Glicko, Perf }
import lila.user.{ User, UserRepo }
@@ -38,13 +37,13 @@ private[puzzle] final class Finisher(
userRatingDiff = userPerf.intRating - user.perfs.puzzle.intRating)
((api.attempt add a) >> {
puzzleColl.update(
- BSONDocument("_id" -> puzzle.id),
- BSONDocument("$inc" -> BSONDocument(
- Puzzle.BSONFields.attempts -> BSONInteger(1),
- Puzzle.BSONFields.wins -> BSONInteger(data.isWin ? 1 | 0)
- )) ++ BSONDocument("$set" -> BSONDocument(
+ $id(puzzle.id),
+ $inc(
+ Puzzle.BSONFields.attempts -> $int(1),
+ Puzzle.BSONFields.wins -> $int(data.isWin ? 1 | 0)
+ ) ++ $set(
Puzzle.BSONFields.perf -> Perf.perfBSONHandler.write(puzzlePerf)
- ))) zip UserRepo.setPerf(user.id, "puzzle", userPerf)
+ )) zip UserRepo.setPerf(user.id, "puzzle", userPerf)
}) recover lila.db.recoverDuplicateKey(_ => ()) inject (a -> none)
}
diff --git a/modules/puzzle/src/main/PuzzleApi.scala b/modules/puzzle/src/main/PuzzleApi.scala
index 525925f213..9020e0aa5c 100644
--- a/modules/puzzle/src/main/PuzzleApi.scala
+++ b/modules/puzzle/src/main/PuzzleApi.scala
@@ -5,9 +5,9 @@ import scala.util.{ Try, Success, Failure }
import org.joda.time.DateTime
import play.api.libs.json.JsValue
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONRegex, BSONArray, BSONBoolean }
+import reactivemongo.bson. BSONArray
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
private[puzzle] final class PuzzleApi(
@@ -20,13 +20,13 @@ private[puzzle] final class PuzzleApi(
object puzzle {
def find(id: PuzzleId): Fu[Option[Puzzle]] =
- puzzleColl.find(BSONDocument("_id" -> id)).one[Puzzle]
+ puzzleColl.find($doc("_id" -> id)).uno[Puzzle]
def latest(nb: Int): Fu[List[Puzzle]] =
- puzzleColl.find(BSONDocument())
- .sort(BSONDocument("date" -> -1))
+ 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")
@@ -43,8 +43,8 @@ private[puzzle] final class PuzzleApi(
case Success(puzzle) :: rest => lila.db.Util findNextId puzzleColl flatMap { id =>
val p = puzzle(id)
val fenStart = p.fen.split(' ').take(2).mkString(" ")
- puzzleColl.count(BSONDocument(
- "fen" -> BSONRegex(fenStart.replace("/", "\\/"), "")
+ puzzleColl.count($doc(
+ "fen".$regex(fenStart.replace("/", "\\/"), "")
).some) flatMap {
case 0 => (puzzleColl insert p) >> {
insertPuzzles(rest) map (Success(id) :: _)
@@ -55,24 +55,24 @@ private[puzzle] final class PuzzleApi(
}
def export(nb: Int): Fu[List[Puzzle]] = List(true, false).map { mate =>
- puzzleColl.find(BSONDocument("mate" -> mate))
- .sort(BSONDocument(Puzzle.BSONFields.voteSum -> -1))
- .cursor[Puzzle]().collect[List](nb / 2)
+ puzzleColl.find($doc("mate" -> mate))
+ .sort($doc(Puzzle.BSONFields.voteSum -> -1))
+ .cursor[Puzzle]().gather[List](nb / 2)
}.sequenceFu.map(_.flatten)
def disable(id: PuzzleId): Funit =
puzzleColl.update(
- BSONDocument("_id" -> id),
- BSONDocument("$set" -> BSONDocument(Puzzle.BSONFields.vote -> Vote.disable))
+ $doc("_id" -> id),
+ $doc("$set" -> $doc(Puzzle.BSONFields.vote -> Vote.disable))
).void
}
object attempt {
def find(puzzleId: PuzzleId, userId: String): Fu[Option[Attempt]] =
- attemptColl.find(BSONDocument(
+ 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}")
@@ -83,11 +83,11 @@ private[puzzle] final class PuzzleApi(
}
val a2 = a1.copy(vote = v.some)
attemptColl.update(
- BSONDocument("_id" -> a2.id),
- BSONDocument("$set" -> BSONDocument(Attempt.BSONFields.vote -> v))) zip
+ $doc("_id" -> a2.id),
+ $doc("$set" -> $doc(Attempt.BSONFields.vote -> v))) zip
puzzleColl.update(
- BSONDocument("_id" -> p2.id),
- BSONDocument("$set" -> BSONDocument(Puzzle.BSONFields.vote -> p2.vote))) map {
+ $doc("_id" -> p2.id),
+ $doc("$set" -> $doc(Puzzle.BSONFields.vote -> p2.vote))) map {
case _ => p2 -> a2
}
}
@@ -95,23 +95,23 @@ private[puzzle] final class PuzzleApi(
def add(a: Attempt) = attemptColl insert a void
def hasPlayed(user: User, puzzle: Puzzle): Fu[Boolean] =
- attemptColl.count(BSONDocument(
+ attemptColl.count($doc(
Attempt.BSONFields.id -> Attempt.makeId(puzzle.id, user.id)
).some) map (0!=)
def playedIds(user: User, max: Int): Fu[BSONArray] =
attemptColl.distinct(Attempt.BSONFields.puzzleId,
- BSONDocument(Attempt.BSONFields.userId -> user.id).some
+ $doc(Attempt.BSONFields.userId -> user.id).some
) map BSONArray.apply
def hasVoted(user: User): Fu[Boolean] = attemptColl.find(
- BSONDocument(Attempt.BSONFields.userId -> user.id),
- BSONDocument(
+ $doc(Attempt.BSONFields.userId -> user.id),
+ $doc(
Attempt.BSONFields.vote -> true,
Attempt.BSONFields.id -> false
- )).sort(BSONDocument(Attempt.BSONFields.date -> -1))
- .cursor[BSONDocument]()
- .collect[List](5) map {
+ )).sort($doc(Attempt.BSONFields.date -> -1))
+ .cursor[Bdoc]()
+ .gather[List](5) map {
case attempts if attempts.size < 5 => true
case attempts => attempts.foldLeft(false) {
case (true, _) => true
diff --git a/modules/puzzle/src/main/Selector.scala b/modules/puzzle/src/main/Selector.scala
index 5d2151e96e..52a88f2795 100644
--- a/modules/puzzle/src/main/Selector.scala
+++ b/modules/puzzle/src/main/Selector.scala
@@ -3,10 +3,7 @@ package lila.puzzle
import scala.concurrent.duration._
import scala.util.Random
-import reactivemongo.api.QueryOpts
-import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONArray }
-
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.User
private[puzzle] final class Selector(
@@ -15,10 +12,10 @@ private[puzzle] final class Selector(
anonMinRating: Int,
maxAttempts: Int) {
- private def popularSelector(mate: Boolean) = BSONDocument(
- Puzzle.BSONFields.voteSum -> BSONDocument("$gt" -> BSONInteger(mate.fold(anonMinRating, 0))))
+ private def popularSelector(mate: Boolean) = $doc(
+ Puzzle.BSONFields.voteSum $gt mate.fold(anonMinRating, 0))
- private def mateSelector(mate: Boolean) = BSONDocument("mate" -> mate)
+ private def mateSelector(mate: Boolean) = $doc("mate" -> mate)
private def difficultyDecay(difficulty: Int) = difficulty match {
case 1 => -200
@@ -36,8 +33,8 @@ private[puzzle] final class Selector(
me match {
case None =>
puzzleColl.find(popularSelector(isMate) ++ mateSelector(isMate))
- .options(QueryOpts(skipN = Random nextInt anonSkipMax))
- .one[Puzzle]
+ .skip(Random nextInt anonSkipMax)
+ .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
@@ -55,15 +52,14 @@ private[puzzle] final class Selector(
case d => 200
}
- private def tryRange(rating: Int, tolerance: Int, step: Int, decay: Int, ids: BSONArray, isMate: Boolean): Fu[Option[Puzzle]] =
- puzzleColl.find(mateSelector(isMate) ++ BSONDocument(
- Puzzle.BSONFields.id -> BSONDocument("$nin" -> ids),
- Puzzle.BSONFields.rating -> BSONDocument(
- "$gt" -> BSONInteger(rating - tolerance + decay),
- "$lt" -> BSONInteger(rating + tolerance + decay)
- )
- )).sort(BSONDocument(Puzzle.BSONFields.voteSum -> -1))
- .one[Puzzle] flatMap {
+ private def tryRange(rating: Int, tolerance: Int, step: Int, decay: Int, ids: Barr, isMate: Boolean): Fu[Option[Puzzle]] =
+ puzzleColl.find(mateSelector(isMate) ++ $doc(
+ Puzzle.BSONFields.id -> $doc("$nin" -> ids),
+ Puzzle.BSONFields.rating $gt
+ (rating - tolerance + decay) $lt
+ (rating + tolerance + decay)
+ )).sort($sort desc Puzzle.BSONFields.voteSum)
+ .uno[Puzzle] flatMap {
case None if (tolerance + step) <= toleranceMax =>
tryRange(rating, tolerance + step, step, decay, ids, isMate)
case res => fuccess(res)
diff --git a/modules/puzzle/src/main/UserInfos.scala b/modules/puzzle/src/main/UserInfos.scala
index 710dbc020c..6bc4339703 100644
--- a/modules/puzzle/src/main/UserInfos.scala
+++ b/modules/puzzle/src/main/UserInfos.scala
@@ -3,7 +3,7 @@ package lila.puzzle
import play.api.libs.json._
import reactivemongo.bson._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.rating.Glicko
import lila.user.User
@@ -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)
diff --git a/modules/qa/src/main/QaApi.scala b/modules/qa/src/main/QaApi.scala
index 1ceff699b9..78482ace68 100644
--- a/modules/qa/src/main/QaApi.scala
+++ b/modules/qa/src/main/QaApi.scala
@@ -8,9 +8,8 @@ import org.joda.time.DateTime
import spray.caching.{ LruCache, Cache }
import lila.common.paginator._
-import lila.db.BSON._
import lila.db.paginator._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
final class QaApi(
@@ -51,34 +50,34 @@ final class QaApi(
def edit(data: QuestionData, id: QuestionId): Fu[Option[Question]] = findById(id) flatMap {
_ ?? { q =>
val q2 = q.copy(title = data.title, body = data.body, tags = data.tags).editNow
- questionColl.update(BSONDocument("_id" -> q2.id), q2) >>
+ questionColl.update($doc("_id" -> q2.id), q2) >>
tag.clearCache >>
relation.clearCache inject q2.some
}
}
def findById(id: QuestionId): Fu[Option[Question]] =
- questionColl.find(BSONDocument("_id" -> id)).one[Question]
+ questionColl.find($doc("_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($doc("_id" -> $doc("$in" -> ids.distinct))).cursor[Question]().gather[List]()
def accept(q: Question) = questionColl.update(
- BSONDocument("_id" -> q.id),
- BSONDocument("$set" -> BSONDocument("acceptedAt" -> DateTime.now))
+ $doc("_id" -> q.id),
+ $doc("$set" -> $doc("acceptedAt" -> DateTime.now))
)
def count: Fu[Int] = questionColl.count(None)
def recentPaginator(page: Int, perPage: Int): Fu[Paginator[Question]] =
- paginator(BSONDocument(), BSONDocument("createdAt" -> -1), page, perPage)
+ paginator($empty, $doc("createdAt" -> -1), page, perPage)
- private def paginator(selector: BSONDocument, sort: BSONDocument, page: Int, perPage: Int): Fu[Paginator[Question]] =
+ private def paginator(selector: Bdoc, sort: Bdoc, page: Int, perPage: Int): Fu[Paginator[Question]] =
Paginator(
- adapter = new BSONAdapter[Question](
+ adapter = new Adapter[Question](
collection = questionColl,
selector = selector,
- projection = BSONDocument(),
+ projection = $empty,
sort = sort
),
currentPage = page,
@@ -86,62 +85,62 @@ final class QaApi(
private def popularCache = mongoCache(
prefix = "qa:popular",
- f = (nb: Int) => questionColl.find(BSONDocument())
- .sort(BSONDocument("vote.score" -> -1))
- .cursor[Question]().collect[List](nb),
+ f = (nb: Int) => questionColl.find($empty)
+ .sort($doc("vote.score" -> -1))
+ .cursor[Question]().gather[List](nb),
timeToLive = 3 hour)
def popular(max: Int): Fu[List[Question]] = popularCache(max)
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)
+ questionColl.find($doc("tags" -> tag.toLowerCase))
+ .sort($doc("vote.score" -> -1))
+ .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($doc("tags" -> $doc("$in" -> tags.map(_.toLowerCase)))).cursor[Question]().gather[List](max)
def addComment(c: Comment)(q: Question) = questionColl.update(
- BSONDocument("_id" -> q.id),
- BSONDocument("$push" -> BSONDocument("comments" -> c)))
+ $doc("_id" -> q.id),
+ $doc("$push" -> $doc("comments" -> c)))
def vote(id: QuestionId, user: User, v: Boolean): Fu[Option[Vote]] =
question findById id flatMap {
_ ?? { q =>
val newVote = q.vote.add(user.id, v)
questionColl.update(
- BSONDocument("_id" -> q.id),
- BSONDocument("$set" -> BSONDocument("vote" -> newVote))
+ $doc("_id" -> q.id),
+ $doc("$set" -> $doc("vote" -> newVote))
) inject newVote.some
}
}
def incViews(q: Question) = questionColl.update(
- BSONDocument("_id" -> q.id),
- BSONDocument("$inc" -> BSONDocument("views" -> BSONInteger(1))))
+ $doc("_id" -> q.id),
+ $doc("$inc" -> $doc("views" -> BSONInteger(1))))
def recountAnswers(id: QuestionId) = answer.countByQuestionId(id) flatMap {
setAnswers(id, _)
}
def setAnswers(id: QuestionId, nb: Int) = questionColl.update(
- BSONDocument("_id" -> id),
- BSONDocument(
- "$set" -> BSONDocument(
+ $doc("_id" -> id),
+ $doc(
+ "$set" -> $doc(
"answers" -> BSONInteger(nb),
"updatedAt" -> DateTime.now
)
)).void
def remove(id: QuestionId) =
- questionColl.remove(BSONDocument("_id" -> id)) >>
+ questionColl.remove($doc("_id" -> id)) >>
(answer removeByQuestion id) >>
tag.clearCache >>
relation.clearCache
def removeComment(id: QuestionId, c: CommentId) = questionColl.update(
- BSONDocument("_id" -> id),
- BSONDocument("$pull" -> BSONDocument("comments" -> BSONDocument("id" -> c)))
+ $doc("_id" -> id),
+ $doc("$pull" -> $doc("comments" -> $doc("id" -> c)))
)
}
@@ -172,26 +171,26 @@ final class QaApi(
def edit(body: String, id: AnswerId): Fu[Option[Answer]] = findById(id) flatMap {
_ ?? { a =>
val a2 = a.copy(body = body).editNow
- answerColl.update(BSONDocument("_id" -> a2.id), a2) inject a2.some
+ answerColl.update($doc("_id" -> a2.id), a2) inject a2.some
}
}
def findById(id: AnswerId): Fu[Option[Answer]] =
- answerColl.find(BSONDocument("_id" -> id)).one[Answer]
+ answerColl.find($doc("_id" -> id)).uno[Answer]
def accept(q: Question, a: Answer) = (question accept q) >> answerColl.update(
- BSONDocument("questionId" -> q.id),
- BSONDocument("$unset" -> BSONDocument("acceptedAt" -> true)),
+ $doc("questionId" -> q.id),
+ $doc("$unset" -> $doc("acceptedAt" -> true)),
multi = true
) >> answerColl.update(
- BSONDocument("_id" -> a.id),
- BSONDocument("$set" -> BSONDocument("acceptedAt" -> DateTime.now))
+ $doc("_id" -> a.id),
+ $doc("$set" -> $doc("acceptedAt" -> DateTime.now))
)
def popular(questionId: QuestionId): Fu[List[Answer]] =
- answerColl.find(BSONDocument("questionId" -> questionId))
- .sort(BSONDocument("vote.score" -> -1))
- .cursor[Answer]().collect[List]()
+ answerColl.find($doc("questionId" -> questionId))
+ .sort($doc("vote.score" -> -1))
+ .cursor[Answer]().gather[List]()
def zipWithQuestions(answers: List[Answer]): Fu[List[AnswerWithQuestion]] =
question.findByIds(answers.map(_.questionId)) map { qs =>
@@ -201,32 +200,32 @@ final class QaApi(
}
def addComment(c: Comment)(a: Answer) = answerColl.update(
- BSONDocument("_id" -> a.id),
- BSONDocument("$push" -> BSONDocument("comments" -> c)))
+ $doc("_id" -> a.id),
+ $doc("$push" -> $doc("comments" -> c)))
def vote(id: QuestionId, user: User, v: Boolean): Fu[Option[Vote]] =
answer findById id flatMap {
_ ?? { a =>
val newVote = a.vote.add(user.id, v)
answerColl.update(
- BSONDocument("_id" -> a.id),
- BSONDocument("$set" -> BSONDocument("vote" -> newVote))
+ $doc("_id" -> a.id),
+ $doc("$set" -> $doc("vote" -> newVote))
) inject newVote.some
}
}
def remove(a: Answer): Fu[Unit] =
- answerColl.remove(BSONDocument("_id" -> a.id)) >>
+ answerColl.remove($doc("_id" -> a.id)) >>
(question recountAnswers a.questionId).void
def remove(id: AnswerId): Fu[Unit] = findById(id) flatMap { _ ?? remove }
def removeByQuestion(id: QuestionId) =
- answerColl.remove(BSONDocument("questionId" -> id))
+ answerColl.remove($doc("questionId" -> id))
def removeComment(id: QuestionId, c: CommentId) = answerColl.update(
- BSONDocument("questionId" -> id),
- BSONDocument("$pull" -> BSONDocument("comments" -> BSONDocument("id" -> c))),
+ $doc("questionId" -> id),
+ $doc("$pull" -> $doc("comments" -> $doc("id" -> c))),
multi = true)
def moveToQuestionComment(a: Answer, q: Question) = {
@@ -251,7 +250,7 @@ final class QaApi(
}
def countByQuestionId(id: QuestionId) =
- answerColl.count(Some(BSONDocument("questionId" -> id)))
+ answerColl.count(Some($doc("questionId" -> id)))
}
object comment {
@@ -291,7 +290,7 @@ final class QaApi(
val col = questionColl
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework.{ AddToSet, Group, Project, Unwind }
- col.aggregate(Project(BSONDocument("tags" -> BSONBoolean(true))), List(
+ col.aggregate(Project($doc("tags" -> BSONBoolean(true))), List(
Unwind("tags"), Group(BSONBoolean(true))("tags" -> AddToSet("tags")))).
map(_.documents.headOption.flatMap(_.getAs[List[String]]("tags")).
getOrElse(List.empty[String]).map(_.toLowerCase).distinct)
diff --git a/modules/qa/src/main/Search.scala b/modules/qa/src/main/Search.scala
index ce8de51363..35ac32c8ec 100644
--- a/modules/qa/src/main/Search.scala
+++ b/modules/qa/src/main/Search.scala
@@ -3,8 +3,7 @@ package lila.qa
import reactivemongo.bson._
import reactivemongo.core.commands._
-import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.Coll
+import lila.db.dsl._
final class Search(collection: Coll) {
@@ -38,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]()
}
diff --git a/modules/rating/src/main/Glicko.scala b/modules/rating/src/main/Glicko.scala
index 4e42cac5cc..63f6d807f2 100644
--- a/modules/rating/src/main/Glicko.scala
+++ b/modules/rating/src/main/Glicko.scala
@@ -67,6 +67,4 @@ case object Glicko {
case object Loss extends Result(0) { def negate = Win }
case object Draw extends Result(0.5) { def negate = Draw }
}
-
- lazy val tube = lila.db.BsTube(glickoBSONHandler)
}
diff --git a/modules/relation/src/main/Env.scala b/modules/relation/src/main/Env.scala
index 11baae8c20..5a14e0eef1 100644
--- a/modules/relation/src/main/Env.scala
+++ b/modules/relation/src/main/Env.scala
@@ -25,8 +25,10 @@ final class Env(
}
import settings._
+ private[relation] val coll = db(CollectionRelation)
+
lazy val api = new RelationApi(
- coll = relationColl,
+ coll = coll,
actor = hub.actor.relation,
bus = system.lilaBus,
timeline = hub.actor.timeline,
@@ -50,8 +52,6 @@ final class Env(
}
}
}
-
- private[relation] lazy val relationColl = db(CollectionRelation)
}
object Env {
diff --git a/modules/relation/src/main/RelationApi.scala b/modules/relation/src/main/RelationApi.scala
index a21da70e8d..3cbafc9d7f 100644
--- a/modules/relation/src/main/RelationApi.scala
+++ b/modules/relation/src/main/RelationApi.scala
@@ -4,14 +4,11 @@ import akka.actor.ActorSelection
import scala.concurrent.duration._
import scala.util.Success
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
import lila.hub.actorApi.timeline.{ Propagate, Follow => FollowUser }
import lila.memo.AsyncCache
-import lila.user.tube.userTube
import lila.user.{ User => UserModel, UserRepo }
-import tube.relationTube
import BSONHandlers._
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
@@ -30,9 +27,9 @@ final class RelationApi(
import RelationRepo.makeId
def fetchRelation(u1: ID, u2: ID): Fu[Option[Relation]] = coll.find(
- BSONDocument("u1" -> u1, "u2" -> u2),
- BSONDocument("r" -> true, "_id" -> false)
- ).one[BSONDocument].map {
+ $doc("u1" -> u1, "u2" -> u2),
+ $doc("r" -> true, "_id" -> false)
+ ).uno[BSONDocument].map {
_.flatMap(_.getAs[Boolean]("r"))
}
@@ -42,64 +39,64 @@ final class RelationApi(
def fetchBlocking = RelationRepo blocking _
- def fetchFriends(userId: ID) = coll.aggregate(Match(BSONDocument(
- "$or" -> BSONArray(BSONDocument("u1" -> userId), BSONDocument("u2" -> userId)),
+ def fetchFriends(userId: ID) = coll.aggregate(Match($doc(
+ "$or" -> BSONArray($doc("u1" -> userId), $doc("u2" -> userId)),
"r" -> Follow
)), List(
Group(BSONNull)(
"u1" -> AddToSet("u1"),
"u2" -> AddToSet("u2")),
- Project(BSONDocument(
- "_id" -> BSONDocument("$setIntersection" -> BSONArray("$u1", "$u2"))
+ Project($doc(
+ "_id" -> $doc("$setIntersection" -> BSONArray("$u1", "$u2"))
))
)).map {
~_.documents.headOption.flatMap(_.getAs[Set[String]]("_id")) - userId
}
def fetchFollows(u1: ID, u2: ID) =
- coll.count(BSONDocument("_id" -> makeId(u1, u2), "r" -> Follow).some).map(0!=)
+ coll.count($doc("_id" -> makeId(u1, u2), "r" -> Follow).some).map(0!=)
def fetchBlocks(u1: ID, u2: ID) =
- coll.count(BSONDocument("_id" -> makeId(u1, u2), "r" -> Block).some).map(0!=)
+ coll.count($doc("_id" -> makeId(u1, u2), "r" -> Block).some).map(0!=)
def fetchAreFriends(u1: ID, u2: ID) =
fetchFollows(u1, u2) flatMap { _ ?? fetchFollows(u2, u1) }
private val countFollowingCache = AsyncCache[ID, Int](
- f = userId => coll.count(BSONDocument("u1" -> userId, "r" -> Follow).some),
+ f = userId => coll.count($doc("u1" -> userId, "r" -> Follow).some),
timeToLive = 10 minutes)
def countFollowing(userId: ID) = countFollowingCache(userId)
private val countFollowersCache = AsyncCache[ID, Int](
- f = userId => coll.count(BSONDocument("u2" -> userId, "r" -> Follow).some),
+ f = userId => coll.count($doc("u2" -> userId, "r" -> Follow).some),
timeToLive = 10 minutes)
def countFollowers(userId: ID) = countFollowersCache(userId)
def countBlocking(userId: ID) =
- coll.count(BSONDocument("u1" -> userId, "r" -> Block).some)
+ coll.count($doc("u1" -> userId, "r" -> Block).some)
def countBlockers(userId: ID) =
- coll.count(BSONDocument("u2" -> userId, "r" -> Block).some)
+ coll.count($doc("u2" -> userId, "r" -> Block).some)
- def followingPaginatorAdapter(userId: ID) = new BSONAdapter[Followed](
+ def followingPaginatorAdapter(userId: ID) = new Adapter[Followed](
collection = coll,
- selector = BSONDocument("u1" -> userId, "r" -> Follow),
- projection = BSONDocument("u2" -> true, "_id" -> false),
- sort = BSONDocument()).map(_.userId)
+ selector = $doc("u1" -> userId, "r" -> Follow),
+ projection = $doc("u2" -> true, "_id" -> false),
+ sort = $empty).map(_.userId)
- def followersPaginatorAdapter(userId: ID) = new BSONAdapter[Follower](
+ def followersPaginatorAdapter(userId: ID) = new Adapter[Follower](
collection = coll,
- selector = BSONDocument("u2" -> userId, "r" -> Follow),
- projection = BSONDocument("u1" -> true, "_id" -> false),
- sort = BSONDocument()).map(_.userId)
+ selector = $doc("u2" -> userId, "r" -> Follow),
+ projection = $doc("u1" -> true, "_id" -> false),
+ sort = $empty).map(_.userId)
- def blockingPaginatorAdapter(userId: ID) = new BSONAdapter[Blocked](
+ def blockingPaginatorAdapter(userId: ID) = new Adapter[Blocked](
collection = coll,
- selector = BSONDocument("u1" -> userId, "r" -> Block),
- projection = BSONDocument("u2" -> true, "_id" -> false),
- sort = BSONDocument()).map(_.userId)
+ selector = $doc("u1" -> userId, "r" -> Block),
+ projection = $doc("u2" -> true, "_id" -> false),
+ sort = $empty).map(_.userId)
def follow(u1: ID, u2: ID): Funit =
if (u1 == u2) funit
diff --git a/modules/relation/src/main/RelationRepo.scala b/modules/relation/src/main/RelationRepo.scala
index 2e85ff5312..a4c1d8e484 100644
--- a/modules/relation/src/main/RelationRepo.scala
+++ b/modules/relation/src/main/RelationRepo.scala
@@ -1,16 +1,13 @@
package lila.relation
-import play.api.libs.json._
import reactivemongo.bson._
-import lila.common.PimpedJson._
-import lila.db.api._
-import lila.db.Implicits._
-import tube.relationTube
+import lila.db.dsl._
private[relation] object RelationRepo {
- val coll = relationTube.coll
+ // dirty
+ private val coll = Env.current.coll
def followers(userId: ID) = relaters(userId, Follow)
def following(userId: ID) = relating(userId, Follow)
@@ -35,24 +32,26 @@ private[relation] object RelationRepo {
def block(u1: ID, u2: ID): Funit = save(u1, u2, Block)
def unblock(u1: ID, u2: ID): Funit = remove(u1, u2)
- def unfollowAll(u1: ID): Funit = $remove(Json.obj("u1" -> u1))
+ def unfollowAll(u1: ID): Funit = coll.remove($doc("u1" -> u1)).void
- private def save(u1: ID, u2: ID, relation: Relation): Funit = $save(
- makeId(u1, u2),
- Json.obj("u1" -> u1, "u2" -> u2, "r" -> relation)
- )
+ private def save(u1: ID, u2: ID, relation: Relation): Funit = coll.update(
+ $id(makeId(u1, u2)),
+ $doc("u1" -> u1, "u2" -> u2, "r" -> relation),
+ upsert = true).void
- def remove(u1: ID, u2: ID): Funit = $remove byId makeId(u1, u2)
+ def remove(u1: ID, u2: ID): Funit = coll.remove($id(makeId(u1, u2))).void
def drop(userId: ID, relation: Relation, nb: Int) =
- $primitive(
- Json.obj("u1" -> userId, "r" -> relation),
- "_id",
- _ sort $sort.naturalAsc,
- max = nb.some,
- hint = reactivemongo.bson.BSONDocument("u1" -> 1)
- )(_.asOpt[String]) flatMap { ids =>
- $remove(Json.obj("_id" -> $in(ids)))
+ coll.find(
+ $doc("u1" -> userId, "r" -> relation),
+ $doc("_id" -> true)
+ ).sort($sort.naturalAsc)
+ .hint($doc("u1" -> 1))
+ .cursor[Bdoc]()
+ .gather[List](nb).map {
+ _.flatMap { _.getAs[String]("_id") }
+ } flatMap { ids =>
+ coll.remove($inIds(ids)).void
}
def makeId(u1: String, u2: String) = s"$u1/$u2"
diff --git a/modules/relation/src/main/package.scala b/modules/relation/src/main/package.scala
index b71cb067d0..51e3c680dc 100644
--- a/modules/relation/src/main/package.scala
+++ b/modules/relation/src/main/package.scala
@@ -1,7 +1,5 @@
package lila
-import lila.db.JsTube
-
package object relation extends PackageObject with WithPlay {
type Relation = Boolean
@@ -9,10 +7,4 @@ package object relation extends PackageObject with WithPlay {
private[relation] val Block: Relation = false
private[relation] type ID = String
-
- object tube {
-
- private[relation] implicit lazy val relationTube =
- JsTube.json inColl Env.current.relationColl
- }
}
diff --git a/modules/report/src/main/Env.scala b/modules/report/src/main/Env.scala
index 729cb212fb..2ba1bfd034 100644
--- a/modules/report/src/main/Env.scala
+++ b/modules/report/src/main/Env.scala
@@ -16,7 +16,7 @@ final class Env(
lazy val forms = new DataForm(hub.actor.captcher)
- lazy val api = new ReportApi
+ lazy val api = new ReportApi(reportColl)
// api actor
system.actorOf(Props(new Actor {
diff --git a/modules/report/src/main/Report.scala b/modules/report/src/main/Report.scala
index 24293aa80d..13dae8f92d 100644
--- a/modules/report/src/main/Report.scala
+++ b/modules/report/src/main/Report.scala
@@ -6,7 +6,7 @@ import ornicar.scalalib.Random
import lila.user.User
case class Report(
- id: String, // also the url slug
+ _id: String, // also the url slug
user: String, // the reportee
reason: String,
text: String,
@@ -14,7 +14,8 @@ case class Report(
createdAt: DateTime,
createdBy: String) {
- def slug = id
+ def id = _id
+ def slug = _id
def isCreator(user: String) = user == createdBy
@@ -47,19 +48,11 @@ object Report {
reason: Reason,
text: String,
createdBy: User): Report = new Report(
- id = Random nextStringUppercase 8,
+ _id = Random nextStringUppercase 8,
user = user.id,
reason = reason.name,
text = text,
processedBy = none,
createdAt = DateTime.now,
createdBy = createdBy.id)
-
- import lila.db.JsTube, JsTube.Helpers._
- import play.api.libs.json._
-
- private[report] lazy val tube = JsTube(
- (__.json update readDate('createdAt)) andThen Json.reads[Report],
- Json.writes[Report] andThen (__.json update writeDate('createdAt))
- )
}
diff --git a/modules/report/src/main/ReportApi.scala b/modules/report/src/main/ReportApi.scala
index 9d0b05c51d..8c77a2e32b 100644
--- a/modules/report/src/main/ReportApi.scala
+++ b/modules/report/src/main/ReportApi.scala
@@ -2,15 +2,14 @@ package lila.report
import akka.actor.ActorSelection
import org.joda.time.DateTime
-import play.api.libs.json._
-import play.modules.reactivemongo.json.ImplicitBSONHandlers._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
-import tube.reportTube
-private[report] final class ReportApi {
+private[report] final class ReportApi(coll: Coll) {
+
+ import lila.db.BSON.BSONJodaDateTimeHandler
+ private implicit val ReportBSONHandler = reactivemongo.bson.Macros.handler[Report]
def create(setup: ReportSetup, by: User): Funit = !by.troll ?? {
Reason(setup.reason).fold[Funit](fufail(s"Invalid report reason ${setup.reason}")) { reason =>
@@ -22,13 +21,13 @@ private[report] final class ReportApi {
createdBy = by)
!isAlreadySlain(report, user) ?? {
lila.mon.mod.report.create(reason.name)()
- if (by.id == UserRepo.lichessId) reportTube.coll.update(
+ if (by.id == UserRepo.lichessId) coll.update(
selectRecent(user, reason),
- Json.obj("$set" -> (reportTube.toMongo(report).get - "processedBy" - "_id"))
+ $doc("$set" -> ReportBSONHandler.write(report).remove("processedBy", "_id"))
) flatMap { res =>
- (res.n == 0) ?? $insert(report)
+ (res.n == 0) ?? coll.insert(report).void
}
- else $insert(report)
+ else coll.insert(report).void
}
} >>- monitorUnprocessed
}
@@ -93,41 +92,41 @@ private[report] final class ReportApi {
}
}
- def clean(userId: String): Funit = $update(
- Json.obj(
+ def clean(userId: String): Funit = coll.update(
+ $doc(
"user" -> userId,
"reason" -> "cheat"
) ++ unprocessedSelect,
$set("processedBy" -> "lichess"),
- multi = true)
+ multi = true).void
- def process(id: String, by: User): Funit = $find byId id flatMap {
+ def process(id: String, by: User): Funit = coll.byId[Report](id) flatMap {
_ ?? { report =>
- $update(
- Json.obj(
+ coll.update(
+ $doc(
"user" -> report.user,
"reason" -> report.reason
) ++ unprocessedSelect,
$set("processedBy" -> by.id),
- multi = true)
+ multi = true).void
} >>- monitorUnprocessed >>- lila.mon.mod.report.close()
}
- def processEngine(userId: String, byModId: String): Funit = $update(
- Json.obj(
+ def processEngine(userId: String, byModId: String): Funit = coll.update(
+ $doc(
"user" -> userId,
- "reason" -> $in(List(Reason.Cheat.name, Reason.CheatPrint.name))
+ "reason" -> $in(Reason.Cheat.name, Reason.CheatPrint.name)
) ++ unprocessedSelect,
$set("processedBy" -> byModId),
- multi = true) >>- monitorUnprocessed
+ multi = true).void >>- monitorUnprocessed
- def processTroll(userId: String, byModId: String): Funit = $update(
- Json.obj(
+ def processTroll(userId: String, byModId: String): Funit = coll.update(
+ $doc(
"user" -> userId,
- "reason" -> $in(List(Reason.Insult.name, Reason.Troll.name, Reason.Other.name))
+ "reason" -> $in(Reason.Insult.name, Reason.Troll.name, Reason.Other.name)
) ++ unprocessedSelect,
$set("processedBy" -> byModId),
- multi = true) >>- monitorUnprocessed
+ multi = true).void >>- monitorUnprocessed
def autoInsultReport(userId: String, text: String): Funit = {
UserRepo byId userId zip UserRepo.lichess flatMap {
@@ -142,16 +141,17 @@ private[report] final class ReportApi {
} >>- monitorUnprocessed
def autoProcess(userId: String): Funit =
- $update(
- Json.obj("user" -> userId.toLowerCase),
- Json.obj("processedBy" -> "lichess")) >>- monitorUnprocessed
+ coll.update(
+ $doc("user" -> userId.toLowerCase),
+ $doc("processedBy" -> "lichess")).void >>- monitorUnprocessed
- private val unprocessedSelect = Json.obj("processedBy" -> $exists(false))
- private val processedSelect = Json.obj("processedBy" -> $exists(true))
+ private val unprocessedSelect: Bdoc = "processedBy" $exists false
+ private val processedSelect: Bdoc = "processedBy" $exists true
- def nbUnprocessed = $count(unprocessedSelect)
+ def nbUnprocessed = coll.countSel(unprocessedSelect)
- def recent(nb: Int) = $find($query.all sort $sort.createdDesc, nb)
+ def recent(nb: Int) =
+ 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,15 +164,16 @@ private[report] final class ReportApi {
}
def recentUnprocessed(nb: Int) =
- $find($query(unprocessedSelect) sort $sort.createdDesc, nb)
+ coll.find(unprocessedSelect).sort($sort.createdDesc).cursor[Report]().gather[List](nb)
- def recentProcessed(nb: Int) = $find($query(processedSelect) sort $sort.createdDesc, nb)
+ def recentProcessed(nb: Int) =
+ coll.find(processedSelect).sort($sort.createdDesc).cursor[Report]().gather[List](nb)
- private def selectRecent(user: User, reason: Reason) = Json.obj(
- "createdAt" -> $gt($date(DateTime.now minusDays 7)),
+ private def selectRecent(user: User, reason: Reason) = $doc(
+ "createdAt" $gt DateTime.now.minusDays(7),
"user" -> user.id,
"reason" -> reason.name)
private def findRecent(user: User, reason: Reason): Fu[Option[Report]] =
- $find.one(selectRecent(user, reason))
+ coll.uno[Report](selectRecent(user, reason))
}
diff --git a/modules/report/src/main/package.scala b/modules/report/src/main/package.scala
index 781e0205cb..b2acce360c 100644
--- a/modules/report/src/main/package.scala
+++ b/modules/report/src/main/package.scala
@@ -1,12 +1,3 @@
package lila
-import lila.db.JsTube
-
-package object report extends PackageObject with WithPlay {
-
- object tube {
-
- private[report] implicit lazy val reportTube =
- Report.tube inColl Env.current.reportColl
- }
-}
+package object report extends PackageObject with WithPlay
diff --git a/modules/round/src/main/Cli.scala b/modules/round/src/main/Cli.scala
deleted file mode 100644
index 8e6659cded..0000000000
--- a/modules/round/src/main/Cli.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package lila.round
-
-import scala.concurrent.duration._
-
-import lila.db.api._
-import lila.user.UserRepo
-import lila.game.Game
-import lila.game.BSONHandlers._
-import lila.game.tube.gameTube
-
-private[round] final class Cli(
- db: lila.db.Env,
- roundMap: akka.actor.ActorRef,
- system: akka.actor.ActorSystem) extends lila.common.Cli {
-
- def process = {
-
- case "round" :: "abort" :: "clock" :: Nil =>
- $enumerate[Game]($query(play.api.libs.json.Json.obj(
- Game.BSONFields.status -> chess.Status.Started.id,
- Game.BSONFields.clock -> $exists(true)
- ))) { game =>
- roundMap ! lila.hub.actorApi.map.Tell(game.id, actorApi.round.AbortForMaintenance)
- } inject "done"
- }
-
-}
diff --git a/modules/round/src/main/Drawer.scala b/modules/round/src/main/Drawer.scala
index 1665b3e4a3..71f7367182 100644
--- a/modules/round/src/main/Drawer.scala
+++ b/modules/round/src/main/Drawer.scala
@@ -3,7 +3,7 @@ package lila.round
import akka.pattern.ask
import chess.{ Game => ChessGame, Board, Clock }
-import lila.db.api._
+import lila.db.dsl._
import lila.game.{ Game, Event, Progress, Pov, PlayerRef, Namer, Source }
import lila.pref.{ Pref, PrefApi }
import makeTimeout.short
diff --git a/modules/round/src/main/Env.scala b/modules/round/src/main/Env.scala
index e97e332360..57ff57ffc8 100644
--- a/modules/round/src/main/Env.scala
+++ b/modules/round/src/main/Env.scala
@@ -118,7 +118,7 @@ final class Env(
roundMap = roundMap,
socketHub = socketHub,
messenger = messenger,
- bus = system.lilaBus)
+ bus = system.lilaBus)(system)
lazy val perfsUpdater = new PerfsUpdater(historyApi, rankingApi)
@@ -155,8 +155,6 @@ final class Env(
private lazy val cheatDetector = new CheatDetector(reporter = hub.actor.report)
- lazy val cli = new Cli(db, roundMap = roundMap, system = system)
-
lazy val messenger = new Messenger(
socketHub = socketHub,
chat = hub.actor.chat,
diff --git a/modules/round/src/main/Finisher.scala b/modules/round/src/main/Finisher.scala
index a6936b32cf..781dbcbbd8 100644
--- a/modules/round/src/main/Finisher.scala
+++ b/modules/round/src/main/Finisher.scala
@@ -4,12 +4,11 @@ import chess.Color._
import chess.Status._
import chess.{ Status, Color, Speed }
-import lila.db.api._
+import lila.db.dsl._
import lila.game.actorApi.{ FinishGame, AbortedBy }
import lila.game.{ GameRepo, Game, Pov, Event }
import lila.i18n.I18nKey.{ Select => SelectI18nKey }
import lila.playban.{ PlaybanApi, Outcome }
-import lila.user.tube.userTube
import lila.user.{ User, UserRepo, Perfs }
private[round] final class Finisher(
@@ -118,6 +117,6 @@ private[round] final class Finisher(
else if (game.loserUserId exists (user.id==)) -1
else 0,
totalTime = totalTime,
- tvTime = tvTime)
+ tvTime = tvTime).void
}
}
diff --git a/modules/round/src/main/ForecastApi.scala b/modules/round/src/main/ForecastApi.scala
index ee2afc8e6c..4f9eebe264 100644
--- a/modules/round/src/main/ForecastApi.scala
+++ b/modules/round/src/main/ForecastApi.scala
@@ -3,7 +3,7 @@ package lila.round
import reactivemongo.bson._
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits._
+import lila.db.dsl._
import org.joda.time.DateTime
import scala.concurrent.duration.Duration
import scala.concurrent.Promise
@@ -28,7 +28,7 @@ final class ForecastApi(coll: Coll, roundMap: akka.actor.ActorSelection) {
private def saveSteps(pov: Pov, steps: Forecast.Steps): Funit = {
lila.mon.round.forecast.create()
coll.update(
- BSONDocument("_id" -> pov.fullId),
+ $doc("_id" -> pov.fullId),
Forecast(
_id = pov.fullId,
steps = steps,
@@ -37,7 +37,7 @@ final class ForecastApi(coll: Coll, roundMap: akka.actor.ActorSelection) {
}
def save(pov: Pov, steps: Forecast.Steps): Funit = firstStep(steps) match {
- case None => coll.remove(BSONDocument("_id" -> pov.fullId)).void
+ case None => coll.remove($doc("_id" -> pov.fullId)).void
case Some(step) if pov.game.turns == step.ply - 1 => saveSteps(pov, steps)
case _ => fufail(Forecast.OutOfSync)
}
@@ -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($doc("_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($doc("_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
@@ -79,7 +79,7 @@ final class ForecastApi(coll: Coll, roundMap: akka.actor.ActorSelection) {
case None => fuccess(none)
case Some(fc) => fc(g, last) match {
case Some((newFc, uciMove)) if newFc.steps.nonEmpty =>
- coll.update(BSONDocument("_id" -> fc._id), newFc) inject uciMove.some
+ coll.update($doc("_id" -> fc._id), newFc) inject uciMove.some
case Some((newFc, uciMove)) => clearPov(Pov player g) inject uciMove.some
case _ => clearPov(Pov player g) inject none
}
@@ -88,9 +88,9 @@ final class ForecastApi(coll: Coll, roundMap: akka.actor.ActorSelection) {
private def firstStep(steps: Forecast.Steps) = steps.headOption.flatMap(_.headOption)
- def clearGame(g: Game) = coll.remove(BSONDocument(
- "_id" -> BSONDocument("$in" -> chess.Color.all.map(g.fullIdOf))
+ def clearGame(g: Game) = coll.remove($doc(
+ "_id" -> $doc("$in" -> chess.Color.all.map(g.fullIdOf))
)).void
- def clearPov(pov: Pov) = coll.remove(BSONDocument("_id" -> pov.fullId)).void
+ def clearPov(pov: Pov) = coll.remove($doc("_id" -> pov.fullId)).void
}
diff --git a/modules/round/src/main/History.scala b/modules/round/src/main/History.scala
index 65d5bf2c31..7aa2a68387 100644
--- a/modules/round/src/main/History.scala
+++ b/modules/round/src/main/History.scala
@@ -8,7 +8,7 @@ import org.joda.time.DateTime
import reactivemongo.bson._
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.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)
}
}
diff --git a/modules/round/src/main/NoteApi.scala b/modules/round/src/main/NoteApi.scala
index 5b53941b9d..a7b21bbb41 100644
--- a/modules/round/src/main/NoteApi.scala
+++ b/modules/round/src/main/NoteApi.scala
@@ -1,21 +1,19 @@
package lila.round
-import lila.db.Types.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
diff --git a/modules/round/src/main/Rematcher.scala b/modules/round/src/main/Rematcher.scala
index d4f57038d8..caf0fe0d3f 100644
--- a/modules/round/src/main/Rematcher.scala
+++ b/modules/round/src/main/Rematcher.scala
@@ -7,7 +7,7 @@ import chess.variant._
import chess.{ Game => ChessGame, Board, Clock, Color => ChessColor, Castles }
import ChessColor.{ White, Black }
-import lila.db.api._
+import lila.db.dsl._
import lila.game.{ GameRepo, Game, Event, Progress, Pov, Source, AnonCookie, PerfPicker }
import lila.memo.ExpireSetMemo
import lila.user.{ User, UserRepo }
diff --git a/modules/round/src/main/Socket.scala b/modules/round/src/main/Socket.scala
index 4c8cb40b4c..00f1a14f97 100644
--- a/modules/round/src/main/Socket.scala
+++ b/modules/round/src/main/Socket.scala
@@ -18,7 +18,7 @@ import lila.hub.actorApi.round.{ IsOnGame, AnalysisAvailable }
import lila.hub.actorApi.tv.{ Select => TvSelect }
import lila.hub.TimeBomb
import lila.socket._
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import makeTimeout.short
private[round] final class Socket(
@@ -139,16 +139,13 @@ private[round] final class Socket(
blackIsGone = blackIsGone)
} pipeTo sender
- case Join(uid, user, color, playerId, ip, userTv) =>
- val (enumerator, channel) = Concurrent.broadcast[JsValue]
- val member = Member(channel, user, color, playerId, ip, userTv = userTv)
+ case AddMember(uid, member) =>
addMember(uid, member)
notifyCrowd
- playerDo(color, _.ping)
- sender ! Connected(enumerator, member)
+ playerDo(member.color, _.ping)
if (member.userTv.isDefined) refreshSubscriptions
if (member.owner) lilaBus.publish(
- lila.hub.actorApi.round.SocketEvent.OwnerJoin(gameId, color, ip),
+ lila.hub.actorApi.round.SocketEvent.OwnerJoin(gameId, member.color, member.ip),
'roundDoor)
case Nil =>
diff --git a/modules/round/src/main/SocketHandler.scala b/modules/round/src/main/SocketHandler.scala
index ff7292af94..4c3519cd5d 100644
--- a/modules/round/src/main/SocketHandler.scala
+++ b/modules/round/src/main/SocketHandler.scala
@@ -14,7 +14,7 @@ import lila.common.PimpedJson._
import lila.game.{ Game, Pov, PovRef, PlayerRef, GameRepo }
import lila.hub.actorApi.map._
import lila.hub.actorApi.round.Berserk
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.Handler
import lila.user.User
import makeTimeout.short
@@ -24,7 +24,7 @@ private[round] final class SocketHandler(
socketHub: ActorRef,
hub: lila.hub.Env,
messenger: Messenger,
- bus: lila.common.Bus) {
+ bus: lila.common.Bus)(implicit val system: ActorSystem) {
private def controller(
gameId: String,
@@ -100,7 +100,7 @@ private[round] final class SocketHandler(
uid: String,
user: Option[User],
ip: String,
- userTv: Option[String]): Fu[Option[JsSocketHandler]] =
+ userTv: Option[String]): Fu[Option[JsFlow]] =
GameRepo.pov(gameId, colorName) flatMap {
_ ?? { join(_, none, uid, "", user, ip, userTv = userTv) map some }
}
@@ -110,7 +110,7 @@ private[round] final class SocketHandler(
uid: String,
token: String,
user: Option[User],
- ip: String): Fu[JsSocketHandler] =
+ ip: String): Fu[JsFlow] =
join(pov, Some(pov.playerId), uid, token, user, ip, userTv = none)
private def join(
@@ -120,21 +120,16 @@ private[round] final class SocketHandler(
token: String,
user: Option[User],
ip: String,
- userTv: Option[String]): Fu[JsSocketHandler] = {
- val join = Join(
- uid = uid,
- user = user,
- color = pov.color,
- playerId = playerId,
- ip = ip,
- userTv = userTv)
- socketHub ? Get(pov.gameId) mapTo manifest[ActorRef] flatMap { socket =>
- Handler(hub, socket, uid, join, user map (_.id)) {
- case Connected(enum, member) =>
- (controller(pov.gameId, socket, uid, pov.ref, member), enum, member)
+ userTv: Option[String]): Fu[JsFlow] =
+ socketHub ? Get(pov.gameId) mapTo manifest[ActorRef] map { socket =>
+ Handler.actorRef { out =>
+ val member = Member(out, user, pov.color, playerId, ip, userTv = userTv)
+ socket ! AddMember(uid, member)
+ Handler.props(hub, socket, member, uid, user.map(_.id)) {
+ controller(pov.gameId, socket, uid, pov.ref, member)
+ }
}
}
- }
private def parseMove(o: JsObject) = for {
d ← o obj "d"
@@ -142,7 +137,7 @@ private[round] final class SocketHandler(
dest ← d str "to"
prom = d str "promotion"
move <- Uci.Move.fromStrings(orig, dest, prom)
- blur = (d int "b") == Some(1)
+ blur = d int "b" contains 1
lag = d int "lag"
} yield (move, blur, ~lag)
@@ -151,7 +146,7 @@ private[round] final class SocketHandler(
role ← d str "role"
pos ← d str "pos"
drop <- Uci.Drop.fromStrings(role, pos)
- blur = (d int "b") == Some(1)
+ blur = d int "b" contains 1
lag = d int "lag"
} yield (drop, blur, ~lag)
diff --git a/modules/round/src/main/Titivate.scala b/modules/round/src/main/Titivate.scala
index e67746e08e..2ec107e0ae 100644
--- a/modules/round/src/main/Titivate.scala
+++ b/modules/round/src/main/Titivate.scala
@@ -6,8 +6,7 @@ import play.api.libs.iteratee._
import reactivemongo.api._
import scala.concurrent.duration._
-import lila.db.api._
-import lila.game.tube.gameTube
+import lila.db.dsl._
import lila.game.{ Query, Game, GameRepo }
import lila.game.BSONHandlers.gameBSONHandler
import lila.hub.actorApi.map.Tell
@@ -37,8 +36,7 @@ private[round] final class Titivate(
throw new RuntimeException(msg)
case Run =>
- $query(Query.checkable)
- .cursor[Game]()
+ GameRepo.cursor(Query.checkable)
.enumerate(5000, stopOnError = true)
.|>>>(Iteratee.foldM[Game, Int](0) {
case (count, game) => {
diff --git a/modules/round/src/main/TvBroadcast.scala b/modules/round/src/main/TvBroadcast.scala
index 3d1baf435f..2a6d8e50ad 100644
--- a/modules/round/src/main/TvBroadcast.scala
+++ b/modules/round/src/main/TvBroadcast.scala
@@ -1,28 +1,30 @@
package lila.round
import akka.actor._
+import akka.stream._
+import akka.stream.scaladsl._
import lila.hub.actorApi.game.ChangeFeatured
import lila.hub.actorApi.round.MoveEvent
import lila.socket.Socket.makeMessage
-import play.api.libs.iteratee._
import play.api.libs.json._
private final class TvBroadcast extends Actor {
- private val (enumerator, channel) = Concurrent.broadcast[JsValue]
+ val (out, publisher) =
+ lila.common.AkkaStream.actorPublisher(20, OverflowStrategy.dropHead)(materializer(context.system))
private var featuredId = none[String]
def receive = {
- case TvBroadcast.GetEnumerator => sender ! enumerator
+ case TvBroadcast.GetPublisher => sender ! publisher
case ChangeFeatured(id, msg) =>
featuredId = id.some
- channel push msg
+ out ! msg
case move: MoveEvent if Some(move.gameId) == featuredId =>
- channel push makeMessage("fen", Json.obj(
+ out ! makeMessage("fen", Json.obj(
"fen" -> move.fen,
"lm" -> move.move
))
@@ -31,7 +33,7 @@ private final class TvBroadcast extends Actor {
object TvBroadcast {
- type EnumeratorType = Enumerator[JsValue]
+ type PublisherType = org.reactivestreams.Publisher[JsValue]
- case object GetEnumerator
+ case object GetPublisher
}
diff --git a/modules/round/src/main/actorApi.scala b/modules/round/src/main/actorApi.scala
index 431e94a5e7..d2bfe3da1a 100644
--- a/modules/round/src/main/actorApi.scala
+++ b/modules/round/src/main/actorApi.scala
@@ -3,6 +3,7 @@ package actorApi
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.Promise
+import akka.actor.ActorRef
import chess.Color
import chess.format.Uci
@@ -29,7 +30,7 @@ sealed trait Member extends SocketMember {
object Member {
def apply(
- channel: JsChannel,
+ out: ActorRef,
user: Option[User],
color: Color,
playerIdOption: Option[String],
@@ -37,14 +38,14 @@ object Member {
userTv: Option[String]): Member = {
val userId = user map (_.id)
val troll = user.??(_.troll)
- playerIdOption.fold[Member](Watcher(channel, userId, color, troll, ip, userTv)) { playerId =>
- Owner(channel, userId, playerId, color, troll, ip)
+ playerIdOption.fold[Member](Watcher(out, userId, color, troll, ip, userTv)) { playerId =>
+ Owner(out, userId, playerId, color, troll, ip)
}
}
}
case class Owner(
- channel: JsChannel,
+ out: ActorRef,
userId: Option[String],
playerId: String,
color: Color,
@@ -56,7 +57,7 @@ case class Owner(
}
case class Watcher(
- channel: JsChannel,
+ out: ActorRef,
userId: Option[String],
color: Color,
troll: Boolean,
@@ -66,14 +67,8 @@ case class Watcher(
val playerIdOption = none
}
-case class Join(
- uid: String,
- user: Option[User],
- color: Color,
- playerId: Option[String],
- ip: String,
- userTv: Option[String])
-case class Connected(enumerator: JsEnumerator, member: Member)
+case class AddMember(uid: String, member: Member)
+
case class Bye(color: Color)
case class IsGone(color: Color)
case object GetSocketStatus
diff --git a/modules/security/src/main/Api.scala b/modules/security/src/main/Api.scala
index 71540da6ce..4150524ade 100644
--- a/modules/security/src/main/Api.scala
+++ b/modules/security/src/main/Api.scala
@@ -7,10 +7,12 @@ import play.api.data.Forms._
import play.api.mvc.RequestHeader
import reactivemongo.bson._
+import lila.db.dsl._
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.user.{ User, UserRepo }
final class Api(
+ coll: Coll,
firewall: Firewall,
tor: Tor,
geoIP: GeoIP,
@@ -78,16 +80,16 @@ final class Api(
def userIdsSharingFingerprint = userIdsSharingField("fp") _
private def userIdsSharingField(field: String)(userId: String): Fu[List[String]] =
- tube.storeColl.distinct(
+ coll.distinct(
field,
- BSONDocument("user" -> userId, field -> BSONDocument("$exists" -> true)).some
+ $doc("user" -> userId, field -> $doc("$exists" -> true)).some
).flatMap {
case Nil => fuccess(Nil)
- case values => tube.storeColl.distinct(
+ case values => coll.distinct(
"user",
- BSONDocument(
- field -> BSONDocument("$in" -> values),
- "user" -> BSONDocument("$ne" -> userId)
+ $doc(
+ field -> $doc("$in" -> values),
+ "user" -> $doc("$ne" -> userId)
).some
) map lila.db.BSON.asStrings
}
@@ -97,11 +99,11 @@ final class Api(
def recentUserIdsByIp = recentUserIdsByField("ip") _
private def recentUserIdsByField(field: String)(value: String): Fu[List[String]] =
- tube.storeColl.distinct(
+ coll.distinct(
"user",
- BSONDocument(
+ $doc(
field -> value,
- "date" -> BSONDocument("$gt" -> DateTime.now.minusYears(1))
+ "date" -> $doc("$gt" -> DateTime.now.minusYears(1))
).some
) map lila.db.BSON.asStrings
}
diff --git a/modules/security/src/main/Cli.scala b/modules/security/src/main/Cli.scala
index 6e0dddc030..d77d9038a0 100644
--- a/modules/security/src/main/Cli.scala
+++ b/modules/security/src/main/Cli.scala
@@ -16,7 +16,7 @@ private[security] final class Cli extends lila.common.Cli {
case "security" :: "grant" :: uid :: roles =>
perform(uid, user =>
- UserRepo.setRoles(user.id, roles map (_.toUpperCase))
+ UserRepo.setRoles(user.id, roles map (_.toUpperCase)).void
)
}
diff --git a/modules/security/src/main/DataForm.scala b/modules/security/src/main/DataForm.scala
index baac8c98c3..b7dca4af58 100644
--- a/modules/security/src/main/DataForm.scala
+++ b/modules/security/src/main/DataForm.scala
@@ -5,9 +5,7 @@ import play.api.data.Forms._
import play.api.data.validation.Constraints
import lila.common.LameName
-import lila.db.api.$count
-import lila.user.tube.userTube
-import lila.user.User
+import lila.user.{ User, UserRepo }
final class DataForm(
val captcher: akka.actor.ActorSelection,
@@ -42,7 +40,7 @@ final class DataForm(
Constraints.pattern(
regex = """^[^\d].+$""".r,
error = "The username must not start with a number")
- ).verifying("This user already exists", u => !$count.exists(u.toLowerCase) awaitSeconds 2)
+ ).verifying("This user already exists", u => !UserRepo.nameExists(u).awaitSeconds(2))
.verifying("This username is not acceptable", u => !LameName(u))
val website = Form(mapping(
diff --git a/modules/security/src/main/Env.scala b/modules/security/src/main/Env.scala
index 206388b9eb..170dbddcf0 100644
--- a/modules/security/src/main/Env.scala
+++ b/modules/security/src/main/Env.scala
@@ -7,7 +7,7 @@ import com.typesafe.config.Config
import scala.concurrent.duration._
import lila.common.PimpedConfig._
-import lila.db.Types.Coll
+import lila.db.dsl.Coll
import lila.user.{ User, UserRepo }
final class Env(
@@ -52,6 +52,7 @@ final class Env(
val RecaptchaPublicKey = config getString "recaptcha.public_key"
lazy val firewall = new Firewall(
+ coll = firewallColl,
cookieName = FirewallCookieName.some filter (_ => FirewallCookieEnabled),
enabled = FirewallEnabled,
cachedIpsTtl = FirewallCachedIpsTtl)
@@ -72,7 +73,7 @@ final class Env(
file = GeoIPFile,
cacheTtl = GeoIPCacheTtl)
- lazy val userSpy = UserSpy(firewall, geoIP) _
+ lazy val userSpy = UserSpy(firewall, geoIP)(storeColl) _
def store = Store
@@ -107,7 +108,7 @@ final class Env(
scheduler.once(30 seconds)(tor.refresh(_ => funit))
scheduler.effect(TorRefreshDelay, "Refresh Tor exit nodes")(tor.refresh(firewall.unblockIps))
- lazy val api = new Api(firewall, tor, geoIP, emailAddress)
+ lazy val api = new Api(storeColl, firewall, tor, geoIP, emailAddress)
def cli = new Cli
diff --git a/modules/security/src/main/Firewall.scala b/modules/security/src/main/Firewall.scala
index f79c195802..961f421384 100644
--- a/modules/security/src/main/Firewall.scala
+++ b/modules/security/src/main/Firewall.scala
@@ -8,15 +8,15 @@ import ornicar.scalalib.Random
import play.api.libs.json._
import play.api.mvc.Results.Redirect
import play.api.mvc.{ RequestHeader, Handler, Action, Cookies }
-import play.modules.reactivemongo.json.ImplicitBSONHandlers._
import spray.caching.{ LruCache, Cache }
import lila.common.LilaCookie
import lila.common.PimpedJson._
-import lila.db.api._
-import tube.firewallTube
+import lila.db.dsl._
+import lila.db.BSON.BSONJodaDateTimeHandler
final class Firewall(
+ coll: Coll,
cookieName: Option[String],
enabled: Boolean,
cachedIpsTtl: Duration) {
@@ -36,11 +36,11 @@ final class Firewall(
def accepts(req: RequestHeader): Fu[Boolean] = blocks(req) map (!_)
def blockIp(ip: String): Funit = validIp(ip) ?? {
- $update(Json.obj("_id" -> ip), Json.obj("_id" -> ip, "date" -> $date(DateTime.now)), upsert = true) >>- refresh
+ coll.update($id(ip), $doc("_id" -> ip, "date" -> DateTime.now), upsert = true).void >>- refresh
}
def unblockIps(ips: Iterable[String]): Funit =
- $remove($select.byIds(ips filter validIp)) >>- refresh
+ coll.remove($inIds(ips filter validIp)).void >>- refresh
private def infectCookie(name: String)(implicit req: RequestHeader) = Action {
logger.info("Infect cookie " + formatReq(req))
@@ -75,7 +75,7 @@ final class Firewall(
def clear { cache.clear }
def contains(ip: String) = apply map (_ contains strToIp(ip))
def fetch: Fu[Set[IP]] =
- firewallTube.coll.distinct("_id") map { res =>
+ coll.distinct("_id") map { res =>
lila.db.BSON.asStringSet(res) map strToIp
} addEffect { ips =>
lila.mon.security.firewall.ip(ips.size)
diff --git a/modules/security/src/main/Store.scala b/modules/security/src/main/Store.scala
index 4ff3d9b418..4e2d152646 100644
--- a/modules/security/src/main/Store.scala
+++ b/modules/security/src/main/Store.scala
@@ -4,23 +4,24 @@ import scala.concurrent.Future
import org.joda.time.DateTime
import play.api.mvc.RequestHeader
-import reactivemongo.bson.BSONDocument
import reactivemongo.bson.Macros
-import lila.db.api._
+import lila.db.dsl._
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.user.{ User, UserRepo }
import lila.common.HTTPRequest
-import tube.storeColl
object Store {
+ // dirty
+ private val coll = Env.current.storeColl
+
private[security] def save(
sessionId: String,
userId: String,
req: RequestHeader,
apiVersion: Option[Int]): Funit =
- storeColl.insert(BSONDocument(
+ coll.insert($doc(
"_id" -> sessionId,
"user" -> userId,
"ip" -> HTTPRequest.lastRemoteAddress(req),
@@ -31,48 +32,48 @@ object Store {
)).void
def userId(sessionId: String): Fu[Option[String]] =
- storeColl.find(
- BSONDocument("_id" -> sessionId, "up" -> true),
- BSONDocument("user" -> true, "_id" -> false)
- ).one[BSONDocument] map { _ flatMap (_.getAs[String]("user")) }
+ coll.find(
+ $doc("_id" -> sessionId, "up" -> true),
+ $doc("user" -> true, "_id" -> false)
+ ).uno[Bdoc] map { _ flatMap (_.getAs[String]("user")) }
case class UserIdAndFingerprint(user: String, fp: Option[String])
private implicit val UserIdAndFingerprintBSONReader = Macros.reader[UserIdAndFingerprint]
def userIdAndFingerprint(sessionId: String): Fu[Option[UserIdAndFingerprint]] =
- storeColl.find(
- BSONDocument("_id" -> sessionId, "up" -> true),
- BSONDocument("user" -> true, "fp" -> true, "_id" -> false)
- ).one[UserIdAndFingerprint]
+ coll.find(
+ $doc("_id" -> sessionId, "up" -> true),
+ $doc("user" -> true, "fp" -> true, "_id" -> false)
+ ).uno[UserIdAndFingerprint]
def delete(sessionId: String): Funit =
- storeColl.update(
- BSONDocument("_id" -> sessionId),
- BSONDocument("$set" -> BSONDocument("up" -> false))).void
+ coll.update(
+ $doc("_id" -> sessionId),
+ $doc("$set" -> $doc("up" -> false))).void
def closeUserAndSessionId(userId: String, sessionId: String): Funit =
- storeColl.update(
- BSONDocument("user" -> userId, "_id" -> sessionId, "up" -> true),
- BSONDocument("$set" -> BSONDocument("up" -> false))).void
+ coll.update(
+ $doc("user" -> userId, "_id" -> sessionId, "up" -> true),
+ $doc("$set" -> $doc("up" -> false))).void
def closeUserExceptSessionId(userId: String, sessionId: String): Funit =
- storeColl.update(
- BSONDocument("user" -> userId, "_id" -> BSONDocument("$ne" -> sessionId), "up" -> true),
- BSONDocument("$set" -> BSONDocument("up" -> false)),
+ coll.update(
+ $doc("user" -> userId, "_id" -> $doc("$ne" -> sessionId), "up" -> true),
+ $doc("$set" -> $doc("up" -> false)),
multi = true).void
// useful when closing an account,
// we want to logout too
- def disconnect(userId: String): Funit = storeColl.update(
- BSONDocument("user" -> userId),
- BSONDocument("$set" -> BSONDocument("up" -> false)),
+ def disconnect(userId: String): Funit = coll.update(
+ $doc("user" -> userId),
+ $doc("$set" -> $doc("up" -> false)),
multi = true).void
private implicit val UserSessionBSONHandler = Macros.handler[UserSession]
def openSessions(userId: String, nb: Int): Fu[List[UserSession]] =
- storeColl.find(
- BSONDocument("user" -> userId, "up" -> true)
- ).sort(BSONDocument("date" -> -1)).cursor[UserSession]().collect[List](nb)
+ coll.find(
+ $doc("user" -> userId, "up" -> true)
+ ).sort($doc("date" -> -1)).cursor[UserSession]().gather[List](nb)
def setFingerprint(id: String, fingerprint: String): Fu[String] = {
import java.util.Base64
@@ -82,9 +83,9 @@ object Store {
Hex decodeHex fingerprint.toArray
} take 8
} flatMap { hash =>
- storeColl.update(
- BSONDocument("_id" -> id),
- BSONDocument("$set" -> BSONDocument("fp" -> hash))
+ coll.update(
+ $doc("_id" -> id),
+ $doc("$set" -> $doc("fp" -> hash))
) inject hash
}
}
@@ -95,10 +96,10 @@ object Store {
private implicit val InfoBSONHandler = Macros.handler[Info]
def findInfoByUser(userId: String): Fu[List[Info]] =
- storeColl.find(
- BSONDocument("user" -> userId),
- BSONDocument("_id" -> false, "ip" -> true, "ua" -> true, "fp" -> true)
- ).cursor[Info]().collect[List]()
+ coll.find(
+ $doc("user" -> userId),
+ $doc("_id" -> false, "ip" -> true, "ua" -> true, "fp" -> true)
+ ).cursor[Info]().gather[List]()
private case class DedupInfo(_id: String, ip: String, ua: String) {
def compositeKey = s"$ip $ua"
@@ -106,15 +107,13 @@ object Store {
private implicit val DedupInfoBSONHandler = Macros.handler[DedupInfo]
def dedup(userId: String, keepSessionId: String): Funit =
- storeColl.find(BSONDocument(
+ coll.find($doc(
"user" -> userId,
"up" -> true
- )).sort(BSONDocument("date" -> -1))
- .cursor[DedupInfo]().collect[List]() flatMap { sessions =>
+ )).sort($doc("date" -> -1))
+ .cursor[DedupInfo]().gather[List]() flatMap { sessions =>
val olds = sessions.groupBy(_.compositeKey).values.map(_ drop 1).flatten
.filter(_._id != keepSessionId)
- storeColl.remove(
- BSONDocument("_id" -> BSONDocument("$in" -> olds.map(_._id)))
- ).void
+ coll.remove($inIds(olds.map(_._id))).void
}
}
diff --git a/modules/security/src/main/UserSpy.scala b/modules/security/src/main/UserSpy.scala
index d7e252dbba..c70dc60a53 100644
--- a/modules/security/src/main/UserSpy.scala
+++ b/modules/security/src/main/UserSpy.scala
@@ -1,14 +1,11 @@
package lila.security
-import scala.concurrent.Future
-
import play.api.mvc.RequestHeader
import reactivemongo.bson._
import lila.common.PimpedJson._
-import lila.db.api._
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
-import tube.storeColl
case class UserSpy(
ips: List[UserSpy.IPData],
@@ -42,7 +39,7 @@ object UserSpy {
case class IPData(ip: IP, blocked: Boolean, location: Location)
- private[security] def apply(firewall: Firewall, geoIP: GeoIP)(userId: String): Fu[UserSpy] = for {
+ private[security] def apply(firewall: Firewall, geoIP: GeoIP)(coll: Coll)(userId: String): Fu[UserSpy] = for {
user ← UserRepo named userId flatten "[spy] user not found"
infos ← Store.findInfoByUser(user.id)
ips = infos.map(_.ip).distinct
@@ -50,8 +47,8 @@ object UserSpy {
locations <- scala.concurrent.Future {
ips map geoIP.orUnknown
}
- sharingIp ← exploreSimilar("ip")(user)
- sharingFingerprint ← exploreSimilar("fp")(user)
+ sharingIp ← exploreSimilar("ip")(user)(coll)
+ sharingFingerprint ← exploreSimilar("fp")(user)(coll)
} yield UserSpy(
ips = ips zip blockedIps zip locations map {
case ((ip, blocked), location) => IPData(ip, blocked, location)
@@ -60,25 +57,25 @@ object UserSpy {
usersSharingIp = (sharingIp + user).toList.sortBy(-_.createdAt.getMillis),
usersSharingFingerprint = (sharingFingerprint + user).toList.sortBy(-_.createdAt.getMillis))
- private def exploreSimilar(field: String)(user: User): Fu[Set[User]] =
+ private def exploreSimilar(field: String)(user: User)(implicit coll: Coll): Fu[Set[User]] =
nextValues(field)(user) flatMap { nValues =>
nextUsers(field)(nValues, user) map { _ + user }
}
- private def nextValues(field: String)(user: User): Fu[Set[Value]] =
- storeColl.find(
- BSONDocument("user" -> user.id),
- BSONDocument(field -> true)
- ).cursor[BSONDocument]().collect[List]() map {
+ private def nextValues(field: String)(user: User)(implicit coll: Coll): Fu[Set[Value]] =
+ coll.find(
+ $doc("user" -> user.id),
+ $doc(field -> true)
+ ).cursor[Bdoc]().gather[List]() map {
_.flatMap(_.getAs[Value](field)).toSet
}
- private def nextUsers(field: String)(values: Set[Value], user: User): Fu[Set[User]] =
+ private def nextUsers(field: String)(values: Set[Value], user: User)(implicit coll: Coll): Fu[Set[User]] =
values.nonEmpty ?? {
- storeColl.distinct("user",
- BSONDocument(
- field -> BSONDocument("$in" -> values),
- "user" -> BSONDocument("$ne" -> user.id)
+ coll.distinct("user",
+ $doc(
+ field -> $doc("$in" -> values),
+ "user" -> $doc("$ne" -> user.id)
).some
) map lila.db.BSON.asStrings flatMap { userIds =>
userIds.nonEmpty ?? (UserRepo byIds userIds) map (_.toSet)
diff --git a/modules/security/src/main/package.scala b/modules/security/src/main/package.scala
index 7bee97fcbd..0d390f0d94 100644
--- a/modules/security/src/main/package.scala
+++ b/modules/security/src/main/package.scala
@@ -1,16 +1,6 @@
package lila
-import lila.db.JsTube
-
package object security extends PackageObject with WithPlay {
- object tube {
-
- private[security] def storeColl = Env.current.storeColl
-
- private[security] implicit lazy val firewallTube =
- JsTube.json inColl Env.current.firewallColl
- }
-
private[security] def logger = lila.log("security")
}
diff --git a/modules/setup/src/main/AnonConfigRepo.scala b/modules/setup/src/main/AnonConfigRepo.scala
index 458253ebee..c0ed4a7002 100644
--- a/modules/setup/src/main/AnonConfigRepo.scala
+++ b/modules/setup/src/main/AnonConfigRepo.scala
@@ -5,19 +5,20 @@ import reactivemongo.api._
import reactivemongo.bson._
import lila.common.{ LilaCookie, LilaException }
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.Game
import lila.user.User
-import tube.anonConfigTube
private[setup] object AnonConfigRepo {
+ // dirty
+ private val coll = Env.current.anonConfigColl
+
def update(req: RequestHeader)(f: UserConfig => UserConfig): Funit =
configOption(req) flatMap {
_ ?? { config =>
- anonConfigTube.coll.update(
- BSONDocument("_id" -> config.id),
+ coll.update(
+ $doc("_id" -> config.id),
f(config),
upsert = true).void
}
@@ -27,8 +28,8 @@ private[setup] object AnonConfigRepo {
configOption(req) map (_ | UserConfig.default("nocookie"))
def config(sid: String): Fu[UserConfig] =
- $find byId sid recover {
- case e: LilaException => {
+ coll.byId[UserConfig](sid) recover {
+ case e: Exception => {
logger.warn("Can't load config", e)
none[UserConfig]
}
@@ -38,12 +39,7 @@ private[setup] object AnonConfigRepo {
sessionId(req).??(s => config(s) map (_.some))
def filter(req: RequestHeader): Fu[FilterConfig] = sessionId(req) ?? { sid =>
- anonConfigTube.coll.find(
- BSONDocument("_id" -> sid),
- BSONDocument("filter" -> true)
- ).one[BSONDocument] map {
- _ flatMap (_.getAs[FilterConfig]("filter"))
- }
+ coll.primitiveOne[FilterConfig]($id(sid), "filter")
} map (_ | FilterConfig.default)
private def sessionId(req: RequestHeader): Option[String] =
diff --git a/modules/setup/src/main/FilterConfig.scala b/modules/setup/src/main/FilterConfig.scala
index 1c0e3743aa..c2dc9e35d3 100644
--- a/modules/setup/src/main/FilterConfig.scala
+++ b/modules/setup/src/main/FilterConfig.scala
@@ -74,6 +74,4 @@ object FilterConfig {
"s" -> o.speed.map(_.id),
"e" -> o.ratingRange.toString)
}
-
- private[setup] val tube = lila.db.BsTube(filterConfigBSONHandler)
}
diff --git a/modules/setup/src/main/FormFactory.scala b/modules/setup/src/main/FormFactory.scala
index a0fc1a8ec4..bd829ddab7 100644
--- a/modules/setup/src/main/FormFactory.scala
+++ b/modules/setup/src/main/FormFactory.scala
@@ -1,12 +1,11 @@
package lila.setup
import lila.rating.RatingRange
-import lila.db.api._
+import lila.db.dsl._
import lila.lobby.Color
import lila.user.UserContext
import play.api.data._
import play.api.data.Forms._
-import tube.{ userConfigTube, anonConfigTube }
private[setup] final class FormFactory(casualOnly: Boolean) {
diff --git a/modules/setup/src/main/Processor.scala b/modules/setup/src/main/Processor.scala
index a5dfb4784b..327a2ac8c4 100644
--- a/modules/setup/src/main/Processor.scala
+++ b/modules/setup/src/main/Processor.scala
@@ -5,14 +5,13 @@ import akka.pattern.ask
import chess.{ Game => ChessGame, Board, Color => ChessColor }
import play.api.libs.json.{ Json, JsObject }
-import lila.db.api._
+import lila.db.dsl._
import lila.game.{ Game, GameRepo, Pov, Progress, PerfPicker }
import lila.i18n.I18nDomain
import lila.lobby.actorApi.{ AddHook, AddSeek }
import lila.lobby.Hook
import lila.user.{ User, UserContext }
import makeTimeout.short
-import tube.{ userConfigTube, anonConfigTube }
private[setup] final class Processor(
lobby: ActorSelection,
diff --git a/modules/setup/src/main/UserConfig.scala b/modules/setup/src/main/UserConfig.scala
index 90810d1307..4d91f99560 100644
--- a/modules/setup/src/main/UserConfig.scala
+++ b/modules/setup/src/main/UserConfig.scala
@@ -29,7 +29,8 @@ private[setup] object UserConfig {
hook = HookConfig.default,
filter = FilterConfig.default)
- import lila.db.{ BsTube, BSON }
+ import lila.db.BSON
+ import lila.db.dsl._
import reactivemongo.bson._
import AiConfig.aiConfigBSONHandler
import FriendConfig.friendConfigBSONHandler
@@ -52,6 +53,4 @@ private[setup] object UserConfig {
"hook" -> o.hook,
"filter" -> o.filter)
}
-
- private[setup] val tube = BsTube(userConfigBSONHandler)
}
diff --git a/modules/setup/src/main/UserConfigRepo.scala b/modules/setup/src/main/UserConfigRepo.scala
index 2dfdea06c1..e73571f7fd 100644
--- a/modules/setup/src/main/UserConfigRepo.scala
+++ b/modules/setup/src/main/UserConfigRepo.scala
@@ -4,35 +4,31 @@ import reactivemongo.api._
import reactivemongo.bson._
import lila.common.LilaException
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.game.Game
import lila.user.User
-import tube.userConfigTube
private[setup] object UserConfigRepo {
+ // dirty
+ private val coll = Env.current.userConfigColl
+
def update(user: User)(f: UserConfig => UserConfig): Funit =
config(user) flatMap { config =>
- userConfigTube.coll.update(
- BSONDocument("_id" -> config.id),
+ coll.update(
+ $doc("_id" -> config.id),
f(config),
upsert = true).void
}
def config(user: User): Fu[UserConfig] =
- $find byId user.id recover {
- case e: LilaException => {
+ coll.byId[UserConfig](user.id) recover {
+ case e: Exception => {
logger.warn("Can't load config", e)
none[UserConfig]
}
} map (_ | UserConfig.default(user.id))
def filter(user: User): Fu[FilterConfig] =
- userConfigTube.coll.find(
- BSONDocument("_id" -> user.id),
- BSONDocument("filter" -> true)
- ).one[BSONDocument] map {
- _ flatMap (_.getAs[FilterConfig]("filter")) getOrElse FilterConfig.default
- }
+ coll.primitiveOne[FilterConfig]($id(user.id), "filter") map (_ | FilterConfig.default)
}
diff --git a/modules/setup/src/main/package.scala b/modules/setup/src/main/package.scala
index ff1f318fc1..5d9b72c58e 100644
--- a/modules/setup/src/main/package.scala
+++ b/modules/setup/src/main/package.scala
@@ -4,14 +4,5 @@ import lila.socket.WithSocket
package object setup extends PackageObject with WithPlay with WithSocket {
- object tube {
-
- private[setup] implicit lazy val userConfigTube =
- UserConfig.tube inColl Env.current.userConfigColl
-
- private[setup] implicit lazy val anonConfigTube =
- UserConfig.tube inColl Env.current.anonConfigColl
- }
-
private[setup] def logger = lila.log("setup")
}
diff --git a/modules/shutup/src/main/ShutupApi.scala b/modules/shutup/src/main/ShutupApi.scala
index b3b0cc7f90..c787d3f4eb 100644
--- a/modules/shutup/src/main/ShutupApi.scala
+++ b/modules/shutup/src/main/ShutupApi.scala
@@ -2,12 +2,10 @@ package lila.shutup
import org.joda.time.DateTime
import reactivemongo.bson._
-import reactivemongo.bson.Macros
import reactivemongo.core.commands._
import scala.concurrent.duration._
-import lila.db.BSON._
-import lila.db.Types.Coll
+import lila.db.dsl._
import lila.game.GameRepo
import lila.user.UserRepo
@@ -20,8 +18,8 @@ final class ShutupApi(
private implicit val UserRecordBSONHandler = Macros.handler[UserRecord]
def getPublicLines(userId: String): Fu[List[String]] =
- coll.find(BSONDocument("_id" -> userId), BSONDocument("pub" -> 1))
- .one[BSONDocument].map {
+ coll.find($doc("_id" -> userId), $doc("pub" -> 1))
+ .uno[Bdoc].map {
~_.flatMap(_.getAs[List[String]]("pub"))
}
@@ -47,25 +45,25 @@ final class ShutupApi(
case false =>
val analysed = Analyser(text)
val pushPublicLine =
- if (textType == TextType.PublicChat && analysed.nbBadWords > 0) BSONDocument(
- "pub" -> BSONDocument(
+ if (textType == TextType.PublicChat && analysed.nbBadWords > 0) $doc(
+ "pub" -> $doc(
"$each" -> List(text),
"$slice" -> -20)
)
- else BSONDocument()
- val push = BSONDocument(
- textType.key -> BSONDocument(
+ else $empty
+ val push = $doc(
+ textType.key -> $doc(
"$each" -> List(BSONDouble(analysed.ratio)),
"$slice" -> -textType.rotation)
) ++ pushPublicLine
coll.findAndUpdate(
- selector = BSONDocument("_id" -> userId),
- update = BSONDocument("$push" -> push),
+ selector = $doc("_id" -> userId),
+ update = $doc("$push" -> push),
fetchNewObject = true,
upsert = true).map(_.value) map2 UserRecordBSONHandler.read flatMap {
- case None => fufail(s"can't find user record for $userId")
- case Some(userRecord) => legiferate(userRecord)
- } logFailure lila.log("shutup")
+ case None => fufail(s"can't find user record for $userId")
+ case Some(userRecord) => legiferate(userRecord)
+ } logFailure lila.log("shutup")
}
}
@@ -73,8 +71,8 @@ final class ShutupApi(
userRecord.reports.exists(_.unacceptable) ?? {
reporter ! lila.hub.actorApi.report.Shutup(userRecord.userId, reportText(userRecord))
coll.update(
- BSONDocument("_id" -> userRecord.userId),
- BSONDocument("$unset" -> BSONDocument(
+ $doc("_id" -> userRecord.userId),
+ $doc("$unset" -> $doc(
TextType.PublicForumMessage.key -> true,
TextType.TeamForumMessage.key -> true,
TextType.PrivateMessage.key -> true,
diff --git a/modules/simul/src/main/Env.scala b/modules/simul/src/main/Env.scala
index 7012f67ba2..e5d4c5266c 100644
--- a/modules/simul/src/main/Env.scala
+++ b/modules/simul/src/main/Env.scala
@@ -18,7 +18,6 @@ final class Env(
scheduler: lila.common.Scheduler,
db: lila.db.Env,
mongoCache: lila.memo.MongoCache.Builder,
- flood: lila.security.Flood,
hub: lila.hub.Env,
lightUser: String => Option[lila.common.LightUser],
onGameStart: String => Unit,
@@ -71,8 +70,7 @@ final class Env(
hub = hub,
socketHub = socketHub,
chat = hub.actor.chat,
- flood = flood,
- exists = repo.exists)
+ exists = repo.exists)(system)
system.lilaBus.subscribe(system.actorOf(Props(new Actor {
import akka.pattern.pipe
@@ -122,7 +120,6 @@ object Env {
scheduler = lila.common.PlayApp.scheduler,
db = lila.db.Env.current,
mongoCache = lila.memo.Env.current.mongoCache,
- flood = lila.security.Env.current.flood,
hub = lila.hub.Env.current,
lightUser = lila.user.Env.current.lightUser,
onGameStart = lila.game.Env.current.onStart,
diff --git a/modules/simul/src/main/SimulApi.scala b/modules/simul/src/main/SimulApi.scala
index a9a84a04be..710d59e616 100644
--- a/modules/simul/src/main/SimulApi.scala
+++ b/modules/simul/src/main/SimulApi.scala
@@ -8,7 +8,7 @@ import scala.concurrent.duration._
import chess.Status
import chess.variant.Variant
import lila.common.Debouncer
-import lila.db.Types.Coll
+import lila.db.dsl.Coll
import lila.game.{ Game, GameRepo }
import lila.hub.actorApi.lobby.ReloadSimuls
import lila.hub.actorApi.map.Tell
diff --git a/modules/simul/src/main/SimulRepo.scala b/modules/simul/src/main/SimulRepo.scala
index 04babfcf9b..1700208ea7 100644
--- a/modules/simul/src/main/SimulRepo.scala
+++ b/modules/simul/src/main/SimulRepo.scala
@@ -7,7 +7,7 @@ import reactivemongo.core.commands._
import chess.Status
import chess.variant.Variant
import lila.db.BSON
-import lila.db.Types.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))))
}
diff --git a/modules/simul/src/main/Socket.scala b/modules/simul/src/main/Socket.scala
index 95ebb592cd..71376f8053 100644
--- a/modules/simul/src/main/Socket.scala
+++ b/modules/simul/src/main/Socket.scala
@@ -1,7 +1,6 @@
package lila.simul
import akka.actor._
-import play.api.libs.iteratee._
import play.api.libs.json._
import scala.concurrent.duration._
@@ -9,7 +8,7 @@ import actorApi._
import lila.common.LightUser
import lila.hub.actorApi.round.MoveEvent
import lila.hub.TimeBomb
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.{ SocketActor, History, Historical }
private[simul] final class Socket(
@@ -78,12 +77,9 @@ private[simul] final class Socket(
case Socket.GetUserIds => sender ! userIds
- case Join(uid, user) =>
- val (enumerator, channel) = Concurrent.broadcast[JsValue]
- val member = Member(channel, user)
+ case AddMember(uid, member) =>
addMember(uid, member)
notifyCrowd
- sender ! Connected(enumerator, member)
case Quit(uid) =>
quit(uid)
diff --git a/modules/simul/src/main/SocketHandler.scala b/modules/simul/src/main/SocketHandler.scala
index e88d0e157c..b3dc5455d1 100644
--- a/modules/simul/src/main/SocketHandler.scala
+++ b/modules/simul/src/main/SocketHandler.scala
@@ -1,16 +1,12 @@
package lila.simul
-import scala.concurrent.duration._
-
import akka.actor._
import akka.pattern.ask
import actorApi._
import lila.common.PimpedJson._
import lila.hub.actorApi.map._
-import lila.security.Flood
-import akka.actor.ActorSelection
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.Handler
import lila.user.User
import makeTimeout.short
@@ -19,35 +15,26 @@ private[simul] final class SocketHandler(
hub: lila.hub.Env,
socketHub: ActorRef,
chat: ActorSelection,
- flood: Flood,
- exists: Simul.ID => Fu[Boolean]) {
+ exists: Simul.ID => Fu[Boolean])(implicit val system: ActorSystem) {
def join(
simId: String,
uid: String,
- user: Option[User]): Fu[Option[JsSocketHandler]] =
- exists(simId) flatMap {
- _ ?? {
- for {
- socket ← socketHub ? Get(simId) mapTo manifest[ActorRef]
- join = Join(uid = uid, user = user)
- handler ← Handler(hub, socket, uid, join, user map (_.id)) {
- case Connected(enum, member) =>
- (controller(socket, simId, uid, member), enum, member)
+ user: Option[User]): Fu[Option[JsFlow]] = exists(simId) flatMap {
+ _ ?? {
+ socketHub ? Get(simId) mapTo manifest[ActorRef] map { socket =>
+ Handler.actorRef { out =>
+ val member = Member(out, user)
+ socket ! AddMember(uid, member)
+ Handler.props(hub, socket, member, uid, user.map(_.id)) {
+ case ("p", o) => o int "v" foreach { v => socket ! PingVersion(uid, v) }
+ case ("talk", o) => o str "d" foreach { text =>
+ member.userId foreach { userId =>
+ chat ! lila.chat.actorApi.UserTalk(simId, userId, text, socket)
+ }
+ }
}
- } yield handler.some
- }
- }
-
- private def controller(
- socket: ActorRef,
- simId: String,
- uid: String,
- member: Member): Handler.Controller = {
- case ("p", o) => o int "v" foreach { v => socket ! PingVersion(uid, v) }
- case ("talk", o) => o str "d" foreach { text =>
- member.userId foreach { userId =>
- chat ! lila.chat.actorApi.UserTalk(simId, userId, text, socket)
+ }.some
}
}
}
diff --git a/modules/simul/src/main/actorApi.scala b/modules/simul/src/main/actorApi.scala
index f4c646c03c..aeeb4706df 100644
--- a/modules/simul/src/main/actorApi.scala
+++ b/modules/simul/src/main/actorApi.scala
@@ -1,34 +1,33 @@
package lila.simul
package actorApi
+import akka.actor.ActorRef
+
+import lila.game.Game
import lila.socket.SocketMember
import lila.user.User
-import lila.game.Game
private[simul] case class Member(
- channel: JsChannel,
+ out: ActorRef,
userId: Option[String],
troll: Boolean) extends SocketMember
private[simul] object Member {
- def apply(channel: JsChannel, user: Option[User]): Member = Member(
- channel = channel,
+ def apply(out: ActorRef, user: Option[User]): Member = Member(
+ out = out,
userId = user map (_.id),
troll = user.??(_.troll))
}
+private[simul] case class AddMember(uid: String, member: Member)
private[simul] case class Messadata(trollish: Boolean = false)
-private[simul] case class Join(
- uid: String,
- user: Option[User])
private[simul] case class Talk(tourId: String, u: String, t: String, troll: Boolean)
private[simul] case class StartGame(game: Game, hostId: String)
private[simul] case class StartSimul(firstGame: Game, hostId: String)
private[simul] case class HostIsOn(gameId: String)
private[simul] case object Reload
private[simul] case object Aborted
-private[simul] case class Connected(enumerator: JsEnumerator, member: Member)
private[simul] case object NotifyCrowd
diff --git a/modules/site/src/main/Env.scala b/modules/site/src/main/Env.scala
index bff39ae737..f83f35f192 100644
--- a/modules/site/src/main/Env.scala
+++ b/modules/site/src/main/Env.scala
@@ -17,7 +17,7 @@ final class Env(
private val socket = system.actorOf(
Props(new Socket(timeout = SocketUidTtl)), name = SocketName)
- lazy val socketHandler = new SocketHandler(socket, hub)
+ lazy val socketHandler = new SocketHandler(socket, hub)(system)
}
object Env {
diff --git a/modules/site/src/main/Socket.scala b/modules/site/src/main/Socket.scala
index 69e7fece13..8163be4188 100644
--- a/modules/site/src/main/Socket.scala
+++ b/modules/site/src/main/Socket.scala
@@ -16,16 +16,11 @@ private[site] final class Socket(timeout: Duration) extends SocketActor[Member](
def receiveSpecific = {
- case Join(uid, username, tags) => {
- val (enumerator, channel) = Concurrent.broadcast[JsValue]
- val member = Member(channel, username, tags)
- addMember(uid, member)
- sender ! Connected(enumerator, member)
- }
+ case AddMember(uid, member) => addMember(uid, member)
case SendToFlag(flag, message) =>
members.values filter (_ hasFlag flag) foreach {
- _.channel push message
+ _ push message
}
}
}
diff --git a/modules/site/src/main/SocketHandler.scala b/modules/site/src/main/SocketHandler.scala
index 5ce078c98a..40dd23b241 100644
--- a/modules/site/src/main/SocketHandler.scala
+++ b/modules/site/src/main/SocketHandler.scala
@@ -1,26 +1,19 @@
package lila.site
-import scala.concurrent.duration._
-
import akka.actor._
import play.api.libs.json._
import actorApi._
-import lila.common.PimpedJson._
import lila.socket._
-import lila.socket.actorApi.StartWatching
private[site] final class SocketHandler(
socket: ActorRef,
- hub: lila.hub.Env) {
-
- def apply(
- uid: String,
- userId: Option[String],
- flag: Option[String]): Fu[JsSocketHandler] = {
+ hub: lila.hub.Env)(implicit val system: ActorSystem) {
- Handler(hub, socket, uid, Join(uid, userId, flag), userId) {
- case Connected(enum, member) => (Handler.emptyController, enum, member)
+ def apply(uid: String, userId: Option[String], flag: Option[String]): JsFlow =
+ Handler.actorRef { out =>
+ val member = Member(out, userId, flag)
+ socket ! AddMember(uid, member)
+ Handler.props(hub, socket, member, uid, userId)(Handler.emptyController)
}
- }
}
diff --git a/modules/site/src/main/actorApi.scala b/modules/site/src/main/actorApi.scala
index 7b1e0c0625..1d5e29710e 100644
--- a/modules/site/src/main/actorApi.scala
+++ b/modules/site/src/main/actorApi.scala
@@ -6,7 +6,7 @@ import play.api.libs.json._
import lila.socket.SocketMember
case class Member(
- channel: JsChannel,
+ out: akka.actor.ActorRef,
userId: Option[String],
flag: Option[String]) extends SocketMember {
@@ -14,6 +14,4 @@ case class Member(
def hasFlag(f: String) = flag ?? (f ==)
}
-
-case class Join(uid: String, userId: Option[String], flag: Option[String])
-private[site] case class Connected(enumerator: JsEnumerator, member: Member)
+case class AddMember(uid: String, member: Member)
diff --git a/modules/socket/src/main/Channel.scala b/modules/socket/src/main/Channel.scala
index 67c52c7708..af059679dd 100644
--- a/modules/socket/src/main/Channel.scala
+++ b/modules/socket/src/main/Channel.scala
@@ -2,7 +2,7 @@ package lila.socket
import actorApi.SocketLeave
import akka.actor._
-import play.api.libs.json.JsValue
+import play.api.libs.json.JsObject
final class Channel extends Actor {
@@ -36,5 +36,5 @@ object Channel {
case class Sub(member: SocketMember)
case class UnSub(member: SocketMember)
- case class Publish(msg: JsValue)
+ case class Publish(msg: JsObject)
}
diff --git a/modules/socket/src/main/Handler.scala b/modules/socket/src/main/Handler.scala
index ddae564bc9..7bd8cc615b 100644
--- a/modules/socket/src/main/Handler.scala
+++ b/modules/socket/src/main/Handler.scala
@@ -1,99 +1,100 @@
package lila.socket
-import akka.actor.ActorRef
+import akka.actor.{ ActorRef, Props, ActorSystem }
import akka.pattern.{ ask, pipe }
-import play.api.libs.iteratee.{ Iteratee, Enumerator }
+import akka.stream._
+import akka.stream.scaladsl._
import play.api.libs.json._
+import play.api.libs.streams.ActorFlow
import scala.concurrent.duration._
import actorApi._
import lila.common.PimpedJson._
import lila.hub.actorApi.relation.ReloadOnlineFriends
+import lila.socket.Socket.makeMessage
import makeTimeout.large
-import Step.openingWriter
object Handler {
type Controller = PartialFunction[(String, JsObject), Unit]
- type Connecter = PartialFunction[Any, (Controller, JsEnumerator, SocketMember)]
val emptyController: Controller = PartialFunction.empty
lazy val AnaRateLimit = new lila.memo.RateLimit(90, 60 seconds, "socket analysis move")
- def apply(
+ def actorRef(withOut: ActorRef => Props)(implicit system: ActorSystem): JsFlow =
+ ActorFlow.actorRef[JsObject, JsObject](withOut)
+
+ def props(
hub: lila.hub.Env,
socket: ActorRef,
+ member: SocketMember,
uid: String,
- join: Any,
- userId: Option[String])(connecter: Connecter): Fu[JsSocketHandler] = {
-
- def baseController(member: SocketMember): Controller = {
- case ("p", _) => socket ! Ping(uid)
- case ("following_onlines", _) => userId foreach { u =>
- hub.actor.relation ! ReloadOnlineFriends(u)
- }
- case ("startWatching", o) => o str "d" foreach { ids =>
- hub.actor.moveBroadcast ! StartWatching(uid, member, ids.split(' ').toSet)
- }
- case ("moveLat", o) => hub.channel.roundMoveTime ! (~(o boolean "d")).fold(
- Channel.Sub(member),
- Channel.UnSub(member))
- case ("anaMove", o) => AnaRateLimit(uid) {
- AnaMove parse o foreach { anaMove =>
- anaMove.step match {
- case scalaz.Success(step) =>
- member push lila.socket.Socket.makeMessage("step", Json.obj(
- "step" -> step.toJson,
- "path" -> anaMove.path
- ))
- case scalaz.Failure(err) =>
- member push lila.socket.Socket.makeMessage("stepFailure", err.toString)
- }
- }
+ userId: Option[String])(controller: Controller): Props = {
+ val control = controller orElse baseController(hub, socket, member, uid, userId)
+ lila.socket.SocketMemberActor.props(in =>
+ in str "t" foreach { t =>
+ control.lift(t -> in)
}
- case ("anaDrop", o) => AnaRateLimit(uid) {
- AnaDrop parse o foreach { anaDrop =>
- anaDrop.step match {
- case scalaz.Success(step) =>
- member push lila.socket.Socket.makeMessage("step", Json.obj(
- "step" -> step.toJson,
- "path" -> anaDrop.path
- ))
- case scalaz.Failure(err) =>
- member push lila.socket.Socket.makeMessage("stepFailure", err.toString)
- }
- }
- }
- case ("anaDests", o) => AnaRateLimit(uid) {
- AnaDests parse o match {
- case Some(req) =>
- member push lila.socket.Socket.makeMessage("dests", Json.obj(
- "dests" -> req.dests,
- "path" -> req.path
- ) ++ req.opening.?? { o =>
- Json.obj("opening" -> o)
- })
- case None =>
- member push lila.socket.Socket.makeMessage("destsFailure", "Bad dests request")
+ )
+ }
+
+ def baseController(
+ hub: lila.hub.Env,
+ socket: ActorRef,
+ member: SocketMember,
+ uid: String,
+ userId: Option[String]): Controller = {
+ case ("p", _) => socket ! Ping(uid)
+ case ("following_onlines", _) => userId foreach { u =>
+ hub.actor.relation ! ReloadOnlineFriends(u)
+ }
+ case ("startWatching", o) => o str "d" foreach { ids =>
+ hub.actor.moveBroadcast ! StartWatching(uid, member, ids.split(' ').toSet)
+ }
+ case ("moveLat", o) => hub.channel.roundMoveTime ! (~(o boolean "d")).fold(
+ Channel.Sub(member),
+ Channel.UnSub(member))
+ case ("anaMove", o) => AnaRateLimit(uid) {
+ AnaMove parse o foreach { anaMove =>
+ anaMove.step match {
+ case scalaz.Success(step) =>
+ member push makeMessage("step", Json.obj(
+ "step" -> step.toJson,
+ "path" -> anaMove.path
+ ))
+ case scalaz.Failure(err) =>
+ member push makeMessage("stepFailure", err.toString)
}
}
- case _ => // logwarn("Unhandled msg: " + msg)
}
-
- def iteratee(controller: Controller, member: SocketMember): JsIteratee = {
- val control = controller orElse baseController(member)
- Iteratee.foreach[JsValue](jsv =>
- jsv.asOpt[JsObject] foreach { obj =>
- obj str "t" foreach { t =>
- control.lift(t -> obj)
- }
+ case ("anaDrop", o) => AnaRateLimit(uid) {
+ AnaDrop parse o foreach { anaDrop =>
+ anaDrop.step match {
+ case scalaz.Success(step) =>
+ member push makeMessage("step", Json.obj(
+ "step" -> step.toJson,
+ "path" -> anaDrop.path
+ ))
+ case scalaz.Failure(err) =>
+ member push makeMessage("stepFailure", err.toString)
}
- ).map(_ => socket ! Quit(uid))
+ }
}
-
- socket ? join map connecter map {
- case (controller, enum, member) => iteratee(controller, member) -> enum
+ case ("anaDests", o) => AnaRateLimit(uid) {
+ import Step.openingWriter
+ AnaDests parse o match {
+ case Some(req) =>
+ member push makeMessage("dests", Json.obj(
+ "dests" -> req.dests,
+ "path" -> req.path
+ ) ++ req.opening.?? { o =>
+ Json.obj("opening" -> o)
+ })
+ case None =>
+ member push makeMessage("destsFailure", "Bad dests request")
+ }
}
+ case _ => // logwarn("Unhandled msg: " + msg)
}
}
diff --git a/modules/socket/src/main/SocketMember.scala b/modules/socket/src/main/SocketMember.scala
index 9d544729a4..b15d73c439 100644
--- a/modules/socket/src/main/SocketMember.scala
+++ b/modules/socket/src/main/SocketMember.scala
@@ -1,10 +1,11 @@
package lila.socket
-import play.api.libs.json.JsValue
+import akka.actor.{ ActorRef, PoisonPill }
+import play.api.libs.json.JsObject
trait SocketMember extends Ordered[SocketMember] {
- protected val channel: JsChannel
+ protected val out: ActorRef
val userId: Option[String]
val troll: Boolean
@@ -12,21 +13,17 @@ trait SocketMember extends Ordered[SocketMember] {
def compare(other: SocketMember) = ~userId compare ~other.userId
- def push(msg: JsValue) {
- channel push msg
- }
+ def push(msg: JsObject) = out ! msg
- def end {
- channel.end
- }
+ def end = out ! PoisonPill
}
object SocketMember {
- def apply(c: JsChannel): SocketMember = apply(c, none, false)
+ def apply(o: ActorRef): SocketMember = apply(o, none, false)
- def apply(c: JsChannel, uid: Option[String], tr: Boolean): SocketMember = new SocketMember {
- val channel = c
+ def apply(o: ActorRef, uid: Option[String], tr: Boolean): SocketMember = new SocketMember {
+ val out = o
val userId = uid
val troll = tr
}
diff --git a/modules/socket/src/main/SocketMemberActor.scala b/modules/socket/src/main/SocketMemberActor.scala
new file mode 100644
index 0000000000..35ba55fe6a
--- /dev/null
+++ b/modules/socket/src/main/SocketMemberActor.scala
@@ -0,0 +1,22 @@
+package lila.socket
+
+import akka.actor._
+import play.api.libs.json.JsObject
+
+// implements ActorFlow.actorRef out actor
+final class SocketMemberActor(
+ handler: JsObject => Unit) extends Actor {
+
+ def receive = {
+
+ case msg: JsObject => handler(msg)
+
+ case m => lila.log("socket").warn(s"SocketMemberActor received $m")
+ }
+}
+
+object SocketMemberActor {
+
+ def props(handler: JsObject => Unit) =
+ Props(new SocketMemberActor(handler))
+}
diff --git a/modules/socket/src/main/WithSocket.scala b/modules/socket/src/main/WithSocket.scala
index 6467c900b3..a3fa4d177f 100644
--- a/modules/socket/src/main/WithSocket.scala
+++ b/modules/socket/src/main/WithSocket.scala
@@ -1,14 +1,10 @@
package lila.socket
-import ornicar.scalalib.Zero
-import play.api.libs.iteratee.{ Iteratee, Enumerator }
-import play.api.libs.json._
-
+import akka.stream.scaladsl.Flow
+import play.api.libs.json.JsObject
+import play.api.libs.streams.ActorFlow
trait WithSocket {
- type JsChannel = play.api.libs.iteratee.Concurrent.Channel[JsValue]
- type JsEnumerator = Enumerator[JsValue]
- type JsIteratee = Iteratee[JsValue, _]
- type JsSocketHandler = (JsIteratee, JsEnumerator)
+ type JsFlow = Flow[JsObject, JsObject, _]
}
diff --git a/modules/socket/src/main/actorApi.scala b/modules/socket/src/main/actorApi.scala
index 741740805d..6c011a2410 100644
--- a/modules/socket/src/main/actorApi.scala
+++ b/modules/socket/src/main/actorApi.scala
@@ -2,11 +2,7 @@ package lila.socket
package actorApi
import play.api.libs.json.JsObject
-import akka.actor.ActorRef
-case class Connected[M <: SocketMember](
- enumerator: JsEnumerator,
- member: M)
case class Sync(uid: String, friends: List[String])
case class Ping(uid: String)
case class PingVersion(uid: String, version: Int)
diff --git a/modules/team/src/main/BSONHandlers.scala b/modules/team/src/main/BSONHandlers.scala
new file mode 100644
index 0000000000..99673e23ca
--- /dev/null
+++ b/modules/team/src/main/BSONHandlers.scala
@@ -0,0 +1,9 @@
+package lila.team
+
+private object BSONHandlers {
+
+ import lila.db.dsl.BSONJodaDateTimeHandler
+ implicit val TeamBSONHandler = reactivemongo.bson.Macros.handler[Team]
+ implicit val RequestBSONHandler = reactivemongo.bson.Macros.handler[Request]
+ implicit val MemberBSONHandler = reactivemongo.bson.Macros.handler[Member]
+}
diff --git a/modules/team/src/main/Cli.scala b/modules/team/src/main/Cli.scala
index b4be03c0f4..74193568b1 100644
--- a/modules/team/src/main/Cli.scala
+++ b/modules/team/src/main/Cli.scala
@@ -1,10 +1,11 @@
package lila.team
-import lila.db.api.$find
+import lila.db.dsl._
import lila.user.{ User, UserRepo }
-import tube.teamTube
-private[team] final class Cli(api: TeamApi) extends lila.common.Cli {
+private[team] final class Cli(api: TeamApi, coll: Colls) extends lila.common.Cli {
+
+ import BSONHandlers._
def process = {
@@ -21,12 +22,12 @@ private[team] final class Cli(api: TeamApi) extends lila.common.Cli {
}
private def perform(teamId: String)(op: Team => Funit): Fu[String] =
- $find byId teamId flatMap {
+ coll.team.byId[Team](teamId) flatMap {
_.fold(fufail[String]("Team not found")) { u => op(u) inject "Success" }
}
private def perform(teamId: String, userIds: List[String])(op: (Team, String) => Funit): Fu[String] =
- $find byId teamId flatMap {
+ coll.team.byId[Team](teamId) flatMap {
_.fold(fufail[String]("Team not found")) { team =>
UserRepo nameds userIds flatMap { users =>
users.map(user => {
diff --git a/modules/team/src/main/Colls.scala b/modules/team/src/main/Colls.scala
new file mode 100644
index 0000000000..daca0a498a
--- /dev/null
+++ b/modules/team/src/main/Colls.scala
@@ -0,0 +1,8 @@
+package lila.team
+
+import lila.db.dsl.Coll
+
+private final class Colls(
+ val team: Coll,
+ val request: Coll,
+ val member: Coll)
diff --git a/modules/team/src/main/DataForm.scala b/modules/team/src/main/DataForm.scala
index ce9fd1ebf4..37acae60cf 100644
--- a/modules/team/src/main/DataForm.scala
+++ b/modules/team/src/main/DataForm.scala
@@ -4,10 +4,11 @@ import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints._
-import lila.db.api.{ $count, $select }
-import tube.teamTube
+import lila.db.dsl._
-private[team] final class DataForm(val captcher: akka.actor.ActorSelection) extends lila.hub.CaptchedForm {
+private[team] final class DataForm(
+ teamColl: Coll,
+ val captcher: akka.actor.ActorSelection) extends lila.hub.CaptchedForm {
import lila.common.Form._
@@ -61,7 +62,7 @@ private[team] final class DataForm(val captcher: akka.actor.ActorSelection) exte
def createWithCaptcha = withCaptcha(create)
private def teamExists(setup: TeamSetup) =
- $count.exists[Team]($select(Team nameToId setup.trim.name))
+ teamColl.exists($id(Team nameToId setup.trim.name))
}
private[team] case class TeamSetup(
diff --git a/modules/team/src/main/Env.scala b/modules/team/src/main/Env.scala
index 6b98e75dbb..7e02648959 100644
--- a/modules/team/src/main/Env.scala
+++ b/modules/team/src/main/Env.scala
@@ -16,9 +16,15 @@ final class Env(config: Config, hub: lila.hub.Env, db: lila.db.Env) {
}
import settings._
- lazy val forms = new DataForm(hub.actor.captcher)
+ private[team] lazy val colls = new Colls(
+ team = db(CollectionTeam),
+ request = db(CollectionRequest),
+ member = db(CollectionMember))
+
+ lazy val forms = new DataForm(colls.team, hub.actor.captcher)
lazy val api = new TeamApi(
+ coll = colls,
cached = cached,
notifier = notifier,
forum = hub.actor.forum,
@@ -26,17 +32,14 @@ final class Env(config: Config, hub: lila.hub.Env, db: lila.db.Env) {
timeline = hub.actor.timeline)
lazy val paginator = new PaginatorBuilder(
+ coll = colls,
maxPerPage = PaginatorMaxPerPage,
maxUserPerPage = PaginatorMaxUserPerPage)
- lazy val cli = new Cli(api)
+ lazy val cli = new Cli(api, colls)
lazy val cached = new Cached
- private[team] lazy val teamColl = db(CollectionTeam)
- private[team] lazy val requestColl = db(CollectionRequest)
- private[team] lazy val memberColl = db(CollectionMember)
-
private lazy val notifier = new Notifier(
sender = NotifierSender,
messenger = hub.actor.messenger,
diff --git a/modules/team/src/main/Member.scala b/modules/team/src/main/Member.scala
index 8f1c8a96a8..c376bb9e21 100644
--- a/modules/team/src/main/Member.scala
+++ b/modules/team/src/main/Member.scala
@@ -5,13 +5,15 @@ import org.joda.time.DateTime
import lila.user.User
private[team] case class Member(
- id: String,
+ _id: String,
team: String,
user: String,
date: DateTime) {
def is(userId: String): Boolean = user == userId
def is(user: User): Boolean = is(user.id)
+
+ def id = _id
}
private[team] object Member {
@@ -19,18 +21,10 @@ private[team] object Member {
def makeId(team: String, user: String) = user + "@" + team
def make(team: String, user: String): Member = new Member(
- id = makeId(team, user),
- user = user,
- team = team,
+ _id = makeId(team, user),
+ user = user,
+ team = team,
date = DateTime.now)
-
- import lila.db.JsTube, JsTube.Helpers._
- import play.api.libs.json._
-
- private[team] lazy val tube = JsTube(
- (__.json update readDate('date)) andThen Json.reads[Member],
- Json.writes[Member] andThen (__.json update writeDate('date))
- )
}
case class MemberWithUser(member: Member, user: User) {
diff --git a/modules/team/src/main/MemberRepo.scala b/modules/team/src/main/MemberRepo.scala
index 41f560d851..a8ec7b30df 100644
--- a/modules/team/src/main/MemberRepo.scala
+++ b/modules/team/src/main/MemberRepo.scala
@@ -1,41 +1,44 @@
package lila.team
-import play.api.libs.json.Json
import reactivemongo.api._
import reactivemongo.bson._
-import lila.db.api._
-import tube.memberTube
+import lila.db.dsl._
object MemberRepo {
+ // dirty
+ private val coll = Env.current.colls.member
+
+ import BSONHandlers._
+
type ID = String
def userIdsByTeam(teamId: ID): Fu[Set[ID]] =
- memberTube.coll.distinct("user", BSONDocument("team" -> teamId).some) map lila.db.BSON.asStringSet
+ coll.distinct("user", $doc("team" -> teamId).some) map lila.db.BSON.asStringSet
def teamIdsByUser(userId: ID): Fu[Set[ID]] =
- memberTube.coll.distinct("team", BSONDocument("user" -> userId).some) map lila.db.BSON.asStringSet
+ coll.distinct("team", $doc("user" -> userId).some) map lila.db.BSON.asStringSet
def removeByteam(teamId: ID): Funit =
- $remove(teamQuery(teamId))
+ coll.remove(teamQuery(teamId)).void
def removeByUser(userId: ID): Funit =
- $remove(userQuery(userId))
+ coll.remove(userQuery(userId)).void
def exists(teamId: ID, userId: ID): Fu[Boolean] =
- $count.exists(selectId(teamId, userId))
+ coll.exists(selectId(teamId, userId))
def add(teamId: String, userId: String): Funit =
- $insert(Member.make(team = teamId, user = userId))
+ coll.insert(Member.make(team = teamId, user = userId)).void
def remove(teamId: String, userId: String): Funit =
- $remove(selectId(teamId, userId))
+ coll.remove(selectId(teamId, userId)).void
def countByTeam(teamId: String): Fu[Int] =
- $count(teamQuery(teamId))
+ coll.countSel(teamQuery(teamId))
- def selectId(teamId: ID, userId: ID) = $select(Member.makeId(teamId, userId))
- def teamQuery(teamId: ID) = Json.obj("team" -> teamId)
- def userQuery(userId: ID) = Json.obj("user" -> userId)
+ def selectId(teamId: ID, userId: ID) = $id(Member.makeId(teamId, userId))
+ def teamQuery(teamId: ID) = $doc("team" -> teamId)
+ def userQuery(userId: ID) = $doc("user" -> userId)
}
diff --git a/modules/team/src/main/PaginatorBuilder.scala b/modules/team/src/main/PaginatorBuilder.scala
index 35bb390e23..5e0fbb182c 100644
--- a/modules/team/src/main/PaginatorBuilder.scala
+++ b/modules/team/src/main/PaginatorBuilder.scala
@@ -1,21 +1,23 @@
package lila.team
import lila.common.paginator._
-import lila.db.api._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.db.paginator._
-import lila.user.tube.userTube
-import lila.user.User
-import tube._
+import lila.user.{ User, UserRepo }
private[team] final class PaginatorBuilder(
+ coll: Colls,
maxPerPage: Int,
maxUserPerPage: Int) {
+ import BSONHandlers._
+
def popularTeams(page: Int): Fu[Paginator[Team]] = Paginator(
- adapter = new Adapter[Team](
+ adapter = new Adapter(
+ collection = coll.team,
selector = TeamRepo.enabledQuery,
- sort = Seq(TeamRepo.sortPopular)),
+ projection = $empty,
+ sort = TeamRepo.sortPopular),
page,
maxPerPage)
@@ -29,8 +31,9 @@ private[team] final class PaginatorBuilder(
val nbResults = fuccess(team.nbMembers)
def slice(offset: Int, length: Int): Fu[Seq[MemberWithUser]] = for {
- members ← $find[Member]($query[Member](selector) sort sorting skip offset, length)
- users ← $find.byOrderedIds[User](members.map(_.user))
+ members ← coll.member.find(selector)
+ .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)
}
diff --git a/modules/team/src/main/Request.scala b/modules/team/src/main/Request.scala
index 39958f1048..e893d909dd 100644
--- a/modules/team/src/main/Request.scala
+++ b/modules/team/src/main/Request.scala
@@ -5,11 +5,13 @@ import org.joda.time.DateTime
import lila.user.User
case class Request(
- id: String,
+ _id: String,
team: String,
user: String,
message: String,
date: DateTime) {
+
+ def id = _id
}
object Request {
@@ -17,19 +19,11 @@ object Request {
def makeId(team: String, user: String) = user + "@" + team
def make(team: String, user: String, message: String): Request = new Request(
- id = makeId(team, user),
+ _id = makeId(team, user),
user = user,
team = team,
message = message.trim,
date = DateTime.now)
-
- import lila.db.JsTube, JsTube.Helpers._
- import play.api.libs.json._
-
- private[team] lazy val tube = JsTube(
- (__.json update readDate('date)) andThen Json.reads[Request],
- Json.writes[Request] andThen (__.json update writeDate('date))
- )
}
case class RequestWithUser(request: Request, user: User) {
diff --git a/modules/team/src/main/RequestRepo.scala b/modules/team/src/main/RequestRepo.scala
index a60025a584..6dcf5715c0 100644
--- a/modules/team/src/main/RequestRepo.scala
+++ b/modules/team/src/main/RequestRepo.scala
@@ -1,34 +1,37 @@
package lila.team
-import play.api.libs.json.Json
import reactivemongo.api._
-import lila.db.api._
-import tube.requestTube
+import lila.db.dsl._
object RequestRepo {
+ // dirty
+ private val coll = Env.current.colls.request
+
+ import BSONHandlers._
+
type ID = String
- def exists(teamId: ID, userId: ID): Fu[Boolean] =
- $count.exists(selectId(teamId, userId))
+ def exists(teamId: ID, userId: ID): Fu[Boolean] =
+ coll.exists(selectId(teamId, userId))
- def find(teamId: ID, userId: ID): Fu[Option[Request]] =
- $find.one(selectId(teamId, userId))
+ def find(teamId: ID, userId: ID): Fu[Option[Request]] =
+ coll.uno[Request](selectId(teamId, userId))
- def countByTeam(teamId: ID): Fu[Int] =
- $count(teamQuery(teamId))
+ def countByTeam(teamId: ID): Fu[Int] =
+ coll.countSel(teamQuery(teamId))
- def countByTeams(teamIds: List[ID]): Fu[Int] =
- $count(teamsQuery(teamIds))
+ def countByTeams(teamIds: List[ID]): Fu[Int] =
+ coll.countSel(teamsQuery(teamIds))
- def findByTeam(teamId: ID): Fu[List[Request]] =
- $find(teamQuery(teamId))
+ def findByTeam(teamId: ID): Fu[List[Request]] =
+ coll.list[Request](teamQuery(teamId))
- def findByTeams(teamIds: List[ID]): Fu[List[Request]] =
- $find(teamsQuery(teamIds))
+ def findByTeams(teamIds: List[ID]): Fu[List[Request]] =
+ coll.list[Request](teamsQuery(teamIds))
- def selectId(teamId: ID, userId: ID) = $select(Request.makeId(teamId, userId))
- def teamQuery(teamId: ID) = Json.obj("team" -> teamId)
- def teamsQuery(teamIds: List[ID]) = Json.obj("team" -> $in(teamIds))
+ def selectId(teamId: ID, userId: ID) = $id(Request.makeId(teamId, userId))
+ def teamQuery(teamId: ID) = $doc("team" -> teamId)
+ def teamsQuery(teamIds: List[ID]) = $doc("team".$in(teamIds:_*))
}
diff --git a/modules/team/src/main/Team.scala b/modules/team/src/main/Team.scala
index d566767a15..4edfbaf5c1 100644
--- a/modules/team/src/main/Team.scala
+++ b/modules/team/src/main/Team.scala
@@ -6,7 +6,7 @@ import ornicar.scalalib.Random
import lila.user.User
case class Team(
- id: String, // also the url slug
+ _id: String, // also the url slug
name: String,
location: Option[String],
description: String,
@@ -16,6 +16,8 @@ case class Team(
createdAt: DateTime,
createdBy: String) {
+ def id = _id
+
def slug = id
def disabled = !enabled
@@ -31,7 +33,7 @@ object Team {
description: String,
open: Boolean,
createdBy: User): Team = new Team(
- id = nameToId(name),
+ _id = nameToId(name),
name = name,
location = location,
description = description,
@@ -45,12 +47,4 @@ object Team {
// if most chars are not latin, go for random slug
(slug.size > (name.size / 2)).fold(slug, Random nextStringUppercase 8)
}
-
- import lila.db.JsTube, JsTube.Helpers._
- import play.api.libs.json._
-
- private[team] lazy val tube = JsTube(
- (__.json update readDate('createdAt)) andThen Json.reads[Team],
- Json.writes[Team] andThen (__.json update writeDate('createdAt))
- )
}
diff --git a/modules/team/src/main/TeamApi.scala b/modules/team/src/main/TeamApi.scala
index f69822406e..9f6b2c5bf2 100644
--- a/modules/team/src/main/TeamApi.scala
+++ b/modules/team/src/main/TeamApi.scala
@@ -2,26 +2,27 @@ package lila.team
import actorApi._
import akka.actor.ActorSelection
-import lila.db.api._
+import lila.db.dsl._
import lila.hub.actorApi.forum.MakeTeam
import lila.hub.actorApi.timeline.{ Propagate, TeamJoin, TeamCreate }
-import lila.user.tube.userTube
-import lila.user.{ User, UserContext }
+import lila.user.{ User, UserRepo, UserContext }
import org.joda.time.Period
-import tube._
final class TeamApi(
+ coll: Colls,
cached: Cached,
notifier: Notifier,
forum: ActorSelection,
indexer: ActorSelection,
timeline: ActorSelection) {
+ import BSONHandlers._
+
val creationPeriod = Period weeks 1
- def team(id: String) = $find.byId[Team](id)
+ def team(id: String) = coll.team.byId[Team](id)
- def request(id: String) = $find.byId[Request](id)
+ def request(id: String) = coll.request.byId[Request](id)
def create(setup: TeamSetup, me: User): Option[Fu[Team]] = me.canTeam option {
val s = setup.trim
@@ -31,7 +32,7 @@ final class TeamApi(
description = s.description,
open = s.isOpen,
createdBy = me)
- $insert(team) >>
+ coll.team.insert(team) >>
MemberRepo.add(team.id, me.id) >>- {
(cached.teamIdsCache invalidate me.id)
(forum ! MakeTeam(team.id, team.name))
@@ -46,10 +47,12 @@ final class TeamApi(
team.copy(
location = e.location,
description = e.description,
- open = e.isOpen) |> { team => $update(team) >>- (indexer ! InsertTeam(team)) }
+ open = e.isOpen) |> { team =>
+ coll.team.update($id(team.id), team).void >>- (indexer ! InsertTeam(team))
+ }
}
- def mine(me: User): Fu[List[Team]] = $find.byIds[Team](cached teamIds me.id)
+ def mine(me: User): Fu[List[Team]] = coll.team.byIds[Team](cached teamIds me.id)
def hasTeams(me: User): Boolean = cached.teamIds(me.id).nonEmpty
@@ -58,7 +61,7 @@ final class TeamApi(
def requestsWithUsers(team: Team): Fu[List[RequestWithUser]] = for {
requests ← RequestRepo findByTeam team.id
- users ← $find.byOrderedIds[User](requests map (_.user))
+ users ← UserRepo byOrderedIds requests.map(_.user)
} yield requests zip users map {
case (request, user) => RequestWithUser(request, user)
}
@@ -66,13 +69,13 @@ final class TeamApi(
def requestsWithUsers(user: User): Fu[List[RequestWithUser]] = for {
teamIds ← TeamRepo teamIdsByCreator user.id
requests ← RequestRepo findByTeams teamIds
- users ← $find.byOrderedIds[User](requests map (_.user))
+ users ← UserRepo byOrderedIds requests.map(_.user)
} yield requests zip users map {
case (request, user) => RequestWithUser(request, user)
}
def join(teamId: String)(implicit ctx: UserContext): Fu[Option[Requesting]] = for {
- teamOption ← $find.byId[Team](teamId)
+ teamOption ← coll.team.byId[Team](teamId)
result ← ~(teamOption |@| ctx.me.filter(_.canTeam))({
case (team, user) if team.open =>
(doJoin(team, user.id) inject Joined(team).some): Fu[Option[Requesting]]
@@ -82,7 +85,7 @@ final class TeamApi(
} yield result
def requestable(teamId: String, user: User): Fu[Option[Team]] = for {
- teamOption ← $find.byId[Team](teamId)
+ teamOption ← coll.team.byId[Team](teamId)
able ← teamOption.??(requestable(_, user))
} yield teamOption filter (_ => able)
@@ -97,14 +100,14 @@ final class TeamApi(
_ ?? {
val request = Request.make(team = team.id, user = user.id, message = setup.message)
val rwu = RequestWithUser(request, user)
- $insert(request) >> (cached.nbRequests remove team.createdBy)
+ coll.request.insert(request).void >> (cached.nbRequests remove team.createdBy)
}
}
def processRequest(team: Team, request: Request, accept: Boolean): Funit = for {
- _ ← $remove(request)
+ _ ← coll.request.remove(request)
_ ← cached.nbRequests remove team.createdBy
- userOption ← $find.byId[User](request.user)
+ userOption ← UserRepo byId request.user
_ ← userOption.filter(_ => accept).??(user =>
doJoin(team, user.id) >>- notifier.acceptRequest(team, request)
)
@@ -119,7 +122,7 @@ final class TeamApi(
} recover lila.db.recoverDuplicateKey(e => ())
def quit(teamId: String)(implicit ctx: UserContext): Fu[Option[Team]] = for {
- teamOption ← $find.byId[Team](teamId)
+ teamOption ← coll.team.byId[Team](teamId)
result ← ~(teamOption |@| ctx.me)({
case (team, user) => doQuit(team, user.id) inject team.some
})
@@ -136,14 +139,14 @@ final class TeamApi(
def kick(team: Team, userId: String): Funit = doQuit(team, userId)
def enable(team: Team): Funit =
- TeamRepo.enable(team) >>- (indexer ! InsertTeam(team))
+ TeamRepo.enable(team).void >>- (indexer ! InsertTeam(team))
def disable(team: Team): Funit =
- TeamRepo.disable(team) >>- (indexer ! RemoveTeam(team.id))
+ TeamRepo.disable(team).void >>- (indexer ! RemoveTeam(team.id))
// delete for ever, with members but not forums
def delete(team: Team): Funit =
- $remove(team) >>
+ coll.team.remove($id(team.id)) >>
MemberRepo.removeByteam(team.id) >>-
(indexer ! RemoveTeam(team.id))
@@ -159,10 +162,13 @@ final class TeamApi(
def nbRequests(teamId: String) = cached nbRequests teamId
+ import play.api.libs.iteratee._
def recomputeNbMembers =
- $enumerate.over[Team]($query.all[Team]) { team =>
- MemberRepo.countByTeam(team.id) flatMap { nb =>
- $update.field[String, Team, Int](team.id, "nbMembers", nb)
+ coll.team.find($empty).cursor[Team]()
+ .enumerate(Int.MaxValue, stopOnError = true) |>>>
+ Iteratee.foldM[Team, Unit](()) {
+ case (_, team) => MemberRepo.countByTeam(team.id) flatMap { nb =>
+ coll.team.updateField($id(team.id), "nbMembers", nb).void
+ }
}
- }
}
diff --git a/modules/team/src/main/TeamRepo.scala b/modules/team/src/main/TeamRepo.scala
index 73f7571091..2cfa2c939b 100644
--- a/modules/team/src/main/TeamRepo.scala
+++ b/modules/team/src/main/TeamRepo.scala
@@ -1,50 +1,56 @@
package lila.team
import org.joda.time.{ DateTime, Period }
-import play.api.libs.json.Json
-import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter
import reactivemongo.api._
import reactivemongo.bson._
-import lila.db.api._
-import lila.user.User
-import tube.teamTube
+import lila.db.dsl._
+import lila.user.{ User, UserRepo }
object TeamRepo {
+ // dirty
+ private val coll = Env.current.colls.team
+
+ import BSONHandlers._
+
type ID = String
+ def byOrderedIds(ids: Seq[String]) = coll.byOrderedIds[Team](ids)(_.id)
+
+ def cursor(selector: Bdoc) = coll.find(selector).cursor[Team]()
+
def owned(id: String, createdBy: String): Fu[Option[Team]] =
- $find.one($select(id) ++ Json.obj("createdBy" -> createdBy))
+ coll.uno[Team]($id(id) ++ $doc("createdBy" -> createdBy))
def teamIdsByCreator(userId: String): Fu[List[String]] =
- teamTube.coll.distinct("_id", BSONDocument("createdBy" -> userId).some) map lila.db.BSON.asStrings
+ coll.distinct("_id", BSONDocument("createdBy" -> userId).some) map lila.db.BSON.asStrings
def name(id: String): Fu[Option[String]] =
- $primitive.one($select(id), "name")(_.asOpt[String])
+ coll.primitiveOne[String]($id(id), "name")
def userHasCreatedSince(userId: String, duration: Period): Fu[Boolean] =
- $count.exists(Json.obj(
- "createdAt" -> $gt($date(DateTime.now minus duration)),
+ coll.exists($doc(
+ "createdAt" $gt DateTime.now.minus(duration),
"createdBy" -> userId
))
def ownerOf(teamId: String): Fu[Option[String]] =
- $primitive.one($select(teamId), "createdBy")(_.asOpt[String])
+ coll.primitiveOne[String]($id(teamId), "createdBy")
def incMembers(teamId: String, by: Int): Funit =
- $update($select(teamId), $inc("nbMembers" -> by))
+ coll.update($id(teamId), $inc("nbMembers" -> by)).void
- def enable(team: Team) = $update.field(team.id, "enabled", true)
+ def enable(team: Team) = coll.updateField($id(team.id), "enabled", true)
- def disable(team: Team) = $update.field(team.id, "enabled", false)
+ def disable(team: Team) = coll.updateField($id(team.id), "enabled", false)
def addRequest(teamId: String, request: Request): Funit =
- $update(
- $select(teamId) ++ Json.obj("requests.user" -> $ne(request.user)),
- $push("requests", request.user))
+ coll.update(
+ $id(teamId) ++ $doc("requests.user" $ne request.user),
+ $push("requests", request.user)).void
- val enabledQuery = Json.obj("enabled" -> true)
+ val enabledQuery = $doc("enabled" -> true)
val sortPopular = $sort desc "nbMembers"
}
diff --git a/modules/team/src/main/package.scala b/modules/team/src/main/package.scala
index 95584b460f..6d2b3c0891 100644
--- a/modules/team/src/main/package.scala
+++ b/modules/team/src/main/package.scala
@@ -2,14 +2,5 @@ package lila
package object team extends PackageObject with WithPlay {
- object tube {
-
- implicit lazy val teamTube = Team.tube inColl Env.current.teamColl
-
- private[team] implicit lazy val requestTube = Request.tube inColl Env.current.requestColl
-
- private[team] implicit lazy val memberTube = Member.tube inColl Env.current.memberColl
- }
-
private[team] def logger = lila.log("team")
}
diff --git a/modules/teamSearch/src/main/Env.scala b/modules/teamSearch/src/main/Env.scala
index fd07ac2650..0c47917bda 100644
--- a/modules/teamSearch/src/main/Env.scala
+++ b/modules/teamSearch/src/main/Env.scala
@@ -3,9 +3,8 @@ package lila.teamSearch
import akka.actor._
import com.typesafe.config.Config
-import lila.db.api.{ $find, $cursor }
+import lila.db.dsl._
import lila.search._
-import lila.team.tube.teamTube
final class Env(
config: Config,
@@ -18,7 +17,7 @@ final class Env(
private val client = makeClient(Index(IndexName))
- val api = new TeamSearchApi(client, $find.byOrderedIds[lila.team.Team] _)
+ val api = new TeamSearchApi(client)
def apply(text: String, page: Int) = paginatorBuilder(Query(text), page)
diff --git a/modules/teamSearch/src/main/TeamSearchApi.scala b/modules/teamSearch/src/main/TeamSearchApi.scala
index d7327236f6..9f7fe5becc 100644
--- a/modules/teamSearch/src/main/TeamSearchApi.scala
+++ b/modules/teamSearch/src/main/TeamSearchApi.scala
@@ -2,17 +2,16 @@ package lila.teamSearch
import lila.search._
import lila.team.actorApi._
-import lila.team.Team
+import lila.team.{ Team, TeamRepo }
import play.api.libs.json._
+import play.api.libs.iteratee._
-final class TeamSearchApi(
- client: ESClient,
- fetcher: Seq[String] => Fu[List[Team]]) extends SearchReadApi[Team, Query] {
+final class TeamSearchApi(client: ESClient) extends SearchReadApi[Team, Query] {
def search(query: Query, from: From, size: Size) =
client.search(query, from, size) flatMap { res =>
- fetcher(res.ids)
+ TeamRepo byOrderedIds res.ids
}
def count(query: Query) = client.count(query) map (_.count)
@@ -28,11 +27,13 @@ final class TeamSearchApi(
def reset = client match {
case c: ESClientHttp => c.putMapping >> {
lila.log("teamSearch").info(s"Index to ${c.index.name}")
- import lila.db.api._
- import lila.team.tube.teamTube
- $enumerate.bulk[Option[Team]]($query[Team](Json.obj("enabled" -> true)), 300) { teamOptions =>
- c.storeBulk(teamOptions.flatten map (t => Id(t.id) -> toDoc(t)))
- }
+ import lila.db.dsl._
+ TeamRepo.cursor($doc("enabled" -> true))
+ .enumerateBulks(Int.MaxValue) |>>>
+ Iteratee.foldM[Iterator[Team], Unit](()) {
+ case (_, teams) =>
+ c.storeBulk(teams.toList map (t => Id(t.id) -> toDoc(t)))
+ }
}
case _ => funit
}
diff --git a/modules/timeline/src/main/Entry.scala b/modules/timeline/src/main/Entry.scala
index cc8a9b1f45..9bae94961c 100644
--- a/modules/timeline/src/main/Entry.scala
+++ b/modules/timeline/src/main/Entry.scala
@@ -3,70 +3,125 @@ package lila.timeline
import org.joda.time.DateTime
import play.api.libs.json._
import reactivemongo.bson._
+import scala.util.{ Try, Success, Failure }
+import lila.db.dsl._
import lila.hub.actorApi.timeline._
-import lila.hub.actorApi.timeline.atomFormat._
case class Entry(
_id: BSONObjectID,
users: List[String],
typ: String,
chan: Option[String],
- data: JsObject,
+ data: Bdoc,
date: DateTime) {
import Entry._
+ import atomBsonHandlers._
def similarTo(other: Entry) = typ == other.typ && data == other.data
- lazy val decode: Option[Atom] = (typ match {
- case "follow" => Json.fromJson[Follow](data)
- case "team-join" => Json.fromJson[TeamJoin](data)
- case "team-create" => Json.fromJson[TeamCreate](data)
- case "forum-post" => Json.fromJson[ForumPost](data)
- case "note-create" => Json.fromJson[NoteCreate](data)
- case "tour-join" => Json.fromJson[TourJoin](data)
- case "qa-question" => Json.fromJson[QaQuestion](data)
- case "qa-answer" => Json.fromJson[QaAnswer](data)
- case "qa-comment" => Json.fromJson[QaComment](data)
- case "game-end" => Json.fromJson[GameEnd](data)
- case "simul-create" => Json.fromJson[SimulCreate](data)
- case "simul-join" => Json.fromJson[SimulJoin](data)
- }).asOpt
+ lazy val decode: Option[Atom] = Try(typ match {
+ case "follow" => followHandler.read(data)
+ case "team-join" => teamJoinHandler.read(data)
+ case "team-create" => teamCreateHandler.read(data)
+ case "forum-post" => forumPostHandler.read(data)
+ case "note-create" => noteCreateHandler.read(data)
+ case "tour-join" => tourJoinHandler.read(data)
+ case "qa-question" => qaQuestionHandler.read(data)
+ case "qa-answer" => qaAnswerHandler.read(data)
+ case "qa-comment" => qaCommentHandler.read(data)
+ case "game-end" => gameEndHandler.read(data)
+ case "simul-create" => simulCreateHandler.read(data)
+ case "simul-join" => simulJoinHandler.read(data)
+ case _ => sys error s"Unhandled atom type: $typ"
+ }) match {
+ case Success(atom) => Some(atom)
+ case Failure(err) =>
+ lila.log("timeline").warn(err.getMessage)
+ none
+ }
def okForKid = decode ?? (_.okForKid)
}
object Entry {
- private[timeline] def make(users: List[String], data: Atom): Option[Entry] = (data match {
- case d: Follow => "follow" -> Json.toJson(d)
- case d: TeamJoin => "team-join" -> Json.toJson(d)
- case d: TeamCreate => "team-create" -> Json.toJson(d)
- case d: ForumPost => "forum-post" -> Json.toJson(d)
- case d: NoteCreate => "note-create" -> Json.toJson(d)
- case d: TourJoin => "tour-join" -> Json.toJson(d)
- case d: QaQuestion => "qa-question" -> Json.toJson(d)
- case d: QaAnswer => "qa-answer" -> Json.toJson(d)
- case d: QaComment => "qa-comment" -> Json.toJson(d)
- case d: GameEnd => "game-end" -> Json.toJson(d)
- case d: SimulCreate => "simul-create" -> Json.toJson(d)
- case d: SimulJoin => "simul-join" -> Json.toJson(d)
- }) match {
- case (typ, json) => json.asOpt[JsObject] map {
- new Entry(BSONObjectID.generate, users, typ, data.channel.some, _, DateTime.now)
+ private def toBson[A](data: A)(implicit writer: BSONDocumentWriter[A]) = writer write data
+ private def fromBson[A](bson: Bdoc)(implicit reader: BSONDocumentReader[A]) = reader read bson
+
+ private[timeline] def make(users: List[String], data: Atom): Entry = {
+ import atomBsonHandlers._
+ data match {
+ case d: Follow => "follow" -> toBson(d)
+ case d: TeamJoin => "team-join" -> toBson(d)
+ case d: TeamCreate => "team-create" -> toBson(d)
+ case d: ForumPost => "forum-post" -> toBson(d)
+ case d: NoteCreate => "note-create" -> toBson(d)
+ case d: TourJoin => "tour-join" -> toBson(d)
+ case d: QaQuestion => "qa-question" -> toBson(d)
+ case d: QaAnswer => "qa-answer" -> toBson(d)
+ case d: QaComment => "qa-comment" -> toBson(d)
+ case d: GameEnd => "game-end" -> toBson(d)
+ case d: SimulCreate => "simul-create" -> toBson(d)
+ case d: SimulJoin => "simul-join" -> toBson(d)
+ }
+ } match {
+ case (typ, bson) =>
+ new Entry(BSONObjectID.generate, users, typ, data.channel.some, bson, DateTime.now)
+ }
+
+ object atomBsonHandlers {
+ implicit val followHandler = Macros.handler[Follow]
+ implicit val teamJoinHandler = Macros.handler[TeamJoin]
+ implicit val teamCreateHandler = Macros.handler[TeamCreate]
+ implicit val forumPostHandler = Macros.handler[ForumPost]
+ implicit val noteCreateHandler = Macros.handler[NoteCreate]
+ implicit val tourJoinHandler = Macros.handler[TourJoin]
+ implicit val qaQuestionHandler = Macros.handler[QaQuestion]
+ implicit val qaAnswerHandler = Macros.handler[QaAnswer]
+ implicit val qaCommentHandler = Macros.handler[QaComment]
+ implicit val gameEndHandler = Macros.handler[GameEnd]
+ implicit val simulCreateHandler = Macros.handler[SimulCreate]
+ implicit val simulJoinHandler = Macros.handler[SimulJoin]
+ }
+
+ object atomJsonWrite {
+ implicit val followWrite = Json.writes[Follow]
+ implicit val teamJoinWrite = Json.writes[TeamJoin]
+ implicit val teamCreateWrite = Json.writes[TeamCreate]
+ implicit val forumPostWrite = Json.writes[ForumPost]
+ implicit val noteCreateWrite = Json.writes[NoteCreate]
+ implicit val tourJoinWrite = Json.writes[TourJoin]
+ implicit val qaQuestionWrite = Json.writes[QaQuestion]
+ implicit val qaAnswerWrite = Json.writes[QaAnswer]
+ implicit val qaCommentWrite = Json.writes[QaComment]
+ implicit val gameEndWrite = Json.writes[GameEnd]
+ implicit val simulCreateWrite = Json.writes[SimulCreate]
+ implicit val simulJoinWrite = Json.writes[SimulJoin]
+ implicit val atomWrite = Writes[Atom] {
+ case d: Follow => followWrite writes d
+ case d: TeamJoin => teamJoinWrite writes d
+ case d: TeamCreate => teamCreateWrite writes d
+ case d: ForumPost => forumPostWrite writes d
+ case d: NoteCreate => noteCreateWrite writes d
+ case d: TourJoin => tourJoinWrite writes d
+ case d: QaQuestion => qaQuestionWrite writes d
+ case d: QaAnswer => qaAnswerWrite writes d
+ case d: QaComment => qaCommentWrite writes d
+ case d: GameEnd => gameEndWrite writes d
+ case d: SimulCreate => simulCreateWrite writes d
+ case d: SimulJoin => simulJoinWrite writes d
}
}
- import play.modules.reactivemongo.json.ImplicitBSONHandlers._
- import lila.db.BSON.BSONJodaDateTimeHandler
- import reactivemongo.bson.Macros
implicit val EntryBSONHandler = Macros.handler[Entry]
implicit val entryWrites = OWrites[Entry] { e =>
+ import atomJsonWrite._
Json.obj(
"type" -> e.typ,
- "data" -> e.data,
+ "data" -> e.decode,
"date" -> e.date)
}
}
diff --git a/modules/timeline/src/main/EntryRepo.scala b/modules/timeline/src/main/EntryRepo.scala
index 7d7304c2ec..1ff54f3ca9 100644
--- a/modules/timeline/src/main/EntryRepo.scala
+++ b/modules/timeline/src/main/EntryRepo.scala
@@ -1,7 +1,6 @@
package lila.timeline
-import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Types.Coll
+import lila.db.dsl._
import org.joda.time.DateTime
import reactivemongo.bson._
@@ -16,23 +15,23 @@ private[timeline] final class EntryRepo(coll: Coll, userMax: Int) {
userEntries(userId, nb)
private def userEntries(userId: String, max: Int): Fu[List[Entry]] =
- coll.find(BSONDocument("users" -> userId))
- .sort(BSONDocument("date" -> -1))
+ 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(BSONDocument(
+ coll.find($doc(
"typ" -> typ,
- "date" -> BSONDocument("$gt" -> since)
+ "date" -> $doc("$gt" -> since)
)).cursor[Entry]()
- .collect[List]()
+ .gather[List]()
def channelUserIdRecentExists(channel: String, userId: String): Fu[Boolean] =
- coll.count(BSONDocument(
+ coll.count($doc(
"users" -> userId,
"chan" -> channel,
- "date" -> BSONDocument("$gt" -> DateTime.now.minusDays(7))
+ "date" $gt DateTime.now.minusDays(7)
).some) map (0 !=)
def insert(entry: Entry) = coll insert entry void
diff --git a/modules/timeline/src/main/Push.scala b/modules/timeline/src/main/Push.scala
index 0b5a49e4e0..4b26ff5fb3 100644
--- a/modules/timeline/src/main/Push.scala
+++ b/modules/timeline/src/main/Push.scala
@@ -55,15 +55,13 @@ private[timeline] final class Push(
}
}
- private def makeEntry(users: List[String], data: Atom): Fu[Entry] =
- Entry.make(users, data).fold(
- fufail[Entry]("[timeline] invalid entry data " + data)
- ) { entry =>
- entryRepo.findRecent(entry.typ, DateTime.now minusMinutes 50) flatMap { entries =>
- entries.exists(_ similarTo entry) fold (
- fufail[Entry]("[timeline] a similar entry already exists"),
- entryRepo insert entry inject entry
- )
- }
- }
+ private def makeEntry(users: List[String], data: Atom): Fu[Entry] = {
+ val entry = Entry.make(users, data)
+ entryRepo.findRecent(entry.typ, DateTime.now minusMinutes 50) flatMap { entries =>
+ entries.exists(_ similarTo entry) fold (
+ fufail[Entry]("[timeline] a similar entry already exists"),
+ entryRepo insert entry inject entry
+ )
+ }
+ }
}
diff --git a/modules/timeline/src/main/UnsubApi.scala b/modules/timeline/src/main/UnsubApi.scala
index 3734ee33be..ef1940027d 100644
--- a/modules/timeline/src/main/UnsubApi.scala
+++ b/modules/timeline/src/main/UnsubApi.scala
@@ -2,14 +2,13 @@ package lila.timeline
import reactivemongo.bson._
-import lila.db.Types.Coll
+import lila.db.dsl._
private[timeline] final class UnsubApi(coll: Coll) {
private def makeId(channel: String, userId: String) = s"$userId@$channel"
- private def select(channel: String, userId: String) =
- BSONDocument("_id" -> makeId(channel, userId))
+ private def select(channel: String, userId: String) = $id(makeId(channel, userId))
def set(channel: String, userId: String, v: Boolean): Funit = {
if (v) coll.insert(select(channel, userId)).void
@@ -22,8 +21,8 @@ private[timeline] final class UnsubApi(coll: Coll) {
coll.count(select(channel, userId).some) map (0 !=)
def filterUnsub(channel: String, userIds: List[String]): Fu[List[String]] =
- coll.distinct("_id", BSONDocument(
- "_id" -> BSONDocument("$in" -> userIds.map { makeId(channel, _) })
+ coll.distinct("_id", $doc(
+ "_id" $in (userIds.map { makeId(channel, _) }: _*)
).some) map lila.db.BSON.asStrings map { unsubs =>
userIds diff unsubs.map(_ takeWhile ('@' !=))
}
diff --git a/modules/tournament/src/main/BSONHandlers.scala b/modules/tournament/src/main/BSONHandlers.scala
index c646e07d8a..3ed4b6e686 100644
--- a/modules/tournament/src/main/BSONHandlers.scala
+++ b/modules/tournament/src/main/BSONHandlers.scala
@@ -165,5 +165,6 @@ object BSONHandlers {
}
import LeaderboardApi.ChartData.AggregationResult
- implicit val leaderboardAggregationResultBSONHandler = Macros.handler[AggregationResult]
+ implicit val leaderboardAggregationResultBSONHandler =
+ BSON.LoggingHandler(logger)(Macros.handler[AggregationResult])
}
diff --git a/modules/tournament/src/main/Env.scala b/modules/tournament/src/main/Env.scala
index 9f0c879062..282236e26d 100644
--- a/modules/tournament/src/main/Env.scala
+++ b/modules/tournament/src/main/Env.scala
@@ -17,7 +17,6 @@ final class Env(
system: ActorSystem,
db: lila.db.Env,
mongoCache: lila.memo.MongoCache.Builder,
- flood: lila.security.Flood,
hub: lila.hub.Env,
roundMap: ActorRef,
roundSocketHub: ActorSelection,
@@ -80,8 +79,7 @@ final class Env(
lazy val socketHandler = new SocketHandler(
hub = hub,
socketHub = socketHub,
- chat = hub.actor.chat,
- flood = flood)
+ chat = hub.actor.chat)(system)
lazy val winners = new Winners(
mongoCache = mongoCache,
@@ -171,7 +169,6 @@ object Env {
system = lila.common.PlayApp.system,
db = lila.db.Env.current,
mongoCache = lila.memo.Env.current.mongoCache,
- flood = lila.security.Env.current.flood,
hub = lila.hub.Env.current,
roundMap = lila.round.Env.current.roundMap,
roundSocketHub = lila.hub.Env.current.socket.round,
diff --git a/modules/tournament/src/main/LeaderboardApi.scala b/modules/tournament/src/main/LeaderboardApi.scala
index abb6142ec0..18c0d9cc59 100644
--- a/modules/tournament/src/main/LeaderboardApi.scala
+++ b/modules/tournament/src/main/LeaderboardApi.scala
@@ -8,8 +8,8 @@ import scala.concurrent.duration._
import lila.common.Maths
import lila.common.paginator.Paginator
import lila.db.BSON._
-import lila.db.paginator.BSONAdapter
-import lila.db.Types.Coll
+import lila.db.dsl._
+import lila.db.paginator.Adapter
import lila.rating.PerfType
import lila.user.User
@@ -20,15 +20,15 @@ final class LeaderboardApi(
import LeaderboardApi._
import BSONHandlers._
- def recentByUser(user: User, page: Int) = paginator(user, page, BSONDocument("d" -> -1))
+ def recentByUser(user: User, page: Int) = paginator(user, page, $doc("d" -> -1))
- def bestByUser(user: User, page: Int) = paginator(user, page, BSONDocument("w" -> 1))
+ def bestByUser(user: User, page: Int) = paginator(user, page, $doc("w" -> 1))
def chart(user: User): Fu[ChartData] = {
import reactivemongo.bson._
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
coll.aggregate(
- Match(BSONDocument("u" -> user.id)),
+ Match($doc("u" -> user.id)),
List(GroupField("v")("nb" -> SumValue(1), "points" -> Push("s"), "ratios" -> Push("w")))
).map {
_.documents map leaderboardAggregationResultBSONHandler.read
@@ -46,11 +46,11 @@ final class LeaderboardApi(
}
}
- private def paginator(user: User, page: Int, sort: BSONDocument): Fu[Paginator[TourEntry]] = Paginator(
- adapter = new BSONAdapter[Entry](
+ private def paginator(user: User, page: Int, sort: Bdoc): Fu[Paginator[TourEntry]] = Paginator(
+ adapter = new Adapter[Entry](
collection = coll,
- selector = BSONDocument("u" -> user.id),
- projection = BSONDocument(),
+ selector = $doc("u" -> user.id),
+ projection = $empty,
sort = sort
) mapFutureList withTournaments,
currentPage = page,
diff --git a/modules/tournament/src/main/LeaderboardIndexer.scala b/modules/tournament/src/main/LeaderboardIndexer.scala
index 69a8a4f22e..f16fd911cd 100644
--- a/modules/tournament/src/main/LeaderboardIndexer.scala
+++ b/modules/tournament/src/main/LeaderboardIndexer.scala
@@ -6,7 +6,7 @@ import reactivemongo.bson._
import scala.concurrent.duration._
import lila.db.BSON._
-import lila.db.Types.Coll
+import lila.db.dsl.Coll
private final class LeaderboardIndexer(
tournamentColl: Coll,
diff --git a/modules/tournament/src/main/PairingRepo.scala b/modules/tournament/src/main/PairingRepo.scala
index 94d2c52bc6..b078278c03 100644
--- a/modules/tournament/src/main/PairingRepo.scala
+++ b/modules/tournament/src/main/PairingRepo.scala
@@ -6,34 +6,33 @@ import reactivemongo.bson._
import reactivemongo.core.commands._
import BSONHandlers._
-import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.dsl._
object PairingRepo {
private lazy val coll = Env.current.pairingColl
- private def selectId(id: String) = BSONDocument("_id" -> id)
- def selectTour(tourId: String) = BSONDocument("tid" -> tourId)
- def selectUser(userId: String) = BSONDocument("u" -> userId)
- private def selectTourUser(tourId: String, userId: String) = BSONDocument(
+ private def selectId(id: String) = $doc("_id" -> id)
+ def selectTour(tourId: String) = $doc("tid" -> tourId)
+ def selectUser(userId: String) = $doc("u" -> userId)
+ private def selectTourUser(tourId: String, userId: String) = $doc(
"tid" -> tourId,
"u" -> userId)
- private val selectPlaying = BSONDocument("s" -> BSONDocument("$lt" -> chess.Status.Mate.id))
- private val selectFinished = BSONDocument("s" -> BSONDocument("$gte" -> chess.Status.Mate.id))
- private val recentSort = BSONDocument("d" -> -1)
- private val chronoSort = BSONDocument("d" -> 1)
+ private val selectPlaying = $doc("s" -> $doc("$lt" -> chess.Status.Mate.id))
+ private val selectFinished = $doc("s" -> $doc("$gte" -> chess.Status.Mate.id))
+ 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(
- selectTour(tourId) ++ BSONDocument("u" -> BSONDocument("$in" -> userIds)),
- BSONDocument("_id" -> false, "u" -> true)
- ).sort(recentSort).cursor[BSONDocument]().enumerate(nb) |>>>
+ selectTour(tourId) ++ $doc("u" -> $doc("$in" -> userIds)),
+ $doc("_id" -> false, "u" -> true)
+ ).sort(recentSort).cursor[Bdoc]().enumerate(nb) |>>>
Iteratee.fold(scala.collection.immutable.Map.empty[String, String]) { (acc, doc) =>
~doc.getAs[List[String]]("u") match {
case List(u1, u2) =>
@@ -45,8 +44,8 @@ object PairingRepo {
def opponentsOf(tourId: String, userId: String): Fu[Set[String]] =
coll.find(
selectTourUser(tourId, userId),
- BSONDocument("_id" -> false, "u" -> true)
- ).cursor[BSONDocument]().collect[List]().map {
+ $doc("_id" -> false, "u" -> true)
+ ).cursor[Bdoc]().gather[List]().map {
_.flatMap { doc =>
~doc.getAs[List[String]]("u").filter(userId!=)
}.toSet
@@ -55,15 +54,15 @@ object PairingRepo {
def recentIdsByTourAndUserId(tourId: String, userId: String, nb: Int): Fu[List[String]] =
coll.find(
selectTourUser(tourId, userId),
- BSONDocument("_id" -> true)
- ).sort(recentSort).cursor[BSONDocument]().collect[List](nb).map {
+ $doc("_id" -> true)
+ ).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
@@ -78,7 +77,7 @@ object PairingRepo {
coll.aggregate(
Match(selectTour(tourId)),
List(
- Project(BSONDocument("u" -> true, "_id" -> false)),
+ Project($doc("u" -> true, "_id" -> false)),
Unwind("u"),
GroupField("u")("nb" -> SumValue(1))
)).map {
@@ -93,25 +92,25 @@ 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) ++ BSONDocument("d" -> DateTime.now)
+ pairingHandler.write(pairing) ++ $doc("d" -> DateTime.now)
}.void
def finish(g: lila.game.Game) =
if (g.aborted) coll.remove(selectId(g.id))
else coll.update(
selectId(g.id),
- BSONDocument("$set" -> BSONDocument(
+ $doc("$set" -> $doc(
"s" -> g.status.id,
"w" -> g.winnerColor.map(_.white),
"t" -> g.turns))).void
@@ -123,14 +122,14 @@ object PairingRepo {
}) ?? { field =>
coll.update(
selectId(pairing.id),
- BSONDocument("$set" -> BSONDocument(field -> value))).void
+ $doc("$set" -> $doc(field -> value))).void
}
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework, AggregationFramework.{ AddToSet, Group, Match, Project, Push, Unwind }
def playingUserIds(tour: Tournament): Fu[Set[String]] =
coll.aggregate(Match(selectTour(tour.id) ++ selectPlaying), List(
- Project(BSONDocument(
+ Project($doc(
"u" -> BSONBoolean(true), "_id" -> BSONBoolean(false))),
Unwind("u"), Group(BSONBoolean(true))("ids" -> AddToSet("u")))).map(
_.documents.headOption.flatMap(_.getAs[Set[String]]("ids")).
diff --git a/modules/tournament/src/main/PlayerRepo.scala b/modules/tournament/src/main/PlayerRepo.scala
index b61b638985..52e4ad3aa1 100644
--- a/modules/tournament/src/main/PlayerRepo.scala
+++ b/modules/tournament/src/main/PlayerRepo.scala
@@ -6,7 +6,7 @@ import reactivemongo.core.commands._
import BSONHandlers._
import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.rating.Perf
import lila.user.{ User, Perfs }
@@ -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] =
@@ -131,9 +131,8 @@ 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]()
+ coll.find(selectTour(tourId) ++ $doc("uid" -> $doc("$in" -> userIds)))
+ .list[Player]()
.chronometer.logIfSlow(200, logger) { players =>
s"PlayerRepo.byTourAndUserIds $tourId ${userIds.size} user IDs, ${players.size} players"
}.result
diff --git a/modules/tournament/src/main/Socket.scala b/modules/tournament/src/main/Socket.scala
index 94d0240e9e..782966e25f 100644
--- a/modules/tournament/src/main/Socket.scala
+++ b/modules/tournament/src/main/Socket.scala
@@ -2,7 +2,6 @@ package lila.tournament
import akka.actor._
import akka.pattern.pipe
-import play.api.libs.iteratee._
import play.api.libs.json._
import scala.concurrent.duration._
@@ -11,7 +10,7 @@ import lila.common.LightUser
import lila.hub.actorApi.WithUserIds
import lila.hub.TimeBomb
import lila.memo.ExpireSetMemo
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.{ SocketActor, History, Historical }
private[tournament] final class Socket(
@@ -78,12 +77,9 @@ private[tournament] final class Socket(
case GetVersion => sender ! history.version
- case Join(uid, user) =>
- val (enumerator, channel) = Concurrent.broadcast[JsValue]
- val member = Member(channel, user)
+ case AddMember(uid, member) =>
addMember(uid, member)
notifyCrowd
- sender ! Connected(enumerator, member)
case Quit(uid) =>
quit(uid)
diff --git a/modules/tournament/src/main/SocketHandler.scala b/modules/tournament/src/main/SocketHandler.scala
index c1eb7f1ba7..6e8c1a2e66 100644
--- a/modules/tournament/src/main/SocketHandler.scala
+++ b/modules/tournament/src/main/SocketHandler.scala
@@ -1,16 +1,12 @@
package lila.tournament
-import scala.concurrent.duration._
-
import akka.actor._
import akka.pattern.ask
import actorApi._
import lila.common.PimpedJson._
import lila.hub.actorApi.map._
-import lila.security.Flood
-import akka.actor.ActorSelection
-import lila.socket.actorApi.{ Connected => _, _ }
+import lila.socket.actorApi._
import lila.socket.Handler
import lila.user.User
import makeTimeout.short
@@ -18,36 +14,25 @@ import makeTimeout.short
private[tournament] final class SocketHandler(
hub: lila.hub.Env,
socketHub: ActorRef,
- chat: ActorSelection,
- flood: Flood) {
+ chat: ActorSelection)(implicit system: ActorSystem) {
- def join(
- tourId: String,
- uid: String,
- user: Option[User]): Fu[Option[JsSocketHandler]] =
+ def join(tourId: String, uid: String, user: Option[User]): Fu[Option[JsFlow]] =
TournamentRepo.exists(tourId) flatMap {
_ ?? {
- for {
- socket ← socketHub ? Get(tourId) mapTo manifest[ActorRef]
- join = Join(uid = uid, user = user)
- handler ← Handler(hub, socket, uid, join, user map (_.id)) {
- case Connected(enum, member) =>
- (controller(socket, tourId, uid, member), enum, member)
- }
- } yield handler.some
- }
- }
-
- private def controller(
- socket: ActorRef,
- tourId: String,
- uid: String,
- member: Member): Handler.Controller = {
- case ("p", o) => o int "v" foreach { v => socket ! PingVersion(uid, v) }
- case ("talk", o) => o str "d" foreach { text =>
- member.userId foreach { userId =>
- chat ! lila.chat.actorApi.UserTalk(tourId, userId, text, socket)
+ socketHub ? Get(tourId) mapTo manifest[ActorRef] map { socket =>
+ Handler.actorRef { out =>
+ val member = Member(out, user)
+ socket ! AddMember(uid, member)
+ Handler.props(hub, socket, member, uid, user.map(_.id)) {
+ case ("p", o) => o int "v" foreach { v => socket ! PingVersion(uid, v) }
+ case ("talk", o) => o str "d" foreach { text =>
+ member.userId foreach { userId =>
+ chat ! lila.chat.actorApi.UserTalk(tourId, userId, text, socket)
+ }
+ }
+ }
+ }.some
+ }
}
}
- }
}
diff --git a/modules/tournament/src/main/TournamentApi.scala b/modules/tournament/src/main/TournamentApi.scala
index c6b14edfe5..1aa8f409d7 100644
--- a/modules/tournament/src/main/TournamentApi.scala
+++ b/modules/tournament/src/main/TournamentApi.scala
@@ -10,7 +10,7 @@ import scalaz.NonEmptyList
import actorApi._
import lila.common.Debouncer
-import lila.db.api._
+import lila.db.dsl._
import lila.game.{ Game, GameRepo, Pov }
import lila.hub.actorApi.lobby.ReloadTournaments
import lila.hub.actorApi.map.{ Tell, TellIds }
diff --git a/modules/tournament/src/main/TournamentRepo.scala b/modules/tournament/src/main/TournamentRepo.scala
index fae5e7322b..b7a219207f 100644
--- a/modules/tournament/src/main/TournamentRepo.scala
+++ b/modules/tournament/src/main/TournamentRepo.scala
@@ -2,65 +2,64 @@ package lila.tournament
import chess.variant.Variant
import org.joda.time.DateTime
-import reactivemongo.bson.{ BSONDocument, BSONArray, BSONInteger }
import BSONHandlers._
import lila.common.paginator.Paginator
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits._
-import lila.db.paginator.BSONAdapter
+import lila.db.dsl._
+import lila.db.paginator.Adapter
object TournamentRepo {
private lazy val coll = Env.current.tournamentColl
- private def selectId(id: String) = BSONDocument("_id" -> id)
+ private def $id(id: String) = $doc("_id" -> id)
- private val enterableSelect = BSONDocument(
- "status" -> BSONDocument("$in" -> List(Status.Created.id, Status.Started.id)))
+ private val enterableSelect = $doc(
+ "status" $in (Status.Created.id, Status.Started.id))
- private val createdSelect = BSONDocument("status" -> Status.Created.id)
- private val startedSelect = BSONDocument("status" -> Status.Started.id)
- private[tournament] val finishedSelect = BSONDocument("status" -> Status.Finished.id)
- private val startedOrFinishedSelect = BSONDocument("status" -> BSONDocument("$gte" -> Status.Started.id))
- private val unfinishedSelect = BSONDocument("status" -> BSONDocument("$ne" -> Status.Finished.id))
- private[tournament] val scheduledSelect = BSONDocument("schedule" -> BSONDocument("$exists" -> true))
- private def sinceSelect(date: DateTime) = BSONDocument("startsAt" -> BSONDocument("$gt" -> date))
+ private val createdSelect = $doc("status" -> Status.Created.id)
+ private val startedSelect = $doc("status" -> Status.Started.id)
+ private[tournament] val finishedSelect = $doc("status" -> Status.Finished.id)
+ private val startedOrFinishedSelect = $doc("status" -> $doc("$gte" -> Status.Started.id))
+ private val unfinishedSelect = $doc("status" -> $doc("$ne" -> Status.Finished.id))
+ private[tournament] val scheduledSelect = $doc("schedule" -> $doc("$exists" -> true))
+ private def sinceSelect(date: DateTime) = $doc("startsAt" -> $doc("$gt" -> date))
private def variantSelect(variant: Variant) =
- if (variant.standard) BSONDocument("variant" -> BSONDocument("$exists" -> false))
- else BSONDocument("variant" -> variant.id)
- private val nonEmptySelect = BSONDocument("nbPlayers" -> BSONDocument("$ne" -> 0))
- private val selectUnique = BSONDocument("schedule.freq" -> "unique")
+ if (variant.standard) $doc("variant" -> $doc("$exists" -> false))
+ else $doc("variant" -> variant.id)
+ private val nonEmptySelect = $doc("nbPlayers" -> $doc("$ne" -> 0))
+ private val selectUnique = $doc("schedule.freq" -> "unique")
- def byId(id: String): Fu[Option[Tournament]] = coll.find(selectId(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(BSONDocument("_id" -> BSONDocument("$in" -> ids)))
- .cursor[Tournament]().collect[List]()
+ coll.find($inIds(ids))
+ .cursor[Tournament]().gather[List]()
def uniqueById(id: String): Fu[Option[Tournament]] =
- coll.find(selectId(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(
- selectId(id) ++ BSONDocument("players.id" -> userId)
- ).one[Tournament]
+ $id(id) ++ $doc("players.id" -> userId)
+ ).uno[Tournament]
def createdById(id: String): Fu[Option[Tournament]] =
- coll.find(selectId(id) ++ createdSelect).one[Tournament]
+ coll.find($id(id) ++ createdSelect).uno[Tournament]
def enterableById(id: String): Fu[Option[Tournament]] =
- coll.find(selectId(id) ++ enterableSelect).one[Tournament]
+ coll.find($id(id) ++ enterableSelect).uno[Tournament]
def startedById(id: String): Fu[Option[Tournament]] =
- coll.find(selectId(id) ++ startedSelect).one[Tournament]
+ coll.find($id(id) ++ startedSelect).uno[Tournament]
def finishedById(id: String): Fu[Option[Tournament]] =
- coll.find(selectId(id) ++ finishedSelect).one[Tournament]
+ coll.find($id(id) ++ finishedSelect).uno[Tournament]
def startedOrFinishedById(id: String): Fu[Option[Tournament]] =
byId(id) map { _ filterNot (_.isCreated) }
@@ -69,88 +68,85 @@ 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(BSONDocument("createdAt" -> -1)).toList[Tournament](None)
+ coll.find(startedSelect).sort($doc("createdAt" -> -1)).list[Tournament](None)
def publicStarted: Fu[List[Tournament]] =
- coll.find(startedSelect ++ BSONDocument("private" -> BSONDocument("$exists" -> false)))
- .sort(BSONDocument("createdAt" -> -1))
- .cursor[Tournament]().collect[List]()
+ coll.find(startedSelect ++ $doc("private" -> $doc("$exists" -> false)))
+ .sort($doc("createdAt" -> -1))
+ .list[Tournament]()
def finished(limit: Int): Fu[List[Tournament]] =
coll.find(finishedSelect)
- .sort(BSONDocument("startsAt" -> -1))
- .cursor[Tournament]().collect[List](limit)
+ .sort($doc("startsAt" -> -1))
+ .list[Tournament](limit)
def finishedNotable(limit: Int): Fu[List[Tournament]] =
- coll.find(finishedSelect ++ BSONDocument(
- "$or" -> BSONArray(
- BSONDocument("nbPlayers" -> BSONDocument("$gte" -> 15)),
+ coll.find(finishedSelect ++ $doc(
+ "$or" -> $arr(
+ $doc("nbPlayers" -> $doc("$gte" -> 15)),
scheduledSelect
)))
- .sort(BSONDocument("startsAt" -> -1))
- .cursor[Tournament]().collect[List](limit)
+ .sort($doc("startsAt" -> -1))
+ .list[Tournament](limit)
def finishedPaginator(maxPerPage: Int, page: Int) = Paginator(
- adapter = new BSONAdapter[Tournament](
+ adapter = new Adapter[Tournament](
collection = coll,
selector = finishedSelect,
- projection = BSONDocument(),
- sort = BSONDocument("startsAt" -> -1)
+ projection = $empty,
+ sort = $doc("startsAt" -> -1)
),
currentPage = page,
maxPerPage = maxPerPage)
def setStatus(tourId: String, status: Status) = coll.update(
- selectId(tourId),
- BSONDocument("$set" -> BSONDocument("status" -> status.id))
+ $id(tourId),
+ $doc("$set" -> $doc("status" -> status.id))
).void
def setNbPlayers(tourId: String, nb: Int) = coll.update(
- selectId(tourId),
- BSONDocument("$set" -> BSONDocument("nbPlayers" -> nb))
+ $id(tourId),
+ $doc("$set" -> $doc("nbPlayers" -> nb))
).void
def setWinnerId(tourId: String, userId: String) = coll.update(
- selectId(tourId),
- BSONDocument("$set" -> BSONDocument("winner" -> userId))
+ $id(tourId),
+ $doc("$set" -> $doc("winner" -> userId))
).void
def setFeaturedGameId(tourId: String, gameId: String) = coll.update(
- selectId(tourId),
- BSONDocument("$set" -> BSONDocument("featured" -> gameId))
+ $id(tourId),
+ $doc("$set" -> $doc("featured" -> gameId))
).void
- def featuredGameId(tourId: String) = coll.find(
- selectId(tourId),
- BSONDocument("featured" -> true)
- ).one[BSONDocument].map(_.flatMap(_.getAs[String]("featured")))
+ def featuredGameId(tourId: String) = coll.primitiveOne[String]($id(tourId), "featured")
- private def allCreatedSelect(aheadMinutes: Int) = createdSelect ++ BSONDocument(
- "$or" -> BSONArray(
- BSONDocument("schedule" -> BSONDocument("$exists" -> false)),
- BSONDocument("startsAt" -> BSONDocument("$lt" -> (DateTime.now plusMinutes aheadMinutes)))
+ private def allCreatedSelect(aheadMinutes: Int) = createdSelect ++ $doc(
+ "$or" -> $arr(
+ $doc("schedule" -> $doc("$exists" -> false)),
+ $doc("startsAt" -> $doc("$lt" -> (DateTime.now plusMinutes aheadMinutes)))
)
)
def publicCreatedSorted(aheadMinutes: Int): Fu[List[Tournament]] = coll.find(
- allCreatedSelect(aheadMinutes) ++ BSONDocument("private" -> BSONDocument("$exists" -> false))
- ).sort(BSONDocument("startsAt" -> 1)).cursor[Tournament]().collect[List]()
+ allCreatedSelect(aheadMinutes) ++ $doc("private" -> $doc("$exists" -> false))
+ ).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 ++ BSONDocument(
- "private" -> BSONDocument("$exists" -> false)
- )).sort(BSONDocument("startsAt" -> 1)).toList[Tournament](none) map {
+ coll.find(startedSelect ++ $doc(
+ "private" -> $doc("$exists" -> false)
+ )).sort($doc("startsAt" -> 1)).list[Tournament](none) map {
_.filter(_.isStillWorthEntering)
}
@@ -176,17 +172,17 @@ object TournamentRepo {
def uniques(max: Int): Fu[List[Tournament]] =
coll.find(selectUnique)
- .sort(BSONDocument("startsAt" -> -1))
- .hint(BSONDocument("startsAt" -> -1))
- .cursor[Tournament]().collect[List]()
+ .sort($doc("startsAt" -> -1))
+ .hint($doc("startsAt" -> -1))
+ .list[Tournament]()
def scheduledUnfinished: Fu[List[Tournament]] =
coll.find(scheduledSelect ++ unfinishedSelect)
- .sort(BSONDocument("startsAt" -> 1)).cursor[Tournament]().collect[List]()
+ .sort($doc("startsAt" -> 1)).list[Tournament]()
def scheduledCreated: Fu[List[Tournament]] =
coll.find(createdSelect ++ scheduledSelect)
- .sort(BSONDocument("startsAt" -> 1)).cursor[Tournament]().collect[List]()
+ .sort($doc("startsAt" -> 1)).list[Tournament]()
def scheduledDedup: Fu[List[Tournament]] = scheduledCreated map {
import Schedule.Freq
@@ -203,35 +199,35 @@ object TournamentRepo {
}
def lastFinishedScheduledByFreq(freq: Schedule.Freq, since: DateTime): Fu[List[Tournament]] = coll.find(
- finishedSelect ++ sinceSelect(since) ++ variantSelect(chess.variant.Standard) ++ BSONDocument(
+ finishedSelect ++ sinceSelect(since) ++ variantSelect(chess.variant.Standard) ++ $doc(
"schedule.freq" -> freq.name,
- "schedule.speed" -> BSONDocument("$in" -> Schedule.Speed.mostPopular.map(_.name))
+ "schedule.speed".$in(Schedule.Speed.mostPopular.map(_.name): _*)
)
- ).sort(BSONDocument("startsAt" -> -1))
- .toList[Tournament](Schedule.Speed.mostPopular.size.some)
+ ).sort($doc("startsAt" -> -1))
+ .list[Tournament](Schedule.Speed.mostPopular.size.some)
def lastFinishedDaily(variant: Variant): Fu[Option[Tournament]] = coll.find(
finishedSelect ++ sinceSelect(DateTime.now minusDays 1) ++ variantSelect(variant) ++
- BSONDocument("schedule.freq" -> Schedule.Freq.Daily.name)
- ).sort(BSONDocument("startsAt" -> -1)).one[Tournament]
+ $doc("schedule.freq" -> Schedule.Freq.Daily.name)
+ ).sort($doc("startsAt" -> -1)).uno[Tournament]
- def update(tour: Tournament) = coll.update(BSONDocument("_id" -> tour.id), tour)
+ def update(tour: Tournament) = coll.update($doc("_id" -> tour.id), tour)
def insert(tour: Tournament) = coll.insert(tour)
- def remove(tour: Tournament) = coll.remove(BSONDocument("_id" -> tour.id))
+ def remove(tour: Tournament) = coll.remove($doc("_id" -> tour.id))
- def exists(id: String) = coll.count(BSONDocument("_id" -> id).some) map (0 !=)
+ def exists(id: String) = coll.count($doc("_id" -> id).some) map (0 !=)
def isFinished(id: String): Fu[Boolean] =
- coll.count(BSONDocument("_id" -> id, "status" -> Status.Finished.id).some) map (0 !=)
+ coll.count($doc("_id" -> id, "status" -> Status.Finished.id).some) map (0 !=)
def toursToWithdrawWhenEntering(tourId: String): Fu[List[Tournament]] =
- coll.find(enterableSelect ++ BSONDocument(
- "_id" -> BSONDocument("$ne" -> tourId),
- "schedule.freq" -> BSONDocument("$nin" -> List(
+ coll.find(enterableSelect ++ $doc(
+ "_id" -> $doc("$ne" -> tourId),
+ "schedule.freq" $nin (
Schedule.Freq.Marathon.name,
Schedule.Freq.Unique.name
- ))
- ) ++ nonEmptySelect).cursor[Tournament]().collect[List]()
+ )
+ ) ++ nonEmptySelect).cursor[Tournament]().gather[List]()
}
diff --git a/modules/tournament/src/main/actorApi.scala b/modules/tournament/src/main/actorApi.scala
index acc8fe5a03..0b0fd1bc9e 100644
--- a/modules/tournament/src/main/actorApi.scala
+++ b/modules/tournament/src/main/actorApi.scala
@@ -1,31 +1,30 @@
package lila.tournament
package actorApi
+import akka.actor.ActorRef
+
import lila.game.Game
import lila.socket.SocketMember
import lila.user.User
private[tournament] case class Member(
- channel: JsChannel,
+ out: ActorRef,
userId: Option[String],
troll: Boolean) extends SocketMember
private[tournament] object Member {
- def apply(channel: JsChannel, user: Option[User]): Member = Member(
- channel = channel,
+ def apply(out: ActorRef, user: Option[User]): Member = Member(
+ out = out,
userId = user map (_.id),
troll = user.??(_.troll))
}
private[tournament] case class Messadata(trollish: Boolean = false)
-private[tournament] case class Join(
- uid: String,
- user: Option[User])
+private[tournament] case class AddMember(uid: String, member: Member)
private[tournament] case class Talk(tourId: String, u: String, t: String, troll: Boolean)
private[tournament] case object Reload
private[tournament] case class StartGame(game: Game)
-private[tournament] case class Connected(enumerator: JsEnumerator, member: Member)
case class RemindTournament(tour: Tournament, activeUserIds: List[String])
case class TournamentTable(tours: List[Tournament])
diff --git a/modules/tv/src/main/Env.scala b/modules/tv/src/main/Env.scala
index 99284f34b5..b664aaae82 100644
--- a/modules/tv/src/main/Env.scala
+++ b/modules/tv/src/main/Env.scala
@@ -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 {
diff --git a/modules/user/src/main/Cached.scala b/modules/user/src/main/Cached.scala
index 1141db9662..6b1c6c963c 100644
--- a/modules/user/src/main/Cached.scala
+++ b/modules/user/src/main/Cached.scala
@@ -7,15 +7,14 @@ import play.api.libs.json._
import reactivemongo.bson._
import lila.common.LightUser
-import lila.db.api.{ $count, $primitive, $gt }
-import lila.db.BSON._
-import lila.db.Implicits._
+import lila.db.BSON
+import lila.db.dsl._
import lila.memo.{ ExpireSetMemo, MongoCache }
import lila.rating.{ Perf, PerfType }
-import tube.userTube
import User.{ LightPerf, LightCount }
final class Cached(
+ userColl: Coll,
nbTtl: FiniteDuration,
onlineUserIdMemo: ExpireSetMemo,
mongoCache: MongoCache.Builder,
@@ -26,7 +25,7 @@ final class Cached(
private val countCache = mongoCache.single[Int](
prefix = "user:nb",
- f = $count(UserRepo.enabledSelect),
+ f = userColl.count(UserRepo.enabledSelect.some),
timeToLive = nbTtl)
def countEnabled: Fu[Int] = countCache(true)
diff --git a/modules/user/src/main/Env.scala b/modules/user/src/main/Env.scala
index 45c9e12ff3..75056fc0a5 100644
--- a/modules/user/src/main/Env.scala
+++ b/modules/user/src/main/Env.scala
@@ -48,7 +48,6 @@ final class Env(
def countEnabled = cached.countEnabled
def cli = new lila.common.Cli {
- import tube.userTube
def process = {
case "user" :: "email" :: userId :: email :: Nil =>
UserRepo.email(User normalize userId, email) inject "done"
@@ -75,6 +74,7 @@ final class Env(
}
lazy val cached = new Cached(
+ userColl = userColl,
nbTtl = CachedNbTtl,
onlineUserIdMemo = onlineUserIdMemo,
mongoCache = mongoCache,
diff --git a/modules/user/src/main/LightUserApi.scala b/modules/user/src/main/LightUserApi.scala
index d591b07242..9159a63550 100644
--- a/modules/user/src/main/LightUserApi.scala
+++ b/modules/user/src/main/LightUserApi.scala
@@ -2,7 +2,7 @@ package lila.user
import lila.common.LightUser
-import lila.db.Types._
+import lila.db.dsl._
import reactivemongo.bson._
import scala.concurrent.duration._
import User.{ BSONFields => F }
@@ -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")
diff --git a/modules/user/src/main/NoteApi.scala b/modules/user/src/main/NoteApi.scala
index 2619c598fd..085bdde470 100644
--- a/modules/user/src/main/NoteApi.scala
+++ b/modules/user/src/main/NoteApi.scala
@@ -1,5 +1,6 @@
package lila.user
+import lila.db.dsl._
import org.joda.time.DateTime
case class Note(
@@ -11,7 +12,7 @@ case class Note(
date: DateTime)
final class NoteApi(
- coll: lila.db.Types.Coll,
+ coll: Coll,
timeline: akka.actor.ActorSelection) {
import reactivemongo.bson._
@@ -20,11 +21,11 @@ final class NoteApi(
def get(user: User, me: User, myFriendIds: Set[String]): Fu[List[Note]] =
coll.find(
- BSONDocument(
+ $doc(
"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)
+ "from" -> $doc("$in" -> (myFriendIds + me.id))
+ ) ++ me.troll.fold($doc(), $doc("troll" -> false))
+ ).sort($doc("date" -> -1)).cursor[Note]().gather[List](100)
def write(to: User, text: String, from: User) = {
diff --git a/modules/user/src/main/RankingApi.scala b/modules/user/src/main/RankingApi.scala
index b6b26b3ae9..da1650b1bd 100644
--- a/modules/user/src/main/RankingApi.scala
+++ b/modules/user/src/main/RankingApi.scala
@@ -6,13 +6,13 @@ import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework
import reactivemongo.bson._
import scala.concurrent.duration._
-import lila.db.BSON._
+import lila.db.dsl._
import lila.db.BSON.MapValue.MapHandler
import lila.memo.{ AsyncCache, MongoCache }
import lila.rating.{ Perf, PerfType }
final class RankingApi(
- coll: lila.db.Types.Coll,
+ coll: Coll,
mongoCache: MongoCache.Builder,
lightUser: String => Option[lila.common.LightUser]) {
@@ -25,9 +25,9 @@ final class RankingApi(
}
def save(userId: User.ID, perfType: PerfType, perf: Perf): Funit =
- (perf.nb >= 2) ?? coll.update(BSONDocument(
+ (perf.nb >= 2) ?? coll.update($doc(
"_id" -> s"$userId:${perfType.id}"
- ), BSONDocument(
+ ), $doc(
"user" -> userId,
"perf" -> perfType.id,
"rating" -> perf.intRating,
@@ -38,8 +38,8 @@ final class RankingApi(
def remove(userId: User.ID): Funit = UserRepo byId userId flatMap {
_ ?? { user =>
- coll.remove(BSONDocument(
- "_id" -> BSONDocument("$in" -> PerfType.leaderboardable.filter { pt =>
+ coll.remove($doc(
+ "_id" -> $doc("$in" -> PerfType.leaderboardable.filter { pt =>
user.perfs(pt).nonEmpty
}.map { pt =>
s"${user.id}:${pt.id}"
@@ -50,9 +50,9 @@ final class RankingApi(
def topPerf(perfId: Perf.ID, nb: Int): Fu[List[User.LightPerf]] =
PerfType.id2key(perfId) ?? { perfKey =>
- coll.find(BSONDocument("perf" -> perfId, "stable" -> true))
- .sort(BSONDocument("rating" -> -1))
- .cursor[Ranking]().collect[List](nb) map {
+ coll.find($doc("perf" -> perfId, "stable" -> true))
+ .sort($doc("rating" -> -1))
+ .cursor[Ranking]().gather[List](nb) map {
_.flatMap { r =>
lightUser(r.user).map { light =>
User.LightPerf(
@@ -80,9 +80,9 @@ final class RankingApi(
private def compute(perfId: Perf.ID): Fu[Map[User.ID, Rank]] = {
val enumerator = coll.find(
- BSONDocument("perf" -> perfId, "stable" -> true),
- BSONDocument("user" -> true, "_id" -> false)
- ).sort(BSONDocument("rating" -> -1)).cursor[BSONDocument]().enumerate()
+ $doc("perf" -> perfId, "stable" -> true),
+ $doc("user" -> true, "_id" -> false)
+ ).sort($doc("rating" -> -1)).cursor[BSONDocument]().enumerate()
var rank = 1
val b = Map.newBuilder[User.ID, Rank]
val mapBuilder: Iteratee[BSONDocument, Unit] = Iteratee.foreach { doc =>
@@ -110,13 +110,13 @@ final class RankingApi(
private def compute(perfId: Perf.ID): Fu[List[NbUsers]] =
lila.rating.PerfType(perfId).exists(lila.rating.PerfType.leaderboardable.contains) ?? {
coll.aggregate(
- Match(BSONDocument("perf" -> perfId)),
- List(Project(BSONDocument(
+ Match($doc("perf" -> perfId)),
+ List(Project($doc(
"_id" -> false,
- "r" -> BSONDocument(
+ "r" -> $doc(
"$subtract" -> BSONArray(
"$rating",
- BSONDocument("$mod" -> BSONArray("$rating", Stat.group))
+ $doc("$mod" -> BSONArray("$rating", Stat.group))
)
)
)),
diff --git a/modules/user/src/main/TrophyApi.scala b/modules/user/src/main/TrophyApi.scala
index 7c4b34e3fa..0b041b0551 100644
--- a/modules/user/src/main/TrophyApi.scala
+++ b/modules/user/src/main/TrophyApi.scala
@@ -2,10 +2,11 @@ package lila.user
import org.joda.time.DateTime
+import lila.db.dsl._
import lila.db.BSON.BSONJodaDateTimeHandler
import reactivemongo.bson._
-final class TrophyApi(coll: lila.db.Types.Coll) {
+final class TrophyApi(coll: Coll) {
private implicit val trophyKindBSONHandler = new BSONHandler[BSONString, Trophy.Kind] {
def read(bsonString: BSONString): Trophy.Kind =
@@ -23,5 +24,5 @@ final class TrophyApi(coll: lila.db.Types.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)
}
diff --git a/modules/user/src/main/User.scala b/modules/user/src/main/User.scala
index 5fc8a58532..30f406992c 100644
--- a/modules/user/src/main/User.scala
+++ b/modules/user/src/main/User.scala
@@ -192,6 +192,4 @@ object User {
lang -> o.lang,
title -> o.title)
}
-
- private[user] lazy val tube = lila.db.BsTube(userBSONHandler)
}
diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala
index 70dde6d790..ed95ed5216 100644
--- a/modules/user/src/main/UserRepo.scala
+++ b/modules/user/src/main/UserRepo.scala
@@ -2,95 +2,93 @@ package lila.user
import com.roundeights.hasher.Implicits._
import org.joda.time.DateTime
-import play.api.libs.json._
-import play.modules.reactivemongo.json.ImplicitBSONHandlers.JsObjectWriter
import reactivemongo.api._
import reactivemongo.bson._
-import lila.common.PimpedJson._
-import lila.db.api._
import lila.db.BSON.BSONJodaDateTimeHandler
-import lila.db.Implicits._
+import lila.db.dsl._
import lila.rating.{ Glicko, Perf, PerfType }
object UserRepo {
- import tube.userTube
import User.userBSONHandler
import User.ID
import User.{ BSONFields => F }
- private val coll = userTube.coll
+ // dirty
+ private val coll = Env.current.userColl
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework.{ Match, Project, Group, GroupField, SumField, SumValue }
val normalize = User normalize _
- def all: Fu[List[User]] = $find.all
-
def topNbGame(nb: Int): Fu[List[User]] =
- $find($query(enabledSelect) sort ($sort desc "count.game"), nb)
+ coll.find(enabledSelect).sort($sort desc "count.game").cursor[User]().gather[List](nb)
- def byId(id: ID): Fu[Option[User]] = $find byId id
+ def byId(id: ID): Fu[Option[User]] = coll.byId[User](id)
- def byIds(ids: Iterable[ID]): Fu[List[User]] = $find byIds ids
+ def byIds(ids: Iterable[ID]): Fu[List[User]] = coll.byIds[User](ids)
- def byEmail(email: String): Fu[Option[User]] = $find one Json.obj(F.email -> email)
+ def byEmail(email: String): Fu[Option[User]] = coll.uno[User]($doc(F.email -> email))
def idByEmail(email: String): Fu[Option[String]] =
- $primitive.one(Json.obj(F.email -> email), "_id")(_.asOpt[String])
+ coll.primitiveOne[String]($doc(F.email -> email), "_id")
def enabledByEmail(email: String): Fu[Option[User]] = byEmail(email) map (_ filter (_.enabled))
def pair(x: Option[ID], y: Option[ID]): Fu[(Option[User], Option[User])] =
- $find byIds List(x, y).flatten map { users =>
+ coll.byIds[User](List(x, y).flatten) map { users =>
x.??(xx => users.find(_.id == xx)) ->
y.??(yy => users.find(_.id == yy))
}
- def byOrderedIds(ids: Seq[ID]): Fu[List[User]] = $find byOrderedIds ids
+ def byOrderedIds(ids: Seq[ID]): Fu[List[User]] =
+ coll.byOrderedIds[User](ids)(_.id)
- def enabledByIds(ids: Iterable[ID]): Fu[List[User]] = $find(enabledSelect ++ $select.byIds(ids))
+ def enabledByIds(ids: Iterable[ID]): Fu[List[User]] =
+ coll.list[User](enabledSelect ++ $inIds(ids))
def enabledById(id: ID): Fu[Option[User]] =
- $find.one(enabledSelect ++ $select.byId(id))
+ coll.uno[User](enabledSelect ++ $id(id))
- def named(username: String): Fu[Option[User]] = $find byId normalize(username)
+ def named(username: String): Fu[Option[User]] = coll.byId[User](normalize(username))
- def nameds(usernames: List[String]): Fu[List[User]] = $find byIds usernames.map(normalize)
+ def nameds(usernames: List[String]): Fu[List[User]] = coll.byIds[User](usernames.map(normalize))
// expensive, send to secondary
def byIdsSortRating(ids: Iterable[ID], nb: Int) =
- coll.find(BSONDocument("_id" -> BSONDocument("$in" -> ids)) ++ goodLadSelectBson)
- .sort(BSONDocument(s"perfs.standard.gl.r" -> -1))
+ coll.find($inIds(ids) ++ goodLadSelectBson)
+ .sort($sort desc "perfs.standard.gl.r")
.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]] =
coll.find(
- BSONDocument("_id" -> BSONDocument("$in" -> ids)) ++ goodLadSelectBson,
- BSONDocument("_id" -> true))
- .sort(BSONDocument(s"perfs.standard.gl.r" -> -1))
- .cursor[BSONDocument](ReadPreference.secondaryPreferred)
- .collect[List](nb).map {
+ $inIds(ids) ++ goodLadSelectBson,
+ $doc("_id" -> true))
+ .sort($doc(s"perfs.standard.gl.r" -> -1))
+ .cursor[Bdoc](ReadPreference.secondaryPreferred)
+ .gather[List](nb).map {
_.flatMap { _.getAs[String]("_id") }
}
- def allSortToints(nb: Int) = $find($query.all sort ($sort desc F.toints), nb)
+ def allSortToints(nb: Int) =
+ coll.find($empty).sort($sort desc F.toints).cursor[User]().gather[List](nb)
- def usernameById(id: ID) = $primitive.one($select(id), F.username)(_.asOpt[String])
+ def usernameById(id: ID) =
+ coll.primitiveOne[String]($id(id), F.username)
def usernamesByIds(ids: List[ID]) =
- coll.distinct(F.username, BSONDocument("_id" -> BSONDocument("$in" -> ids)).some) map lila.db.BSON.asStrings
+ coll.distinct(F.username, $inIds(ids).some) map lila.db.BSON.asStrings
def orderByGameCount(u1: String, u2: String): Fu[Option[(String, String)]] = {
coll.find(
- BSONDocument("_id" -> BSONDocument("$in" -> BSONArray(u1, u2))),
- BSONDocument(s"${F.count}.game" -> true)
- ).cursor[BSONDocument]().collect[List]() map { docs =>
+ $doc("_id".$in(u1, u2)),
+ $doc(s"${F.count}.game" -> true)
+ ).cursor[Bdoc]().gather[List]() map { docs =>
docs.sortBy {
- _.getAs[BSONDocument](F.count).flatMap(_.getAs[BSONNumberLike]("game")).??(_.toInt)
+ _.getAs[Bdoc](F.count).flatMap(_.getAs[BSONNumberLike]("game")).??(_.toInt)
}.map(_.getAs[String]("_id")).flatten match {
case List(u1, u2) => (u1, u2).some
case _ => none
@@ -101,15 +99,15 @@ object UserRepo {
def firstGetsWhite(u1O: Option[String], u2O: Option[String]): Fu[Boolean] =
(u1O |@| u2O).tupled.fold(fuccess(scala.util.Random.nextBoolean)) {
case (u1, u2) => coll.find(
- BSONDocument("_id" -> BSONDocument("$in" -> BSONArray(u1, u2))),
- BSONDocument("_id" -> true)
- ).sort(BSONDocument(F.colorIt -> 1)).one[BSONDocument].map {
+ $doc("_id".$in(u1, u2)),
+ $doc("_id" -> true)
+ ).sort($doc(F.colorIt -> 1)).uno[Bdoc].map {
_.fold(scala.util.Random.nextBoolean) { doc =>
doc.getAs[String]("_id") contains u1
}
}.addEffect { v =>
- $update.unchecked($select(u1), $incBson(F.colorIt -> v.fold(1, -1)))
- $update.unchecked($select(u2), $incBson(F.colorIt -> v.fold(-1, 1)))
+ coll.uncheckedUpdate($id(u1), $inc(F.colorIt -> v.fold(1, -1)))
+ coll.uncheckedUpdate($id(u2), $inc(F.colorIt -> v.fold(-1, 1)))
}
}
@@ -122,47 +120,49 @@ object UserRepo {
s"perfs.${pt.key}" -> Perf.perfBSONHandler.write(perfs(pt))
}
}
- diff.nonEmpty ?? $update(
- $select(user.id),
- BSONDocument("$set" -> BSONDocument(diff))
- )
+ diff.nonEmpty ?? coll.update(
+ $id(user.id),
+ $doc("$set" -> $doc(diff))
+ ).void
}
- def setPerf(userId: String, perfName: String, perf: Perf) = $update($select(userId), $setBson(
- s"${F.perfs}.$perfName" -> Perf.perfBSONHandler.write(perf)
- ))
+ def setPerf(userId: String, perfName: String, perf: Perf) =
+ coll.update($id(userId), $set(
+ s"${F.perfs}.$perfName" -> Perf.perfBSONHandler.write(perf)
+ )).void
def setProfile(id: ID, profile: Profile): Funit =
- $update($select(id), $setBson(F.profile -> Profile.profileBSONHandler.write(profile)))
+ coll.update(
+ $id(id),
+ $set(F.profile -> Profile.profileBSONHandler.write(profile))
+ ).void
def setTitle(id: ID, title: Option[String]): Funit = title match {
- case Some(t) => $update.field(id, F.title, t)
- case None => $update($select(id), $unset(F.title))
+ case Some(t) => coll.updateField($id(id), F.title, t).void
+ case None => coll.update($id(id), $unset(F.title)).void
}
def setPlayTime(u: User, playTime: User.PlayTime): Funit =
- $update($select(u.id), $setBson(F.playTime -> User.playTimeHandler.write(playTime)))
+ coll.update($id(u.id), $set(F.playTime -> User.playTimeHandler.write(playTime))).void
- val enabledSelect = Json.obj(F.enabled -> true)
- def engineSelect(v: Boolean) = Json.obj(F.engine -> v.fold(JsBoolean(true), $ne(true)))
- def trollSelect(v: Boolean) = Json.obj(F.troll -> v.fold(JsBoolean(true), $ne(true)))
- def boosterSelect(v: Boolean) = Json.obj(F.booster -> v.fold(JsBoolean(true), $ne(true)))
- def stablePerfSelect(perf: String) = Json.obj(
+ val enabledSelect = $doc(F.enabled -> true)
+ def engineSelect(v: Boolean) = $doc(F.engine -> v.fold[BSONValue]($boolean(true), $ne(true)))
+ def trollSelect(v: Boolean) = $doc(F.troll -> v.fold[BSONValue]($boolean(true), $ne(true)))
+ def boosterSelect(v: Boolean) = $doc(F.booster -> v.fold[BSONValue]($boolean(true), $ne(true)))
+ def stablePerfSelect(perf: String) = $doc(
s"perfs.$perf.nb" -> $gte(30),
s"perfs.$perf.gl.d" -> $lt(lila.rating.Glicko.provisionalDeviation))
val goodLadSelect = enabledSelect ++ engineSelect(false) ++ boosterSelect(false)
- val goodLadSelectBson = BSONDocument(
+ val goodLadSelectBson = $doc(
F.enabled -> true,
- F.engine -> BSONDocument("$ne" -> true),
- F.booster -> BSONDocument("$ne" -> true))
-
- val goodLadQuery = $query(goodLadSelect)
+ F.engine -> $doc("$ne" -> true),
+ F.booster -> $doc("$ne" -> true))
def sortPerfDesc(perf: String) = $sort desc s"perfs.$perf.gl.r"
val sortCreatedAtDesc = $sort desc F.createdAt
def incNbGames(id: ID, rated: Boolean, ai: Boolean, result: Int, totalTime: Option[Int], tvTime: Option[Int]) = {
- val incs = List(
+ val incs: List[(String, BSONInteger)] = List(
"count.game".some,
rated option "count.rated",
ai option "count.ai",
@@ -178,166 +178,153 @@ object UserRepo {
case 0 => "count.drawH".some
case _ => none
}) ifFalse ai
- ).flatten.map(_ -> 1) ::: List(
- totalTime map (s"${F.playTime}.total" -> _),
- tvTime map (s"${F.playTime}.tv" -> _)
+ ).flatten.map(_ -> BSONInteger(1)) ::: List(
+ totalTime map BSONInteger.apply map (s"${F.playTime}.total" -> _),
+ tvTime map BSONInteger.apply map (s"${F.playTime}.tv" -> _)
).flatten
- $update($select(id), $incBson(incs: _*))
+ coll.update($id(id), $inc(incs))
}
- def incToints(id: ID, nb: Int) = $update($select(id), $incBson("toints" -> nb))
- def removeAllToints = $update($select.all, $unset("toints"), multi = true)
+ def incToints(id: ID, nb: Int) = coll.update($id(id), $inc("toints" -> nb))
+ def removeAllToints = coll.update($empty, $unset("toints"), multi = true)
def authenticateById(id: ID, password: String): Fu[Option[User]] =
- checkPasswordById(id, password) flatMap { _ ?? ($find byId id) }
+ checkPasswordById(id, password) flatMap { _ ?? coll.byId[User](id) }
def authenticateByEmail(email: String, password: String): Fu[Option[User]] =
checkPasswordByEmail(email, password) flatMap { _ ?? byEmail(email) }
- private case class AuthData(password: String, salt: String, enabled: Boolean, sha512: Boolean) {
- def compare(p: String) = password == sha512.fold(hash512(p, salt), hash(p, salt))
+ private case class AuthData(password: String, salt: String, enabled: Boolean, sha512: Option[Boolean]) {
+ def compare(p: String) = password == (~sha512).fold(hash512(p, salt), hash(p, salt))
}
- private object AuthData {
-
- import lila.db.JsTube.Helpers._
- import play.api.libs.json._
-
- private def defaults = Json.obj("sha512" -> false)
-
- lazy val reader = (__.json update merge(defaults)) andThen Json.reads[AuthData]
- }
+ private implicit val AuthDataBSONHandler = Macros.handler[AuthData]
def checkPasswordById(id: ID, password: String): Fu[Boolean] =
- checkPassword($select(id), password)
+ checkPassword($id(id), password)
def checkPasswordByEmail(email: String, password: String): Fu[Boolean] =
- checkPassword(Json.obj(F.email -> email), password)
+ checkPassword($doc(F.email -> email), password)
- private def checkPassword(select: JsObject, password: String): Fu[Boolean] =
- $projection.one(select, Seq("password", "salt", "enabled", "sha512", "email")) { obj =>
- (AuthData.reader reads obj).asOpt
- } map {
+ private def checkPassword(select: Bdoc, password: String): Fu[Boolean] =
+ coll.uno[AuthData](select) map {
_ ?? (data => data.enabled && data.compare(password))
}
def getPasswordHash(id: ID): Fu[Option[String]] =
- $primitive.one($select(id), "password")(_.asOpt[String])
+ coll.primitiveOne[String]($id(id), "password")
def create(username: String, password: String, email: Option[String], blind: Boolean, mobileApiVersion: Option[Int]): Fu[Option[User]] =
!nameExists(username) flatMap {
_ ?? {
- $insert.bson(newUser(username, password, email, blind, mobileApiVersion)) >> named(normalize(username))
+ coll.insert(newUser(username, password, email, blind, mobileApiVersion)) >>
+ named(normalize(username))
}
}
def nameExists(username: String): Fu[Boolean] = idExists(normalize(username))
- def idExists(id: String): Fu[Boolean] = $count exists id
+ def idExists(id: String): Fu[Boolean] = coll exists $id(id)
def engineIds: Fu[Set[String]] =
- coll.distinct("_id", BSONDocument("engine" -> true).some) map lila.db.BSON.asStringSet
+ coll.distinct("_id", $doc("engine" -> true).some) map lila.db.BSON.asStringSet
def usernamesLike(username: String, max: Int = 10): Fu[List[String]] = {
import java.util.regex.Matcher.quoteReplacement
val escaped = """^([\w-]*).*$""".r.replaceAllIn(normalize(username), m => quoteReplacement(m group 1))
val regex = "^" + escaped + ".*$"
- $primitive(
- $select.byId($regex(regex)) ++ enabledSelect,
- F.username,
- _ sort $sort.desc("_id"),
- max.some
- )(_.asOpt[String])
+ coll.find($doc("_id".$regex(regex, "")), $doc(F.username -> true))
+ .sort($sort desc "_id")
+ .cursor[Bdoc]().gather[List](max)
+ .map {
+ _ flatMap { _.getAs[String](F.username) }
+ }
}
- def toggleEngine(id: ID): Funit = $update.docBson[ID, User](id) { u =>
- $setBson("engine" -> BSONBoolean(!u.engine))
- }
+ def toggleEngine(id: ID): Funit =
+ coll.fetchUpdate[User]($id(id)) { u =>
+ $set("engine" -> !u.engine)
+ }
- def setEngine(id: ID, v: Boolean): Funit = $update.field(id, "engine", v)
+ def setEngine(id: ID, v: Boolean): Funit = coll.updateField($id(id), "engine", v).void
- def setBooster(id: ID, v: Boolean): Funit = $update.field(id, "booster", v)
+ def setBooster(id: ID, v: Boolean): Funit = coll.updateField($id(id), "booster", v).void
- def toggleIpBan(id: ID) = $update.doc[ID, User](id) { u => $set("ipBan" -> !u.ipBan) }
+ def toggleIpBan(id: ID) = coll.fetchUpdate[User]($id(id)) { u => $set("ipBan" -> !u.ipBan) }
- def toggleKid(user: User) = $update.field(user.id, "kid", !user.kid)
+ def toggleKid(user: User) = coll.updateField($id(user.id), "kid", !user.kid)
- def updateTroll(user: User) = $update.field(user.id, "troll", user.troll)
+ def updateTroll(user: User) = coll.updateField($id(user.id), "troll", user.troll)
- def isEngine(id: ID): Fu[Boolean] = $count.exists($select(id) ++ engineSelect(true))
+ def isEngine(id: ID): Fu[Boolean] = coll.exists($id(id) ++ engineSelect(true))
- def isTroll(id: ID): Fu[Boolean] = $count.exists($select(id) ++ trollSelect(true))
+ def isTroll(id: ID): Fu[Boolean] = coll.exists($id(id) ++ trollSelect(true))
- def setRoles(id: ID, roles: List[String]) = $update.field(id, "roles", roles)
+ def setRoles(id: ID, roles: List[String]) = coll.updateField($id(id), "roles", roles)
- def enable(id: ID) = $update.field(id, "enabled", true)
+ def enable(id: ID) = coll.updateField($id(id), "enabled", true)
- def disable(user: User) = $update(
- $select(user.id),
- BSONDocument("$set" -> BSONDocument("enabled" -> false)) ++
+ def disable(user: User) = coll.update(
+ $id(user.id),
+ $doc("$set" -> $doc("enabled" -> false)) ++
user.lameOrTroll.fold(
- BSONDocument(),
- BSONDocument("$unset" -> BSONDocument("email" -> true))
+ $doc(),
+ $doc("$unset" -> $doc("email" -> true))
)
)
def passwd(id: ID, password: String): Funit =
- $primitive.one($select(id), "salt")(_.asOpt[String]) flatMap { saltOption =>
+ coll.primitiveOne[String]($id(id), "salt") flatMap { saltOption =>
saltOption ?? { salt =>
- $update($select(id), $set(Json.obj(
+ coll.update($id(id), $set(
"password" -> hash(password, salt),
- "sha512" -> false)))
+ "sha512" -> false)).void
}
}
- def email(id: ID, email: String): Funit = $update.field(id, F.email, email)
+ def email(id: ID, email: String): Funit = coll.updateField($id(id), F.email, email).void
- def email(id: ID): Fu[Option[String]] = $primitive.one($select(id), F.email)(_.asOpt[String])
+ def email(id: ID): Fu[Option[String]] = coll.primitiveOne[String]($id(id), F.email)
def hasEmail(id: ID): Fu[Boolean] = email(id).map(_.isDefined)
def perfOf(id: ID, perfType: PerfType): Fu[Option[Perf]] = coll.find(
- BSONDocument("_id" -> id),
- BSONDocument(s"${F.perfs}.${perfType.key}" -> true)
- ).one[BSONDocument].map {
- _.flatMap(_.getAs[BSONDocument](F.perfs)).flatMap(_.getAs[Perf](perfType.key))
+ $doc("_id" -> id),
+ $doc(s"${F.perfs}.${perfType.key}" -> true)
+ ).uno[Bdoc].map {
+ _.flatMap(_.getAs[Bdoc](F.perfs)).flatMap(_.getAs[Perf](perfType.key))
}
def setSeenAt(id: ID) {
- $update.fieldUnchecked(id, "seenAt", $date(DateTime.now))
+ coll.updateFieldUnchecked($id(id), "seenAt", DateTime.now)
}
def recentlySeenNotKidIdsCursor(since: DateTime) =
- coll.find(BSONDocument(
+ coll.find($doc(
F.enabled -> true,
- "seenAt" -> BSONDocument("$gt" -> since),
- "count.game" -> BSONDocument("$gt" -> 9),
- "kid" -> BSONDocument("$ne" -> true)
- ), BSONDocument("_id" -> true)).cursor[BSONDocument]()
+ "seenAt" -> $doc("$gt" -> since),
+ "count.game" -> $doc("$gt" -> 9),
+ "kid" -> $doc("$ne" -> true)
+ ), $doc("_id" -> true)).cursor[Bdoc]()
- def setLang(id: ID, lang: String) = $update.field(id, "lang", lang)
+ def setLang(id: ID, lang: String) = coll.updateField($id(id), "lang", lang).void
def idsSumToints(ids: Iterable[String]): Fu[Int] =
- ids.nonEmpty ?? coll.aggregate(Match(BSONDocument("_id" -> BSONDocument("$in" -> ids))),
+ ids.nonEmpty ?? coll.aggregate(Match($inIds(ids)),
List(Group(BSONNull)(F.toints -> SumField(F.toints)))).map(
_.documents.headOption flatMap { _.getAs[Int](F.toints) }
).map(~_)
def filterByEngine(userIds: List[String]): Fu[List[String]] =
- $primitive(Json.obj("_id" -> $in(userIds)) ++ engineSelect(true), F.username)(_.asOpt[String])
+ coll.primitive[String]($inIds(userIds) ++ engineSelect(true), F.username)
def countEngines(userIds: List[String]): Fu[Int] =
- coll.count(BSONDocument(
- "_id" -> BSONDocument("$in" -> userIds),
- F.engine -> true
- ).some)
+ coll.countSel($inIds(userIds) ++ engineSelect(true))
def mustConfirmEmail(id: String): Fu[Boolean] =
- $count.exists($select(id) ++ Json.obj(F.mustConfirmEmail -> $exists(true)))
+ coll.exists($id(id) ++ $doc(F.mustConfirmEmail $exists true))
- def setEmailConfirmed(id: String): Funit = $update(
- $select(id),
- BSONDocument("$unset" -> BSONDocument(F.mustConfirmEmail -> true)))
+ def setEmailConfirmed(id: String): Funit = coll.update($id(id), $unset(F.mustConfirmEmail)).void
private def newUser(username: String, password: String, email: Option[String], blind: Boolean, mobileApiVersion: Option[Int]) = {
@@ -346,20 +333,20 @@ object UserRepo {
implicit def perfsHandler = Perfs.perfsBSONHandler
import lila.db.BSON.BSONJodaDateTimeHandler
- BSONDocument(
+ $doc(
F.id -> normalize(username),
F.username -> username,
F.email -> email,
F.mustConfirmEmail -> (email.isDefined && mobileApiVersion.isEmpty).option(DateTime.now),
"password" -> hash(password, salt),
"salt" -> salt,
- F.perfs -> Json.obj(),
+ F.perfs -> $empty,
F.count -> Count.default,
F.enabled -> true,
F.createdAt -> DateTime.now,
F.createdWithApiVersion -> mobileApiVersion,
F.seenAt -> DateTime.now) ++ {
- if (blind) BSONDocument("blind" -> true) else BSONDocument()
+ if (blind) $doc("blind" -> true) else $empty
}
}
diff --git a/modules/user/src/main/package.scala b/modules/user/src/main/package.scala
index a48030ff9b..aead83eec8 100644
--- a/modules/user/src/main/package.scala
+++ b/modules/user/src/main/package.scala
@@ -2,12 +2,6 @@ package lila
package object user extends PackageObject with WithPlay {
- object tube {
-
- // expose user tube
- implicit lazy val userTube = User.tube inColl Env.current.userColl
- }
-
private[user] def logger = lila.log("user")
type Trophies = List[Trophy]
diff --git a/modules/video/src/main/VideoApi.scala b/modules/video/src/main/VideoApi.scala
index 4f8374a224..2b3f57a46f 100644
--- a/modules/video/src/main/VideoApi.scala
+++ b/modules/video/src/main/VideoApi.scala
@@ -6,8 +6,8 @@ import reactivemongo.core.commands._
import scala.concurrent.duration._
import lila.common.paginator._
-import lila.db.paginator.BSONAdapter
-import lila.db.Types.Coll
+import lila.db.dsl._
+import lila.db.paginator.Adapter
import lila.memo.AsyncCache
import lila.user.{ User, UserRepo }
@@ -41,16 +41,16 @@ private[video] final class VideoApi(
private val maxPerPage = 18
def find(id: Video.ID): Fu[Option[Video]] =
- videoColl.find(BSONDocument("_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 " "
- val textScore = BSONDocument("score" -> BSONDocument("$meta" -> "textScore"))
+ val textScore = $doc("score" -> $doc("$meta" -> "textScore"))
Paginator(
- adapter = new BSONAdapter[Video](
+ adapter = new Adapter[Video](
collection = videoColl,
- selector = BSONDocument(
- "$text" -> BSONDocument("$search" -> q)
+ selector = $doc(
+ "$text" -> $doc("$search" -> q)
),
projection = textScore,
sort = textScore
@@ -61,19 +61,19 @@ private[video] final class VideoApi(
def save(video: Video): Funit =
videoColl.update(
- BSONDocument("_id" -> video.id),
- BSONDocument("$set" -> video),
+ $doc("_id" -> video.id),
+ $doc("$set" -> video),
upsert = true).void
def removeNotIn(ids: List[Video.ID]) =
videoColl.remove(
- BSONDocument("_id" -> BSONDocument("$nin" -> ids))
+ $doc("_id" $nin (ids: _*))
).void
def setMetadata(id: Video.ID, metadata: Youtube.Metadata) =
videoColl.update(
- BSONDocument("_id" -> id),
- BSONDocument("$set" -> BSONDocument("metadata" -> metadata)),
+ $doc("_id" -> id),
+ $doc("$set" -> $doc("metadata" -> metadata)),
upsert = false
).void
@@ -81,11 +81,11 @@ private[video] final class VideoApi(
videoColl.distinct("_id", none) map lila.db.BSON.asStrings
def popular(user: Option[User], page: Int): Fu[Paginator[VideoView]] = Paginator(
- adapter = new BSONAdapter[Video](
+ adapter = new Adapter[Video](
collection = videoColl,
- selector = BSONDocument(),
- projection = BSONDocument(),
- sort = BSONDocument("metadata.likes" -> -1)
+ selector = $empty,
+ projection = $empty,
+ sort = $doc("metadata.likes" -> -1)
) mapFutureList videoViews(user),
currentPage = page,
maxPerPage = maxPerPage)
@@ -93,37 +93,33 @@ private[video] final class VideoApi(
def byTags(user: Option[User], tags: List[Tag], page: Int): Fu[Paginator[VideoView]] =
if (tags.isEmpty) popular(user, page)
else Paginator(
- adapter = new BSONAdapter[Video](
+ adapter = new Adapter[Video](
collection = videoColl,
- selector = BSONDocument(
- "tags" -> BSONDocument("$all" -> tags)
- ),
- projection = BSONDocument(),
- sort = BSONDocument("metadata.likes" -> -1)
+ selector = $doc("tags".$all(tags: _*)),
+ projection = $empty,
+ sort = $doc("metadata.likes" -> -1)
) mapFutureList videoViews(user),
currentPage = page,
maxPerPage = maxPerPage)
def byAuthor(user: Option[User], author: String, page: Int): Fu[Paginator[VideoView]] =
Paginator(
- adapter = new BSONAdapter[Video](
+ adapter = new Adapter[Video](
collection = videoColl,
- selector = BSONDocument(
- "author" -> author
- ),
- projection = BSONDocument(),
- sort = BSONDocument("metadata.likes" -> -1)
+ selector = $doc("author" -> author),
+ projection = $empty,
+ sort = $doc("metadata.likes" -> -1)
) mapFutureList videoViews(user),
currentPage = page,
maxPerPage = maxPerPage)
def similar(user: Option[User], video: Video, max: Int): Fu[Seq[VideoView]] =
- videoColl.find(BSONDocument(
- "tags" -> BSONDocument("$in" -> video.tags),
- "_id" -> BSONDocument("$ne" -> video.id)
- )).sort(BSONDocument("metadata.likes" -> -1))
+ videoColl.find($doc(
+ "tags" $in (video.tags: _*),
+ "_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)
@@ -142,25 +138,23 @@ private[video] final class VideoApi(
object view {
def find(videoId: Video.ID, userId: String): Fu[Option[View]] =
- viewColl.find(BSONDocument(
+ 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(_ => ())
def hasSeen(user: User, video: Video): Fu[Boolean] =
- viewColl.count(BSONDocument(
+ viewColl.count($doc(
View.BSONFields.id -> View.makeId(video.id, user.id)
).some) map (0!=)
def seenVideoIds(user: User, videos: Seq[Video]): Fu[Set[Video.ID]] =
viewColl.distinct(View.BSONFields.videoId,
- BSONDocument(
- "_id" -> BSONDocument("$in" -> videos.map { v =>
- View.makeId(v.id, user.id)
- })
- ).some) map lila.db.BSON.asStringSet
+ $inIds(videos.map { v =>
+ View.makeId(v.id, user.id)
+ }).some) map lila.db.BSON.asStringSet
}
object tag {
@@ -182,8 +176,8 @@ private[video] final class VideoApi(
tags.filterNot(_.isNumeric)
}
else videoColl.aggregate(
- Match(BSONDocument("tags" -> BSONDocument("$all" -> filterTags))),
- List(Project(BSONDocument("tags" -> BSONBoolean(true))),
+ Match($doc("tags".$all(filterTags: _*))),
+ List(Project($doc("tags" -> true)),
Unwind("tags"), GroupField("tags")("nb" -> SumValue(1)))).map(
_.documents.flatMap(_.asOpt[TagNb]))
@@ -208,7 +202,7 @@ private[video] final class VideoApi(
private val popularCache = AsyncCache.single[List[TagNb]](
f = videoColl.aggregate(
- Project(BSONDocument("tags" -> BSONBoolean(true))), List(
+ Project($doc("tags" -> true)), List(
Unwind("tags"), GroupField("tags")("nb" -> SumValue(1)),
Sort(Descending("nb")))).map(
_.documents.flatMap(_.asOpt[TagNb])),
diff --git a/modules/wiki/src/main/Api.scala b/modules/wiki/src/main/Api.scala
index cb16726a31..09ae21ab8c 100644
--- a/modules/wiki/src/main/Api.scala
+++ b/modules/wiki/src/main/Api.scala
@@ -1,23 +1,19 @@
package lila.wiki
import Page.DefaultLang
-import play.api.libs.json._
-import lila.common.PimpedJson._
-import lila.db.api._
-import lila.db.Implicits._
-import tube.pageTube
+import lila.db.dsl._
-private[wiki] final class Api {
+private[wiki] final class Api(coll: Coll) {
+
+ import Page.PageBSONHandler
def show(slug: String, lang: String): Fu[Option[(Page, List[Page])]] = for {
- page ← $find.one(Json.obj("slug" -> slug, "lang" -> lang)) zip
- $find.one(Json.obj("slug" -> slug, "lang" -> DefaultLang)) map {
- case (a, b) => a orElse b
- }
- pages ← $find($query(Json.obj(
- "lang" -> $in(Seq(lang, DefaultLang))
- )).sort($sort asc "number"))
+ page ← coll.uno[Page]($doc("slug" -> slug, "lang" -> lang)) orElse
+ coll.uno[Page]($doc("slug" -> slug, "lang" -> DefaultLang))
+ pages ← coll.find($doc(
+ "lang" $in (lang, DefaultLang)
+ )).sort($sort asc "number").cursor[Page]().gather[List]()
} yield page map { _ -> makeMenu(pages) }
private def makeMenu(pages: List[Page]): List[Page] = {
diff --git a/modules/wiki/src/main/Env.scala b/modules/wiki/src/main/Env.scala
index f0fc887eec..dabe44df9d 100644
--- a/modules/wiki/src/main/Env.scala
+++ b/modules/wiki/src/main/Env.scala
@@ -2,8 +2,7 @@ package lila.wiki
import com.typesafe.config.Config
-import lila.db.api.$find
-import tube.pageTube
+import lila.db.dsl._
final class Env(config: Config, db: lila.db.Env) {
@@ -11,11 +10,14 @@ final class Env(config: Config, db: lila.db.Env) {
private val GitUrl = config getString "git.url"
private val MarkdownPath = config getString "markdown_path"
- lazy val api = new Api
+ private lazy val pageColl = db(CollectionPage)
- private lazy val fetcher = new Fetch(gitUrl = GitUrl, markdownPath = MarkdownPath)(pageColl)
+ lazy val api = new Api(pageColl)
- private[wiki] lazy val pageColl = db(CollectionPage)
+ private lazy val fetcher = new Fetch(
+ coll = pageColl,
+ gitUrl = GitUrl,
+ markdownPath = MarkdownPath)
def cli = new lila.common.Cli {
def process = {
diff --git a/modules/wiki/src/main/Fetch.scala b/modules/wiki/src/main/Fetch.scala
index 05db6790d2..9199a629ed 100644
--- a/modules/wiki/src/main/Fetch.scala
+++ b/modules/wiki/src/main/Fetch.scala
@@ -9,11 +9,14 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Repository
import Page.DefaultLang
-import lila.db.api._
-import lila.db.Types.Coll
-import tube._
+import lila.db.dsl._
-private[wiki] final class Fetch(gitUrl: String, markdownPath: String)(implicit coll: Coll) {
+private[wiki] final class Fetch(
+ coll: Coll,
+ gitUrl: String,
+ markdownPath: String) {
+
+ import Page.PageBSONHandler
def apply: Funit = getFiles flatMap { files =>
val (defaultPages, langPages) = files.map(filePage).flatten partition (_.isDefaultLang)
@@ -22,7 +25,9 @@ private[wiki] final class Fetch(gitUrl: String, markdownPath: String)(implicit c
page.copy(slug = default.slug)
}
}).flatten
- $remove($select.all) >> (newLangPages ::: defaultPages).map($insert(_)).sequenceFu.void
+ coll.remove($empty) >> (newLangPages ::: defaultPages).map { page =>
+ coll.insert[Page](page)
+ }.sequenceFu.void
}
private def filePage(file: File): Option[Page] = {
diff --git a/modules/wiki/src/main/Page.scala b/modules/wiki/src/main/Page.scala
index afd4fac2bb..a4852db959 100644
--- a/modules/wiki/src/main/Page.scala
+++ b/modules/wiki/src/main/Page.scala
@@ -34,11 +34,6 @@ object Page {
case _ => none
}
- import lila.db.JsTube
- import play.api.libs.json._
-
- private[wiki] lazy val tube = JsTube(Json.reads[Page], Json.writes[Page])
-
// does not lowercase
private def slugify(input: String) = {
val nowhitespace = input.replace(" ", "_")
@@ -48,5 +43,7 @@ object Page {
private def dropNumber(input: String) =
"""^\d+_(.+)$""".r.replaceAllIn(input, m => quoteReplacement(m group 1))
-}
+ import lila.db.dsl.BSONJodaDateTimeHandler
+ implicit val PageBSONHandler = reactivemongo.bson.Macros.handler[Page]
+}
diff --git a/modules/wiki/src/main/package.scala b/modules/wiki/src/main/package.scala
index d36bbd6f3a..6f41d410b8 100644
--- a/modules/wiki/src/main/package.scala
+++ b/modules/wiki/src/main/package.scala
@@ -1,9 +1,3 @@
package lila
-package object wiki extends PackageObject with WithPlay {
-
- object tube {
-
- private[wiki] implicit lazy val pageTube = Page.tube inColl Env.current.pageColl
- }
-}
+package object wiki extends PackageObject with WithPlay
diff --git a/modules/worldMap/src/main/Env.scala b/modules/worldMap/src/main/Env.scala
index c278eb5627..8a6cb75026 100644
--- a/modules/worldMap/src/main/Env.scala
+++ b/modules/worldMap/src/main/Env.scala
@@ -13,19 +13,20 @@ final class Env(
private val GeoIPFile = config getString "geoip.file"
private val GeoIPCacheTtl = config duration "geoip.cache_ttl"
- private val stream = system.actorOf(
+ val stream = system.actorOf(
Props(new Stream(
geoIp = MaxMindIpGeo(GeoIPFile, 0),
geoIpCacheTtl = GeoIPCacheTtl)))
- system.lilaBus.subscribe(stream, 'changeFeaturedGame, 'streams, 'nbMembers, 'nbRounds)
-
- def getStream = {
- import play.api.libs.iteratee._
- import play.api.libs.json._
- import akka.pattern.ask
- import makeTimeout.short
- stream ? Stream.Get mapTo manifest[Enumerator[JsValue]]
- }
+
+ system.lilaBus.subscribe(stream, 'roundDoor)
+
+ import akka.pattern.ask
+ import akka.stream.scaladsl.Source
+ import scala.concurrent.duration._
+ import play.api.libs.json.JsValue
+ implicit val timeout = akka.util.Timeout(5 seconds)
+ def getSource: Fu[Stream.SourceType] =
+ stream ? Stream.GetSource mapTo manifest[Stream.SourceType]
}
object Env {
diff --git a/modules/worldMap/src/main/Stream.scala b/modules/worldMap/src/main/Stream.scala
index fcdb91c520..ff52c15d7d 100644
--- a/modules/worldMap/src/main/Stream.scala
+++ b/modules/worldMap/src/main/Stream.scala
@@ -1,10 +1,11 @@
package lila.worldMap
import akka.actor._
+import akka.stream._
+import akka.stream.scaladsl._
import com.sanoma.cda.geoip.{ MaxMindIpGeo, IpLocation }
import java.security.MessageDigest
import lila.hub.actorApi.round.SocketEvent
-import play.api.libs.iteratee._
import play.api.libs.json._
import scala.concurrent.duration._
@@ -14,49 +15,58 @@ private final class Stream(
geoIp: MaxMindIpGeo,
geoIpCacheTtl: Duration) extends Actor {
- import Stream.game2json
+ import Stream._
+ implicit val mat = context.system.lilaMat
- val games = scala.collection.mutable.Map.empty[String, Stream.Game]
+ val games = scala.collection.mutable.Map.empty[String, Game]
- private def makeMd5 = MessageDigest getInstance "MD5"
+ val (out, publisher) = lila.common.AkkaStream.actorPublisher[Event](100)
- private val loadCompleteJson = Json.obj("loadComplete" -> true)
+ def preload = Source[JsValue](games.values.pp.map(game2json(makeMd5)).toList.pp)
+ val preloadComplete = Source[JsValue](List(Json.obj("loadComplete" -> true)))
+
+ val transformer = Flow.fromFunction[Event, JsValue] {
+ case Event.Add(game) => game2json(makeMd5)(game)
+ case Event.Remove(id) => Json.obj("id" -> id)
+ }
+
+ def realtime = Source.fromPublisher[Event](publisher)
+
+ def makeSource =
+ Source.combine(preload, preloadComplete, realtime via transformer)(_ => Concat())
def receive = {
+
case SocketEvent.OwnerJoin(id, color, ip) =>
- ipCache get ip foreach { point =>
+ (ipCache get "58.8.28.97").pp foreach { point =>
val game = games get id match {
case Some(game) => game withPoint point
- case None => Stream.Game(id, List(point))
+ case None => Game(id, List(point))
}
games += (id -> game)
- channel push Stream.Event.Add(game)
+ out ! Event.Add(game).pp
}
+
case SocketEvent.Stop(id) =>
games -= id
- channel push Stream.Event.Remove(id)
- case Stream.Get => sender ! {
- Enumerator.enumerate(games.values.map(game2json(makeMd5))) andThen
- Enumerator.enumerate(List(loadCompleteJson)) andThen
- producer
- }
- }
+ out ! Event.Remove(id)
- val (enumerator, channel) = Concurrent.broadcast[Stream.Event]
-
- val producer = enumerator &> Enumeratee.map[Stream.Event].apply[JsValue] {
- case Stream.Event.Add(game) => game2json(makeMd5)(game)
- case Stream.Event.Remove(id) => Json.obj("id" -> id)
+ case GetSource =>
+ println(games)
+ sender ! makeSource
}
+ def makeMd5 = MessageDigest getInstance "MD5"
val ipCache = lila.memo.Builder.cache(geoIpCacheTtl, ipToPoint)
- def ipToPoint(ip: String): Option[Stream.Point] =
- geoIp getLocation ip flatMap Stream.toPoint
+ def ipToPoint(ip: String): Option[Point] =
+ geoIp getLocation ip flatMap toPoint
}
object Stream {
- case object Get
+ case object GetSource
+
+ type SourceType = Source[JsValue, akka.NotUsed]
case class Game(id: String, points: List[Point]) {
diff --git a/project/Build.scala b/project/Build.scala
index 7bcd658085..c493942993 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -1,7 +1,8 @@
import com.typesafe.sbt.packager.Keys.scriptClasspath
import com.typesafe.sbt.web.SbtWeb.autoImport._
-import play.Play.autoImport._
+import play.sbt.Play.autoImport._
import play.sbt.PlayImport._
+import play.sbt.routes.RoutesKeys._
import play.twirl.sbt.Import._
import PlayKeys._
import sbt._, Keys._
@@ -21,6 +22,7 @@ object ApplicationBuild extends Build {
scalacOptions := compilerOptions,
incOptions := incOptions.value.withNameHashing(true),
updateOptions := updateOptions.value.withCachedResolution(true),
+ routesGenerator := StaticRoutesGenerator,
sources in doc in Compile := List(),
// disable publishing the main API jar
publishArtifact in (Compile, packageDoc) := false,
@@ -33,9 +35,10 @@ object ApplicationBuild extends Build {
// offline := true,
libraryDependencies ++= Seq(
scalaz, scalalib, hasher, config, apache,
- jgit, findbugs, RM, PRM, akka.actor, akka.slf4j,
+ jgit, findbugs, RM, akka.actor, akka.slf4j, akka.stream,
spray.caching, maxmind, prismic,
- kamon.core, kamon.statsd, pushy, java8compat, semver),
+ kamon.core, kamon.statsd, pushy, java8compat, semver,
+ jzlib),
TwirlKeys.templateImports ++= Seq(
"lila.game.{ Game, Player, Pov }",
"lila.tournament.Tournament",
@@ -74,23 +77,23 @@ object ApplicationBuild extends Build {
lazy val puzzle = project("puzzle", Seq(
common, memo, hub, db, user, rating)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val quote = project("quote", Seq())
lazy val opening = project("opening", Seq(
common, memo, hub, db, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val video = project("video", Seq(
common, memo, hub, db, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val coordinate = project("coordinate", Seq(common, db)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val worldMap = project("worldMap", Seq(common, hub, memo, rating)).settings(
@@ -98,21 +101,21 @@ object ApplicationBuild extends Build {
)
lazy val qa = project("qa", Seq(common, db, memo, user, security)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val blog = project("blog", Seq(common, memo, user, message)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM, prismic)
+ libraryDependencies ++= provided(play.api, RM, prismic)
)
lazy val donation = project("donation", Seq(
common, db, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val evaluation = project("evaluation", Seq(
common, hub, db, user, game, analyse)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
// lazy val simulation = project("simulation", Seq(
@@ -125,19 +128,19 @@ object ApplicationBuild extends Build {
)
lazy val rating = project("rating", Seq(common, db, chess)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val perfStat = project("perfStat", Seq(common, db, chess, user, game, rating)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val history = project("history", Seq(common, db, memo, game, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val db = project("db", Seq(common)).settings(
- libraryDependencies ++= provided(play.test, play.api, RM, PRM)
+ libraryDependencies ++= provided(play.test, play.api, RM)
)
lazy val memo = project("memo", Seq(common, db)).settings(
@@ -149,138 +152,138 @@ object ApplicationBuild extends Build {
)
lazy val chat = project("chat", Seq(common, db, user, security, i18n)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val timeline = project("timeline", Seq(common, db, game, user, hub, security, relation)).settings(
libraryDependencies ++= provided(
- play.api, play.test, RM, PRM)
+ play.api, play.test, RM)
)
lazy val mod = project("mod", Seq(common, db, user, hub, security, game, analyse, evaluation, report)).settings(
libraryDependencies ++= provided(
- play.api, play.test, RM, PRM)
+ play.api, play.test, RM)
)
lazy val user = project("user", Seq(common, memo, db, hub, chess, rating)).settings(
libraryDependencies ++= provided(
- play.api, play.test, RM, PRM, hasher)
+ play.api, play.test, RM, hasher)
)
lazy val game = project("game", Seq(common, memo, db, hub, user, chess, chat)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM)
+ play.api, RM)
)
lazy val gameSearch = project("gameSearch", Seq(common, hub, chess, search, game)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM)
+ play.api, RM)
)
lazy val tv = project("tv", Seq(common, db, hub, socket, game, user, chess)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM, hasher)
+ libraryDependencies ++= provided(play.api, RM, hasher)
)
lazy val analyse = project("analyse", Seq(common, hub, chess, game, user)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM, spray.caching)
+ play.api, RM, spray.caching)
)
lazy val round = project("round", Seq(
common, db, memo, hub, socket, chess, game, user,
i18n, fishnet, pref, chat, history, playban)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM, hasher, kamon.core)
+ libraryDependencies ++= provided(play.api, RM, hasher, kamon.core)
)
lazy val lobby = project("lobby", Seq(
common, db, memo, hub, socket, chess, game, user, round, timeline, relation, playban, security)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val setup = project("setup", Seq(
common, db, memo, hub, socket, chess, game, user, lobby, pref, relation)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val importer = project("importer", Seq(common, chess, game, round)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val insight = project("insight",
Seq(common, chess, game, user, analyse, relation, pref, socket, round, security)
).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val tournament = project("tournament", Seq(
common, hub, socket, chess, game, round, security, chat, memo, quote)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val simul = project("simul", Seq(
common, hub, socket, chess, game, round, chat, memo, quote)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val fishnet = project("fishnet", Seq(common, chess, game, analyse, db)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM, semver)
+ libraryDependencies ++= provided(play.api, RM, semver)
)
lazy val security = project("security", Seq(common, hub, db, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM, maxmind, hasher)
+ libraryDependencies ++= provided(play.api, RM, maxmind, hasher)
)
lazy val shutup = project("shutup", Seq(common, db, hub, game, relation)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val challenge = project("challenge", Seq(common, db, hub, setup, game)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val playban = project("playban", Seq(common, db, game)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val push = project("push", Seq(common, db, user, game, challenge)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM, pushy)
+ libraryDependencies ++= provided(play.api, RM, pushy)
)
lazy val slack = project("slack", Seq(common, hub, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val relation = project("relation", Seq(common, db, memo, hub, user, game, pref)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val pref = project("pref", Seq(common, db, user)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val message = project("message", Seq(common, db, user, hub, relation, security)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM, spray.caching)
+ play.api, RM, spray.caching)
)
lazy val forum = project("forum", Seq(common, db, user, security, hub, mod)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM, spray.caching)
+ play.api, RM, spray.caching)
)
lazy val forumSearch = project("forumSearch", Seq(common, hub, forum, search)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM)
+ play.api, RM)
)
lazy val team = project("team", Seq(common, memo, db, user, forum, security, hub)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM)
+ play.api, RM)
)
lazy val teamSearch = project("teamSearch", Seq(common, hub, team, search)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM)
+ play.api, RM)
)
lazy val i18n = project("i18n", Seq(common, db, user, hub)).settings(
@@ -290,26 +293,26 @@ object ApplicationBuild extends Build {
(sourceManaged in Compile).value / "messages"
)
}.taskValue,
- libraryDependencies ++= provided(play.api, RM, PRM, jgit)
+ libraryDependencies ++= provided(play.api, RM, jgit)
)
lazy val bookmark = project("bookmark", Seq(common, memo, db, hub, user, game)).settings(
libraryDependencies ++= provided(
- play.api, play.test, RM, PRM)
+ play.api, play.test, RM)
)
lazy val wiki = project("wiki", Seq(common, db)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM, jgit, guava)
+ play.api, RM, jgit, guava)
)
lazy val report = project("report", Seq(common, db, user)).settings(
libraryDependencies ++= provided(
- play.api, RM, PRM)
+ play.api, RM)
)
lazy val explorer = project("explorer", Seq(common, db, game)).settings(
- libraryDependencies ++= provided(play.api, RM, PRM)
+ libraryDependencies ++= provided(play.api, RM)
)
lazy val notification = project("notification", Seq(common, user, hub)).settings(
@@ -321,7 +324,7 @@ object ApplicationBuild extends Build {
)
lazy val socket = project("socket", Seq(common, hub, memo)).settings(
- libraryDependencies ++= provided(play.api)
+ libraryDependencies ++= provided(play.api, akka.stream)
)
lazy val hub = project("hub", Seq(common, chess)).settings(
diff --git a/project/BuildSettings.scala b/project/BuildSettings.scala
index 3b1851d648..e696d4aa5e 100644
--- a/project/BuildSettings.scala
+++ b/project/BuildSettings.scala
@@ -1,4 +1,4 @@
-import play.Play.autoImport._
+import play.sbt.Play.autoImport._
import sbt._, Keys._
object BuildSettings {
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index f9bb25f3f9..52890c6819 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -35,15 +35,15 @@ object Dependencies {
val jgit = "org.eclipse.jgit" % "org.eclipse.jgit" % "3.2.0.201312181205-r"
val jodaTime = "joda-time" % "joda-time" % "2.9.2"
val RM = "org.reactivemongo" % "reactivemongo_2.11" % "0.11.9.1-LILA"
- val PRM = "org.reactivemongo" % "play2-reactivemongo_2.11" % "0.11.9"
val maxmind = "com.sanoma.cda" %% "maxmind-geoip2-scala" % "1.2.3-THIB"
- val prismic = "io.prismic" %% "scala-kit" % "1.2.11-THIB"
+ val prismic = "io.prismic" %% "scala-kit" % "1.2.11-THIB2"
val pushy = "com.relayrides" % "pushy" % "0.4.3"
val java8compat = "org.scala-lang.modules" %% "scala-java8-compat" % "0.7.0"
val semver = "com.gilt" %% "gfc-semver" % "0.0.2-9-g11173e1"
+ val jzlib = "com.jcraft" % "jzlib" % "1.1.3"
object play {
- val version = "2.4.6"
+ val version = "2.5.1"
val api = "com.typesafe.play" %% "play" % version
val test = "com.typesafe.play" %% "play-test" % version
}
@@ -53,9 +53,10 @@ object Dependencies {
val util = "io.spray" %% "spray-util" % version
}
object akka {
- val version = "2.4.2"
+ val version = "2.4.3"
val actor = "com.typesafe.akka" %% "akka-actor" % version
val slf4j = "com.typesafe.akka" %% "akka-slf4j" % version
+ val stream = "com.typesafe.akka" %% "akka-stream" % version
}
object kamon {
val version = "0.5.2"
diff --git a/project/MessageCompiler.scala b/project/MessageCompiler.scala
index 5ba3f536cf..bb7d29052d 100644
--- a/project/MessageCompiler.scala
+++ b/project/MessageCompiler.scala
@@ -19,9 +19,11 @@ object MessageCompiler {
val (lang, file) = entry
val srcFile = src / file
val dstFile = dst / s"$lang.scala"
- val pairs = readLines(srcFile) map makePair
- printToFile(dstFile) {
- render(lang, pairs)
+ if (srcFile.lastModified > dstFile.lastModified) {
+ val pairs = readLines(srcFile) map makePair
+ printToFile(dstFile) {
+ render(lang, pairs)
+ }
}
dstFile
}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 6a8deb5e3a..ec045af777 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,3 +1,3 @@
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.6")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.1")
diff --git a/public/worldMap/app.js b/public/worldMap/app.js
index e4a23e8d04..cdffbcc453 100644
--- a/public/worldMap/app.js
+++ b/public/worldMap/app.js
@@ -39,7 +39,7 @@ $(function() {
point[1] + Math.random() - 0.5);
};
- var source = new EventSource("http://en.lichess.org/network/stream");
+ var source = new EventSource("http://" + location.hostname + "/network/stream");
var removeFunctions = {};