You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
101 lines
3.1 KiB
Kotlin
101 lines
3.1 KiB
Kotlin
package moe.odango.index.http.graphql
|
|
|
|
import io.inbot.eskotlinwrapper.IndexRepository
|
|
import io.inbot.eskotlinwrapper.dsl.*
|
|
import moe.odango.index.di
|
|
import moe.odango.index.entity.Title
|
|
import moe.odango.index.es.dto.AnimeDTO
|
|
import moe.odango.index.http.graphql.dto.AnimeItem
|
|
import moe.odango.index.http.graphql.dto.AnimeTitleItem
|
|
import moe.odango.index.http.graphql.dto.AutoCompleteItem
|
|
import moe.odango.index.utils.ScoreMode
|
|
import moe.odango.index.utils.nested
|
|
import org.elasticsearch.action.search.dsl
|
|
import org.elasticsearch.action.search.source
|
|
import org.elasticsearch.client.RestHighLevelClient
|
|
import org.elasticsearch.common.unit.Fuzziness
|
|
import org.elasticsearch.search.suggest.SuggestBuilder
|
|
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder
|
|
import org.kodein.di.instance
|
|
|
|
class AnimeService {
|
|
private val es by di.instance<IndexRepository<AnimeDTO>>()
|
|
private val client by di.instance<RestHighLevelClient>()
|
|
|
|
fun autocomplete(title: String): List<AutoCompleteItem> {
|
|
return es.search {
|
|
source {
|
|
suggest(
|
|
SuggestBuilder().addSuggestion(
|
|
"title",
|
|
CompletionSuggestionBuilder("title.name").prefix(title, Fuzziness.AUTO)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
.hits
|
|
.mapNotNull { (s, item) ->
|
|
AutoCompleteItem(
|
|
s.innerHits["title"]?.hits?.get(0)?.sourceAsMap?.get("name")?.toString() ?: return@mapNotNull null,
|
|
item?.id.toString() ?: return@mapNotNull null
|
|
)
|
|
}
|
|
.toList()
|
|
}
|
|
|
|
fun search(
|
|
title: String? = null,
|
|
description: String? = null,
|
|
genre: List<String>? = null
|
|
): List<AnimeItem> {
|
|
val items = mutableListOf<ESQuery>()
|
|
|
|
title?.let {
|
|
items.add(
|
|
nested("title") {
|
|
query = MatchQuery("title.name", it)
|
|
scoreMode = ScoreMode.max
|
|
}
|
|
)
|
|
}
|
|
|
|
genre?.let {
|
|
items.add(
|
|
bool {
|
|
must(*it.toSet().map { genre -> TermQuery("genre", genre) }.toTypedArray())
|
|
}
|
|
)
|
|
}
|
|
|
|
description?.let {
|
|
items.add(
|
|
nested("description") {
|
|
query = MatchQuery("description.name", it)
|
|
scoreMode = ScoreMode.max
|
|
}
|
|
)
|
|
}
|
|
|
|
return es.search {
|
|
dsl {
|
|
query = disMax {
|
|
queries(*items.toTypedArray())
|
|
}
|
|
}
|
|
}
|
|
.hits
|
|
.mapNotNull { (_, dto) ->
|
|
dto?.let {
|
|
AnimeItem(
|
|
it.id.toString(),
|
|
it.title.map { dto ->
|
|
AnimeTitleItem(dto.name, dto.language, dto.type.let(Title.TitleType::valueOf), dto.source)
|
|
},
|
|
it.genre.toSet().toList()
|
|
)
|
|
}
|
|
}
|
|
.toList()
|
|
}
|
|
}
|