study search engine WIP

proxy-selector
Thibault Duplessis 2016-07-25 20:14:43 +02:00
parent b11041bd54
commit c6f187392d
11 changed files with 108 additions and 36 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) =>

View File

@ -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 = {

View File

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

View File

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

View File

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