rewrite forum DB code

This commit is contained in:
Thibault Duplessis 2016-04-02 11:01:52 +07:00
parent d11f0acaff
commit 1b0628c168
12 changed files with 69 additions and 74 deletions

View file

@ -19,17 +19,7 @@ abstract class BSON[T]
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)
}
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))
}
object BSON extends Handlers {
object MapDocument {
@ -88,24 +78,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 = {

View file

@ -0,0 +1,37 @@
package lila.db
import dsl._
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)
}
}
}

View file

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

View file

@ -84,8 +84,4 @@ private[forum] final class CategApi(env: Env) {
lastPostIdTroll = lastPostTroll ?? (_.id)
)).void
} yield ()
def denormalize: Funit = env.categColl.list[Categ]($empty) flatMap { categs =>
categs.map(denormalize).sequenceFu
} void
}

View file

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

View file

@ -54,13 +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 {
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)

View file

@ -60,7 +60,7 @@ sealed abstract class PostRepo(troll: Boolean) {
else $doc("lang" $in langs)
def findDuplicate(post: Post): Fu[Option[Post]] = coll.one($doc(
"createdAt" -> $gt(DateTime.now.minusHours(1)),
"createdAt" $gt DateTime.now.minusHours(1),
"userId" -> ~post.userId,
"text" -> post.text
))

View file

@ -56,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,

View file

@ -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
}

View file

@ -13,22 +13,22 @@ sealed abstract class TopicRepo(troll: Boolean) {
import BSONHandlers.TopicBSONHandler
private lazy val trollFilter = troll.fold(
Json.obj(),
Json.obj("troll" -> false)
)
// 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.one[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)
@ -42,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
}

View file

@ -7,7 +7,7 @@ import play.api.libs.json._
import reactivemongo.bson._
import lila.common.LightUser
import lila.db.BSON._
import lila.db.BSON
import lila.db.dsl._
import lila.memo.{ ExpireSetMemo, MongoCache }
import lila.rating.{ Perf, PerfType }

View file

@ -7,7 +7,6 @@ import reactivemongo.bson._
import scala.concurrent.duration._
import lila.db.dsl._
import lila.db.BSON._
import lila.db.BSON.MapValue.MapHandler
import lila.memo.{ AsyncCache, MongoCache }
import lila.rating.{ Perf, PerfType }