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=76c2341416f371022678a04ac2d15ce7b2c6749c;hp=577ea9021d73cdbfd74f253842ad2bf1cf709e1f;hb=42d2da43d7a3dd0645e17c4db39252faa20a92d7;hpb=a9c9094ae93da0d112df6bb1a1d601cde4abe4ff 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 577ea90..76c2341 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt @@ -9,6 +9,7 @@ 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 @@ -21,13 +22,17 @@ import net.pterodactylus.util.template.TemplateContext import net.pterodactylus.util.text.StringEscaper import net.pterodactylus.util.text.TextException import java.util.concurrent.TimeUnit.MINUTES +import javax.inject.Inject /** * This page lets the user search for posts and replies that contain certain * words. */ -class SearchPage @JvmOverloads constructor(template: Template, webInterface: WebInterface, ticker: Ticker = Ticker.systemTicker()): - SoneTemplatePage("search.html", template, "Page.Search.Title", webInterface, false) { +class SearchPage(template: Template, webInterface: WebInterface, ticker: Ticker = Ticker.systemTicker()) : + SoneTemplatePage("search.html", webInterface, template, "Page.Search.Title") { + + @Inject constructor(template: Template, webInterface: WebInterface) : + this(template, webInterface, Ticker.systemTicker()) private val cache: Cache, Pagination> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5, MINUTES).build() @@ -53,14 +58,15 @@ class SearchPage @JvmOverloads constructor(template: Template, webInterface: Web } } + 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.") @@ -78,34 +84,33 @@ 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 Iterable.indicesFor(text: String, predicate: (Phrase) -> Boolean) = + filter(predicate).map(Phrase::phrase).map(String::toLowerCase).flatMap { text.findAll(it) } private fun score(text: String, phrases: Iterable): Double { val requiredPhrases = phrases.count { it.required } - val requiredHits = phrases.filter(Phrase::required) - .map(Phrase::phrase) - .flatMap { text.findAll(it) } + val requiredHits = phrases.indicesFor(text, Phrase::required) .map { Math.pow(1 - it / text.length.toDouble(), 2.0) } .sum() - val optionalHits = phrases.filter(Phrase::optional) - .map(Phrase::phrase) - .flatMap { text.findAll(it) } + val optionalHits = phrases.indicesFor(text, Phrase::optional) .map { Math.pow(1 - it / text.length.toDouble(), 2.0) } .sum() - val forbiddenHits = phrases.filter(Phrase::forbidden) - .map(Phrase::phrase) - .map { text.findAll(it).size } - .sum() + val forbiddenHits = phrases.indicesFor(text, Phrase::forbidden) + .count() return requiredHits * 3 + optionalHits + (requiredHits - requiredPhrases) * 5 - (forbiddenHits * 2) } @@ -118,7 +123,6 @@ class SearchPage @JvmOverloads constructor(template: Template, webInterface: Web private fun String.parse() = StringEscaper.parseLine(this) - .map(String::toLowerCase) .map { when { it == "+" || it == "-" -> Phrase(it, OPTIONAL)