Optimize global search
parent
d9d0656ef4
commit
64752da948
@ -1,40 +1,46 @@
|
||||
package org.koitharu.kotatsu.search.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import android.annotation.SuppressLint
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.utils.ext.levenshteinDistance
|
||||
|
||||
class MangaSearchRepository(private val settings: AppSettings) {
|
||||
|
||||
fun globalSearch(query: String, batchSize: Int = 4): Flow<List<Manga>> = flow {
|
||||
val sources = MangaProviderFactory.getSources(settings, includeHidden = false)
|
||||
val lists = EnumMap<MangaSource, List<Manga>>(MangaSource::class.java)
|
||||
var i = 0
|
||||
while (true) {
|
||||
var isEmitted = false
|
||||
for (source in sources) {
|
||||
val list = lists.getOrPut(source) {
|
||||
try {
|
||||
source.repository.getList(0, query, SortOrder.POPULARITY)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
emptyList<Manga>()
|
||||
fun globalSearch(query: String, concurrency: Int = DEFAULT_CONCURRENCY): Flow<Manga> =
|
||||
MangaProviderFactory.getSources(settings, includeHidden = false).asFlow()
|
||||
.flatMapMerge(concurrency) { source ->
|
||||
runCatching {
|
||||
source.repository.getList(0, query, SortOrder.POPULARITY)
|
||||
}.getOrElse {
|
||||
emptyList()
|
||||
}.asFlow()
|
||||
}.filter {
|
||||
match(it, query)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
private val REGEX_SPACE = Regex("\\s+")
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
fun match(manga: Manga, query: String): Boolean {
|
||||
val words = HashSet<String>()
|
||||
words += manga.title.toLowerCase().split(REGEX_SPACE)
|
||||
words += manga.altTitle?.toLowerCase()?.split(REGEX_SPACE).orEmpty()
|
||||
val words2 = query.toLowerCase().split(REGEX_SPACE).toSet()
|
||||
for (w in words) {
|
||||
for (w2 in words2) {
|
||||
val diff = w.levenshteinDistance(w2) / ((w.length + w2.length) / 2f)
|
||||
if (diff < 0.5) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (i < list.size) {
|
||||
emit(list.subList(i, (i + batchSize).coerceAtMost(list.lastIndex)))
|
||||
isEmitted = true
|
||||
}
|
||||
}
|
||||
i += batchSize
|
||||
if (!isEmitted) {
|
||||
return@flow
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue