study flat chapters WIP

studyFlatChapter
Thibault Duplessis 2021-02-02 12:30:20 +01:00
parent 570c6b4857
commit 29f506a41e
4 changed files with 113 additions and 83 deletions

View File

@ -9,6 +9,7 @@ import reactivemongo.api.bson._
import scala.util.Success
import scala.util.Try
import lila.common.Iso
import lila.db.BSON
import lila.db.BSON.{ Reader, Writer }
import lila.db.dsl._
@ -16,8 +17,6 @@ import lila.tree.Eval
import lila.tree.Eval.Score
import lila.tree.Node.{ Comment, Comments, Gamebook, Shape, Shapes }
import lila.common.Iso
object BSONHandlers {
import Chapter._
@ -159,65 +158,50 @@ object BSONHandlers {
)
}
implicit val ChildrenBSONHandler: BSONHandler[Node.Children] = tryHandler[Node.Children](
{ case arr: BSONArray =>
Try {
Node.Children(arr.values.view.flatMap(NodeBSONHandler.readOpt).toVector)
}
},
children =>
BSONArray(children.nodes map { n =>
NodeBSONHandler.writeTry(n).get
})
)
implicit lazy val NodeBSONHandler: BSON[Node] = new BSON[Node] {
def reads(r: Reader) =
Node(
id = r.get[UciCharPair]("i"),
ply = r int "p",
move = WithSan(r.get[Uci]("u"), r.str("s")),
fen = r.get[FEN]("f"),
check = r boolD "c",
shapes = r.getO[Shapes]("h") | Shapes.empty,
comments = r.getO[Comments]("co") | Comments.empty,
gamebook = r.getO[Gamebook]("ga"),
glyphs = r.getO[Glyphs]("g") | Glyphs.empty,
score = r.getO[Score]("e"),
crazyData = r.getO[Crazyhouse.Data]("z"),
clock = r.getO[Centis]("l"),
children =
try {
r.get[Node.Children]("n")
} catch {
case _: StackOverflowError =>
logger.warn(s"study ChildrenBSONHandler StackOverflowError")
Node.emptyChildren
},
forceVariation = r boolD "fv"
)
def writes(w: Writer, s: Node) =
$doc(
"i" -> s.id,
"p" -> s.ply,
"u" -> s.move.uci,
"s" -> s.move.san,
"f" -> s.fen,
"c" -> w.boolO(s.check),
"h" -> s.shapes.value.nonEmpty.option(s.shapes),
"co" -> s.comments.value.nonEmpty.option(s.comments),
"ga" -> s.gamebook,
"g" -> s.glyphs.nonEmpty,
"e" -> s.score,
"l" -> s.clock,
"z" -> s.crazyData,
"n" -> (if (s.ply < Node.MAX_PLIES) s.children else Node.emptyChildren),
"fv" -> w.boolO(s.forceVariation)
)
def readNode(doc: Bdoc, id: UciCharPair): Node = {
val r = new Reader(doc)
Node(
id = id,
ply = r int "p",
move = WithSan(r.get[Uci]("u"), r.str("s")),
fen = r.get[FEN]("f"),
check = r boolD "c",
shapes = r.getO[Shapes]("h") | Shapes.empty,
comments = r.getO[Comments]("co") | Comments.empty,
gamebook = r.getO[Gamebook]("ga"),
glyphs = r.getO[Glyphs]("g") | Glyphs.empty,
score = r.getO[Score]("e"),
crazyData = r.getO[Crazyhouse.Data]("z"),
clock = r.getO[Centis]("l"),
children = Node.emptyChildren,
forceVariation = r boolD "fv"
)
}
def writeNode(s: Node) = {
val w = new Writer
$doc(
"p" -> s.ply,
"u" -> s.move.uci,
"s" -> s.move.san,
"f" -> s.fen,
"c" -> w.boolO(s.check),
"h" -> s.shapes.value.nonEmpty.option(s.shapes),
"co" -> s.comments.value.nonEmpty.option(s.comments),
"ga" -> s.gamebook,
"g" -> s.glyphs.nonEmpty,
"e" -> s.score,
"l" -> s.clock,
"z" -> s.crazyData,
"fv" -> w.boolO(s.forceVariation)
)
}
import Node.Root
implicit private[study] lazy val NodeRootBSONHandler: BSON[Root] = new BSON[Root] {
def reads(r: Reader) =
def reads(fullReader: Reader) = {
val rootNode = fullReader.doc.getAsOpt[Bdoc]("") err "Missing root"
val r = new Reader(rootNode)
Root(
ply = r int "p",
fen = r.get[FEN]("f"),
@ -229,22 +213,22 @@ object BSONHandlers {
score = r.getO[Score]("e"),
clock = r.getO[Centis]("l"),
crazyData = r.getO[Crazyhouse.Data]("z"),
children = r.get[Node.Children]("n")
)
def writes(w: Writer, s: Root) =
$doc(
"p" -> s.ply,
"f" -> s.fen,
"c" -> w.boolO(s.check),
"h" -> s.shapes.value.nonEmpty.option(s.shapes),
"co" -> s.comments.value.nonEmpty.option(s.comments),
"ga" -> s.gamebook,
"g" -> s.glyphs.nonEmpty,
"e" -> s.score,
"l" -> s.clock,
"z" -> s.crazyData,
"n" -> s.children
children = StudyFlatTree.rootChildren(fullReader.doc)
)
}
def writes(w: Writer, s: Root) = ???
// $doc(
// "p" -> s.ply,
// "f" -> s.fen,
// "c" -> w.boolO(s.check),
// "h" -> s.shapes.value.nonEmpty.option(s.shapes),
// "co" -> s.comments.value.nonEmpty.option(s.comments),
// "ga" -> s.gamebook,
// "g" -> s.glyphs.nonEmpty,
// "e" -> s.score,
// "l" -> s.clock,
// "z" -> s.crazyData
// )
}
implicit val PathBSONHandler = BSONStringHandler.as[Path](Path.apply, _.toString)

View File

@ -126,7 +126,9 @@ final class ChapterRepo(val coll: Coll)(implicit
def setScore(score: Option[lila.tree.Eval.Score]) = setNodeValue("e", score) _
def setChildren(children: Node.Children) = setNodeValue("n", children.some) _
def setChildren(children: Node.Children)(chapter: Chapter, path: Path): Funit =
??? // TODO
// setNodeValue("n", children.some) _
private def setNodeValue[A: BSONWriter](
field: String,
@ -152,14 +154,15 @@ final class ChapterRepo(val coll: Coll)(implicit
}
private[study] def setChild(chapter: Chapter, path: Path, child: Node): Funit =
pathToField(chapter, path, "n") ?? { parentChildrenPath =>
coll.update.one(
$id(chapter.id) ++ $doc(s"$parentChildrenPath.i" -> child.id),
$set(s"$parentChildrenPath.$$" -> child)
) flatMap { res =>
(res.n == 0) ?? coll.update.one($id(chapter.id), $push(parentChildrenPath -> child)).void
}
}
??? // TODO
// pathToField(chapter, path, "n") ?? { parentChildrenPath =>
// coll.update.one(
// $id(chapter.id) ++ $doc(s"$parentChildrenPath.i" -> child.id),
// $set(s"$parentChildrenPath.$$" -> child)
// ) flatMap { res =>
// (res.n == 0) ?? coll.update.one($id(chapter.id), $push(parentChildrenPath -> child)).void
// }
// }
private[study] def idNamesByStudyIds(
studyIds: Seq[Study.Id],

View File

@ -46,7 +46,7 @@ final class Env(
private val socket = wire[StudySocket]
lazy val studyRepo = new StudyRepo(db(CollName("study")))
lazy val chapterRepo = new ChapterRepo(db(CollName("study_chapter")))
lazy val chapterRepo = new ChapterRepo(db(CollName("study_chapter_flat")))
private lazy val topicRepo = new StudyTopicRepo(db(CollName("study_topic")))
private lazy val userTopicRepo = new StudyUserTopicRepo(db(CollName("study_user_topic")))

View File

@ -0,0 +1,43 @@
package lila.study
import BSONHandlers._
import Node.Children
import lila.db.dsl._
import lila.tree.Eval
import lila.tree.Eval.Score
private object StudyFlatTree {
private case class FlatNode(path: Path, data: Bdoc) {
val depth = path.ids.size
def toNodeWithChildren(children: Option[Children]): Node = {
readNode(data, path.ids.last)
}.copy(children = children | Node.emptyChildren)
}
def rootChildren(flatTree: Bdoc): Children = traverse {
flatTree.elements.toList
.collect {
case el if el.name.nonEmpty => FlatNode(Path(el.name), el.value.asOpt[Bdoc].get)
}
.sortBy(-_.depth)
}
private def traverse(children: List[FlatNode]): Children =
children
.foldLeft(Map.empty[Path, Children]) { case (allChildren, flat) =>
update(allChildren, flat)
}
.get(Path.root) | Node.emptyChildren
// assumes that node has a greater depth than roots (sort beforehand)
private def update(roots: Map[Path, Children], flat: FlatNode): Map[Path, Children] = {
val node = flat.toNodeWithChildren(roots get flat.path)
roots.removed(flat.path).updatedWith(flat.path.init) {
case None => Children(Vector(node)).some
case Some(siblings) => siblings.addNode(node).some
}
}
}