From 2c0c2440600d009553ea7668907a336cb64e0e6e Mon Sep 17 00:00:00 2001 From: Eugene Date: Sun, 9 Aug 2026 17:57:53 +0300 Subject: [PATCH] update filter alghoritm --- .../vbox/backend/data/FuzzySearchFilter.kt | 45 +++++-------------- .../vbox/backend/data/http/SessionVideoApi.kt | 2 +- 2 files changed, 13 insertions(+), 34 deletions(-) diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/FuzzySearchFilter.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/FuzzySearchFilter.kt index f8c8764..f057ee3 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/FuzzySearchFilter.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/FuzzySearchFilter.kt @@ -2,7 +2,6 @@ package ru.shadowsparky.vbox.backend.data import org.koin.core.annotation.Single import ru.shadowsparky.vbox.shared.domain.model.VideoItem -import kotlin.math.max @Single class FuzzyMovieSearchFilter { @@ -15,20 +14,9 @@ class FuzzyMovieSearchFilter { if (titleNormalized.contains(query)) { return@map Pair(movie, 1.0) } - val queryWords = query.split(" ") - val titleWords = titleNormalized.split(" ") - var maxScore = 0.0 - for (qWord in queryWords) { - for (tWord in titleWords) { - val score = tWord.fuzzySimilarity(qWord) - if (score > maxScore) maxScore = score - } - } - Pair(movie, maxScore) - } - .filter { it.second >= THRESHOLD } - .sortedByDescending { it.second } - .map { it.first } + val score = titleNormalized.fuzzySimilarity(query) + Pair(movie, score) + }.filter { it.second >= THRESHOLD }.sortedByDescending { it.second }.map { it.first } } private fun String.normalize(): String { @@ -61,27 +49,18 @@ class FuzzyMovieSearchFilter { private fun String.fuzzySimilarity(other: String): Double { if (this == other) return 1.0 - if (this.isEmpty() || other.isEmpty()) return 0.0 - val s1 = this - val dp = IntArray(other.length + 1) { it } - for (i in 1..s1.length) { - var prev = dp[0] - dp[0] = i - for (j in 1..other.length) { - val temp = dp[j] - if (s1[i - 1] == other[j - 1]) { - dp[j] = prev - } else { - dp[j] = minOf(dp[j - 1], dp[j], prev) + 1 - } - prev = temp - } + if (this.isBlank() || other.isBlank()) return 0.0 + val s1Pairs = this.windowed(2) + val s2Pairs = other.windowed(2).toMutableList() + if (s1Pairs.isEmpty() || s2Pairs.isEmpty()) return 0.0 + var matches = 0 + for (pair in s1Pairs) { + if (s2Pairs.remove(pair)) { matches++ } } - val maxLen = max(s1.length, other.length) - return (maxLen - dp[other.length]).toDouble() / maxLen + return (2.0 * matches) / (s1Pairs.size + other.windowed(2).size) } private companion object { - const val THRESHOLD: Double = 0.5 + const val THRESHOLD: Double = 0.45 } } diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt index d7022f8..c5f1655 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt @@ -26,7 +26,7 @@ class SessionVideoApi( private val linksMutex = Mutex() override suspend fun fetchNewVideos(query: String?): VideosResponse { - val cacheKey = query.orEmpty() + val cacheKey = query?.trim().orEmpty() videosCache.getValid(cacheKey)?.let { return it } return videosMutex.withLock { videosCache.getValid(cacheKey) ?: run {