improvement: More stable search that prefers title

This commit is contained in:
Artem Chepurnyi 2024-09-29 12:43:52 +03:00
parent 390c999f65
commit b533c6a876
2 changed files with 39 additions and 21 deletions

View File

@ -929,10 +929,10 @@ fun vaultListScreenState(
}, },
) )
val indexed = mutableListOf<SearchToken>() val indexed = mutableListOf<SearchToken>()
indexed += SearchToken( //indexed += SearchToken(
priority = 2f, // priority = 2f,
value = secret.name, // value = secret.name,
) //)
if (secret.login != null) { if (secret.login != null) {
// Make username searchable // Make username searchable
if (secret.login.username != null) { if (secret.login.username != null) {
@ -2005,7 +2005,8 @@ private suspend fun List<IndexedModel<VaultItem2.Item>>.search(
source = x.tokens, source = x.tokens,
query = queryComponents, query = queryComponents,
) )
}.div(it.indexedOther.size.coerceAtLeast(1)) }
.div(it.indexedOther.size.coerceAtLeast(1))
val r = it.indexedText.find( val r = it.indexedText.find(
query = query, query = query,
colorBackground = highlightBackgroundColor, colorBackground = highlightBackgroundColor,
@ -2014,15 +2015,24 @@ private suspend fun List<IndexedModel<VaultItem2.Item>>.search(
if (r == null && q <= 0.0001f) { if (r == null && q <= 0.0001f) {
return@parallelSearch null return@parallelSearch null
} }
// Slightly prefer favorite items, but by a very small
// margin. The idea is that favorite items are show on top
// anyway, so if a user is using the search, then he probably
// searches for something other than the favorite items.
val favoriteScoreModifier = if (it.model.favourite) 1.35f else 1f
val finalScore = if (r != null) {
r.score + r.score * q / 10f
} else {
q / 15f
} * favoriteScoreModifier
FilteredModel( FilteredModel(
model = it.model, model = it.model,
score = q + (r?.score ?: 0f), score = finalScore,
result = r, result = r,
) )
} }
.sortedWith( .sortedWith(
compareBy( compareBy(
{ !it.model.favourite },
{ -it.score }, { -it.score },
), ),
) )

View File

@ -54,7 +54,7 @@ fun IndexedText.find(
return null return null
} }
var x = query.components.map { false }.toMutableList() val x = MutableList(query.components.size) { false }
var score = 0f var score = 0f
val text = buildAnnotatedString { val text = buildAnnotatedString {
@ -77,14 +77,20 @@ fun IndexedText.find(
return@forEachIndexed return@forEachIndexed
} }
val queryPositionScore = 1f - i.toFloat() / comp.lowercase.length.toFloat() // We want to prefer results that start with a given query
val queryLengthScore = val queryPositionScore = if (i == 0) { // starts with a query
queryComp.lowercase.length.toFloat() / comp.lowercase.length.toFloat() 1f
val s = 0f + } else {
queryPositionScore + val penalty = 0.25f
val ratio = i.toFloat() / comp.lowercase.length.toFloat()
(1f - penalty) * (1f - ratio)
}
val queryLengthScore = queryComp.lowercase.length.toFloat()
val totalScore = 0f +
queryPositionScore *
queryLengthScore queryLengthScore
if (s > max) { if (totalScore > max) {
max = s max = totalScore
// remember the position of the match, so we can draw it. // remember the position of the match, so we can draw it.
start = i start = i
end = i + queryComp.lowercase.length end = i + queryComp.lowercase.length
@ -104,19 +110,21 @@ fun IndexedText.find(
end = offset + end, end = offset + end,
) )
} }
score += max *
// We want to prioritize when the component starts with val componentPositionScoreModifier = kotlin.run {
// a given query. val compIndexNormalized = compIndex.toFloat() / components.size.toFloat()
((1f - compIndex.toFloat() / components.size.toFloat()) / 5f + 0.8f) (1f - compIndexNormalized) * 0.5f + 0.5f
}
score += max * componentPositionScoreModifier
} }
if (score < 0.01f || !x.all { it } && requireAll) return null if (score < 0.01f || requireAll && !x.all { it }) return null
} }
return IndexedText.FindResult( return IndexedText.FindResult(
text = this.text, text = this.text,
components = this.components, components = this.components,
highlightedText = text, highlightedText = text,
score = score / components.size.toFloat(), score = score / this.text.length.toFloat(),
) )
} }