X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fkotlin%2Fnet%2Fpterodactylus%2Fsone%2Fweb%2Fpages%2FSearchPage.kt;h=7cddc3588983a7e9f4f7f63c10323783149f2893;hp=5f402387ecd9bf19d0d842b8770a119f943932e6;hb=d63b7445567b65ffdbd50fa8f7ffbfea1304dff9;hpb=8e313509a42a8c638fcac018dd73dd975bf9cb68 diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt index 5f40238..7cddc35 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt @@ -3,11 +3,13 @@ package net.pterodactylus.sone.web.pages import com.google.common.base.Ticker import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder +import freenet.support.Logger import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.data.PostReply import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.utils.Pagination import net.pterodactylus.sone.utils.emptyToNull +import net.pterodactylus.sone.utils.memoize import net.pterodactylus.sone.utils.paginate import net.pterodactylus.sone.utils.parameters import net.pterodactylus.sone.web.WebInterface @@ -26,11 +28,12 @@ import java.util.concurrent.TimeUnit.MINUTES * words. */ class SearchPage @JvmOverloads constructor(template: Template, webInterface: WebInterface, ticker: Ticker = Ticker.systemTicker()): - SoneTemplatePage("search.html", template, "Page.Search.Title", webInterface, false) { + SoneTemplatePage("search.html", webInterface, template, "Page.Search.Title") { private val cache: Cache, Pagination> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5, MINUTES).build() override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) { + val startTime = System.currentTimeMillis() val phrases = try { freenetRequest.parameters["query"].emptyToNull?.parse() } catch (te: TextException) { @@ -43,24 +46,26 @@ class SearchPage @JvmOverloads constructor(template: Template, webInterface: Web 1 -> phrases.first().phrase.also { word -> when { word.removePrefix("sone://").let(webInterface.core::getSone) != null -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}") - word.removePrefix("post://").let(webInterface.core::getPost).isPresent -> redirect("viewPost.html?post=${word.removePrefix("post://")}") - word.removePrefix("reply://").let(webInterface.core::getPostReply).isPresent -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(webInterface.core::getPostReply).get().postId}") + word.removePrefix("post://").let(webInterface.core::getPost) != null -> redirect("viewPost.html?post=${word.removePrefix("post://")}") + word.removePrefix("reply://").let(webInterface.core::getPostReply) != null -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(webInterface.core::getPostReply)?.postId}") word.removePrefix("album://").let(webInterface.core::getAlbum) != null -> redirect("imageBrowser.html?album=${word.removePrefix("album://")}") word.removePrefix("image://").let { webInterface.core.getImage(it, false) } != null -> redirect("imageBrowser.html?image=${word.removePrefix("image://")}") } } } + val soneNameCache = { sone: Sone -> sone.names() }.memoize() val sonePagination = webInterface.core.sones - .scoreAndPaginate(phrases) { it.allText() } + .scoreAndPaginate(phrases) { it.allText(soneNameCache) } .apply { page = freenetRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 } val postPagination = cache.get(phrases) { webInterface.core.sones .flatMap(Sone::getPosts) .filter { Post.FUTURE_POSTS_FILTER.apply(it) } - .scoreAndPaginate(phrases) { it.allText() } + .scoreAndPaginate(phrases) { it.allText(soneNameCache) } }.apply { page = freenetRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 } + Logger.normal(SearchPage::class.java, "Finished search for “${freenetRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.") templateContext["sonePagination"] = sonePagination templateContext["soneHits"] = sonePagination.items templateContext["postPagination"] = postPagination @@ -75,17 +80,19 @@ class SearchPage @JvmOverloads constructor(template: Template, webInterface: Web .paginate(webInterface.core.preferences.postsPerPage) private fun Sone.names() = - listOf(name, profile.firstName, profile.middleName, profile.lastName) - .filterNotNull() - .joinToString("") + with(profile) { + listOf(name, firstName, middleName, lastName) + .filterNotNull() + .joinToString("") + } - private fun Sone.allText() = - (names() + profile.fields.map { "${it.name} ${it.value}" }.joinToString(" ", " ")).toLowerCase() + private fun Sone.allText(soneNameCache: (Sone) -> String) = + (soneNameCache(this) + profile.fields.map { "${it.name} ${it.value}" }.joinToString(" ", " ")).toLowerCase() - private fun Post.allText() = - (text + recipient.orNull()?.let { " ${it.names()}" } + webInterface.core.getReplies(id) + private fun Post.allText(soneNameCache: (Sone) -> String) = + (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + webInterface.core.getReplies(id) .filter { PostReply.FUTURE_REPLY_FILTER.apply(it) } - .map { "${it.sone.names()} ${it.text}" }.joinToString(" ", " ")).toLowerCase() + .map { "${soneNameCache(it.sone)} ${it.text}" }.joinToString(" ", " ")).toLowerCase() private fun score(text: String, phrases: Iterable): Double { val requiredPhrases = phrases.count { it.required } @@ -106,15 +113,12 @@ class SearchPage @JvmOverloads constructor(template: Template, webInterface: Web return requiredHits * 3 + optionalHits + (requiredHits - requiredPhrases) * 5 - (forbiddenHits * 2) } - private fun String.findAll(needle: String): List { - var nextIndex = indexOf(needle) - val positions = mutableListOf() - while (nextIndex != -1) { - positions += nextIndex - nextIndex = indexOf(needle, nextIndex + 1) - } - return positions - } + private fun String.findAll(needle: String) = + generateSequence(indexOf(needle).takeIf { it > -1 }) { lastPosition -> + lastPosition + .let { indexOf(needle, it + 1) } + .takeIf { it > -1 } + }.toList() private fun String.parse() = StringEscaper.parseLine(this)