study search engine WIP
parent
b11041bd54
commit
c6f187392d
|
@ -17,6 +17,16 @@ object Study extends LilaController {
|
|||
|
||||
private def env = Env.study
|
||||
|
||||
def search(text: String, page: Int) = OpenBody { implicit ctx =>
|
||||
if (text.trim.isEmpty)
|
||||
env.pager.all(ctx.me, Order.default, page) map { pag =>
|
||||
Ok(html.study.all(pag, Order.default))
|
||||
}
|
||||
else Env.studySearch(ctx.me)(text, page) map { pag =>
|
||||
Ok(html.study.search(pag, text))
|
||||
}
|
||||
}
|
||||
|
||||
def allDefault(page: Int) = all(Order.Hot.key, page)
|
||||
|
||||
def all(o: String, page: Int) = Open { implicit ctx =>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
@(me: User, active: String, order: lila.study.Order)(implicit ctx: Context)
|
||||
|
||||
<a class="@active.active("mine")" href="@routes.Study.mine(order.key)">My studies</a>
|
||||
<a class="@active.active("mineMember")" href="@routes.Study.mineMember(order.key)">Studies I contribute to</a>
|
||||
<a class="@active.active("minePublic")" href="@routes.Study.minePublic(order.key)">My public studies</a>
|
||||
<a class="@active.active("minePrivate")" href="@routes.Study.minePrivate(order.key)">My private studies</a>
|
||||
<a class="@active.active("mineLikes")" href="@routes.Study.mineLikes(order.key)">Favourite studies</a>
|
|
@ -10,12 +10,8 @@
|
|||
|
||||
@menu = {
|
||||
<a class="@active.active("all")" href="@routes.Study.all(order.key)">All studies</a>
|
||||
@if(ctx.isAuth) {
|
||||
<a class="@active.active("mine")" href="@routes.Study.mine(order.key)">My studies</a>
|
||||
<a class="@active.active("mineMember")" href="@routes.Study.mineMember(order.key)">Studies I contribute to</a>
|
||||
<a class="@active.active("minePublic")" href="@routes.Study.minePublic(order.key)">My public studies</a>
|
||||
<a class="@active.active("minePrivate")" href="@routes.Study.minePrivate(order.key)">My private studies</a>
|
||||
<a class="@active.active("mineLikes")" href="@routes.Study.mineLikes(order.key)">Favourite studies</a>
|
||||
@ctx.me.map { me =>
|
||||
@authLinks(me, active, order)
|
||||
}
|
||||
<a class="text" data-icon="" href="//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way">What are studies?</a>
|
||||
}
|
||||
|
@ -29,6 +25,8 @@ withLangAnnotations = false) {
|
|||
<div class="content_box no_padding studies">
|
||||
<div class="top">
|
||||
<h1>@titleHtml</h1>
|
||||
<form action=
|
||||
<input name="q" placeholder="@trans.search()" />
|
||||
@orderChoice(o => url(o.key), order, if (active == "all") lila.study.Order.allButOldest else lila.study.Order.all)
|
||||
@newForm()
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
@(pag: Paginator[lila.study.Study.WithChaptersAndLiked], text: String)(implicit ctx: Context)
|
||||
|
||||
@import lila.study.Order
|
||||
|
||||
@moreCss = {
|
||||
@cssTag("studyList.css")
|
||||
}
|
||||
|
||||
@moreJs = {
|
||||
@jsTag("vendor/jquery.infinitescroll.min.js")
|
||||
}
|
||||
|
||||
@menu = {
|
||||
<a href="@routes.Study.all(Order.default.key)">All studies</a>
|
||||
@ctx.me.map { me =>
|
||||
@authLinks(me, "search", Order.default)
|
||||
}
|
||||
<a class="text" data-icon="" href="//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way">What are studies?</a>
|
||||
}
|
||||
|
||||
@base.layout(
|
||||
title = text,
|
||||
menu = menu.some,
|
||||
moreCss = moreCss,
|
||||
moreJs = moreJs,
|
||||
withLangAnnotations = false) {
|
||||
<div class="content_box no_padding studies">
|
||||
<div class="top">
|
||||
<form action="@routes.Study.search()" method="get">
|
||||
<input name="q" placeholder="@trans.search()" />
|
||||
</form>
|
||||
@newForm()
|
||||
</div>
|
||||
@list(pag, routes.Study.search(text))
|
||||
</div>
|
||||
}
|
|
@ -128,6 +128,7 @@ GET /study/private/:order controllers.Study.minePrivate(order: Stri
|
|||
GET /study/likes/:order controllers.Study.mineLikes(order: String, page: Int ?= 1)
|
||||
GET /study/by/:username controllers.Study.byOwnerDefault(username: String, page: Int ?= 1)
|
||||
GET /study/by/:username/:order controllers.Study.byOwner(username: String, order: String, page: Int ?= 1)
|
||||
GET /study/search controllers.Study.search(q: String ?= "", page: Int ?= 1)
|
||||
GET /study/$id<\w{8}> controllers.Study.show(id: String)
|
||||
POST /study controllers.Study.create
|
||||
GET /study/$id<\w{8}>/socket/v:apiVersion controllers.Study.websocket(id: String, apiVersion: Int)
|
||||
|
|
|
@ -89,6 +89,8 @@ object Study {
|
|||
|
||||
case class WithChapters(study: Study, chapters: Seq[String])
|
||||
|
||||
case class WithActualChapters(study: Study, chapters: Seq[Chapter])
|
||||
|
||||
case class WithChaptersAndLiked(study: Study, chapters: Seq[String], liked: Boolean)
|
||||
|
||||
type ID = String
|
||||
|
|
|
@ -65,14 +65,14 @@ final class StudyPager(
|
|||
maxPerPage = maxPerPage.value)
|
||||
}
|
||||
|
||||
private def withChapters(studies: Seq[Study]): Fu[Seq[Study.WithChapters]] =
|
||||
def withChapters(studies: Seq[Study]): Fu[Seq[Study.WithChapters]] =
|
||||
chapterRepo namesByStudyIds studies.map(_.id) map { chapters =>
|
||||
studies.map { study =>
|
||||
Study.WithChapters(study, ~(chapters get study.id))
|
||||
}
|
||||
}
|
||||
|
||||
private def withLiking(me: Option[User])(studies: Seq[Study.WithChapters]): Fu[Seq[Study.WithChaptersAndLiked]] =
|
||||
def withLiking(me: Option[User])(studies: Seq[Study.WithChapters]): Fu[Seq[Study.WithChaptersAndLiked]] =
|
||||
me.?? { u => studyRepo.filterLiked(u, studies.map(_.study.id)) } map { liked =>
|
||||
studies.map {
|
||||
case Study.WithChapters(study, chapters) =>
|
||||
|
|
|
@ -3,8 +3,12 @@ package lila.studySearch
|
|||
import akka.actor._
|
||||
import com.typesafe.config.Config
|
||||
|
||||
import lila.common.paginator._
|
||||
import lila.db.dsl._
|
||||
import lila.search._
|
||||
import lila.search.PaginatorBuilder
|
||||
import lila.study.Study
|
||||
import lila.user.User
|
||||
|
||||
final class Env(
|
||||
config: Config,
|
||||
|
@ -20,7 +24,14 @@ final class Env(
|
|||
|
||||
val api = new StudySearchApi(client, studyEnv.studyRepo, studyEnv.chapterRepo)
|
||||
|
||||
def apply(text: String, page: Int) = paginatorBuilder(Query(text), page)
|
||||
def apply(me: Option[User])(text: String, page: Int) =
|
||||
Paginator[Study.WithChaptersAndLiked](
|
||||
adapter = new AdapterLike[Study] {
|
||||
def nbResults = api count Query(text)
|
||||
def slice(offset: Int, length: Int) = api.search(Query(text), From(offset), Size(length))
|
||||
} mapFutureList studyEnv.pager.withChapters mapFutureList studyEnv.pager.withLiking(me),
|
||||
currentPage = page,
|
||||
maxPerPage = MaxPerPage)
|
||||
|
||||
def cli = new lila.common.Cli {
|
||||
def process = {
|
||||
|
@ -28,11 +39,6 @@ final class Env(
|
|||
}
|
||||
}
|
||||
|
||||
private lazy val paginatorBuilder =
|
||||
new lila.search.PaginatorBuilder[lila.study.Study, Query](
|
||||
searchApi = api,
|
||||
maxPerPage = MaxPerPage)
|
||||
|
||||
// system.actorOf(Props(new Actor {
|
||||
// import lila.study.actorApi._
|
||||
// def receive = {
|
||||
|
|
|
@ -5,8 +5,13 @@ private[studySearch] object Fields {
|
|||
val owner = "owner"
|
||||
val members = "members"
|
||||
val chapters = "chapters"
|
||||
val createdAt = "createdAt"
|
||||
val updatedAt = "updatedAt"
|
||||
val rank = "rank"
|
||||
// val createdAt = "createdAt"
|
||||
// val updatedAt = "updatedAt"
|
||||
// val rank = "rank"
|
||||
val likes = "likes"
|
||||
val public = "public"
|
||||
|
||||
object chapter {
|
||||
val name = "name"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package lila.studySearch
|
||||
|
||||
import lila.search._
|
||||
import lila.study.{Study,StudyRepo,ChapterRepo}
|
||||
import lila.study.{ Study, Chapter, StudyRepo, ChapterRepo }
|
||||
|
||||
import play.api.libs.json._
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.json._
|
||||
|
||||
final class StudySearchApi(
|
||||
client: ESClient,
|
||||
studyRepo: StudyRepo,
|
||||
chapterRepo: ChapterRepo) extends SearchReadApi[Study, Query] {
|
||||
client: ESClient,
|
||||
studyRepo: StudyRepo,
|
||||
chapterRepo: ChapterRepo) extends SearchReadApi[Study, Query] {
|
||||
|
||||
def search(query: Query, from: From, size: Size) =
|
||||
client.search(query, from, size) flatMap { res =>
|
||||
|
@ -18,27 +18,34 @@ chapterRepo: ChapterRepo) extends SearchReadApi[Study, Query] {
|
|||
|
||||
def count(query: Query) = client.count(query) map (_.count)
|
||||
|
||||
def store(study: Study) = client.store(Id(study.id), toDoc(study))
|
||||
def store(study: Study) = getChapters(study) flatMap { s =>
|
||||
client.store(Id(s.study.id), toDoc(s))
|
||||
}
|
||||
|
||||
private def toDoc(study: Study) = Json.obj(
|
||||
Fields.name -> study.name,
|
||||
Fields.owner -> study.ownerId,
|
||||
Fields.members -> study.members.ids,
|
||||
// Fields.chapters -> study
|
||||
Fields.createdAt -> study.createdAt)
|
||||
private def toDoc(s: Study.WithActualChapters) = Json.obj(
|
||||
Fields.name -> s.study.name,
|
||||
Fields.owner -> s.study.ownerId,
|
||||
Fields.members -> s.study.members.ids,
|
||||
Fields.chapters -> JsArray(s.chapters.map(chapterToDoc)),
|
||||
// Fields.createdAt -> study.createdAt)
|
||||
// Fields.updatedAt -> study.updatedAt,
|
||||
// Fields.rank -> study.rank,
|
||||
// Fields.like -> study.like)
|
||||
Fields.public -> s.study.isPublic,
|
||||
Fields.likes -> s.study.likes.value)
|
||||
|
||||
private def chapterToDoc(c: Chapter) = Json.obj(
|
||||
Fields.chapter.name -> c.name)
|
||||
|
||||
private def getChapters(s: Study): Fu[Study.WithActualChapters] =
|
||||
chapterRepo.orderedByStudy(s.id) map { Study.WithActualChapters(s, _) }
|
||||
|
||||
def reset = client match {
|
||||
case c: ESClientHttp => c.putMapping >> {
|
||||
lila.log("studySearch").info(s"Index to ${c.index.name}")
|
||||
import lila.db.dsl._
|
||||
studyRepo.cursor($doc("enabled" -> true))
|
||||
.enumerateBulks(Int.MaxValue) |>>>
|
||||
Iteratee.foldM[Iterator[Study], Unit](()) {
|
||||
case (_, studies) =>
|
||||
c.storeBulk(studies.toList map (t => Id(t.id) -> toDoc(t)))
|
||||
studyRepo.cursor($empty).enumerate() |>>>
|
||||
Iteratee.foldM[Study, Unit](()) {
|
||||
case (_, study) => store(study)
|
||||
}
|
||||
}
|
||||
case _ => funit
|
||||
|
|
|
@ -62,7 +62,7 @@ case class Tournament(
|
|||
|
||||
def isRecentlyFinished = isFinished && (nowSeconds - finishesAt.getSeconds) < 30 * 60
|
||||
|
||||
def isRecentlyStarted = isStarted && (nowSeconds - startsAt.getSeconds) < 60
|
||||
def isRecentlyStarted = isStarted && (nowSeconds - startsAt.getSeconds) < 3
|
||||
|
||||
def duration = new Duration(minutes * 60 * 1000)
|
||||
|
||||
|
|
Loading…
Reference in New Issue