Optimize global search
parent
d9d0656ef4
commit
64752da948
@ -1,40 +1,46 @@
|
|||||||
package org.koitharu.kotatsu.search.domain
|
package org.koitharu.kotatsu.search.domain
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import android.annotation.SuppressLint
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.*
|
||||||
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
|
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
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.model.SortOrder
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import java.util.*
|
import org.koitharu.kotatsu.utils.ext.levenshteinDistance
|
||||||
|
|
||||||
class MangaSearchRepository(private val settings: AppSettings) {
|
class MangaSearchRepository(private val settings: AppSettings) {
|
||||||
|
|
||||||
fun globalSearch(query: String, batchSize: Int = 4): Flow<List<Manga>> = flow {
|
fun globalSearch(query: String, concurrency: Int = DEFAULT_CONCURRENCY): Flow<Manga> =
|
||||||
val sources = MangaProviderFactory.getSources(settings, includeHidden = false)
|
MangaProviderFactory.getSources(settings, includeHidden = false).asFlow()
|
||||||
val lists = EnumMap<MangaSource, List<Manga>>(MangaSource::class.java)
|
.flatMapMerge(concurrency) { source ->
|
||||||
var i = 0
|
runCatching {
|
||||||
while (true) {
|
source.repository.getList(0, query, SortOrder.POPULARITY)
|
||||||
var isEmitted = false
|
}.getOrElse {
|
||||||
for (source in sources) {
|
emptyList()
|
||||||
val list = lists.getOrPut(source) {
|
}.asFlow()
|
||||||
try {
|
}.filter {
|
||||||
source.repository.getList(0, query, SortOrder.POPULARITY)
|
match(it, query)
|
||||||
} catch (e: Throwable) {
|
}
|
||||||
e.printStackTrace()
|
|
||||||
emptyList<Manga>()
|
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