Skip to content

Commit

Permalink
[optimize] Change popular tags recommendation strategy to prioritize …
Browse files Browse the repository at this point in the history
…recently shared stickers
  • Loading branch information
SkyD666 committed Aug 4, 2024
1 parent 70f2569 commit 287a55c
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 78 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 67
versionName = "2.3-alpha02"
versionName = "2.3-alpha03"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.skyd.rays.model.bean.StickerBean
import com.skyd.rays.model.bean.StickerWithTags
import com.skyd.rays.model.bean.TagBean
import com.skyd.rays.model.db.dao.SearchDomainDao
import com.skyd.rays.model.db.dao.cache.StickerShareTimeDao
import com.skyd.rays.model.db.dao.sticker.StickerDao
import com.skyd.rays.model.preference.ExportStickerDirPreference
import com.skyd.rays.model.preference.search.IntersectSearchBySpacePreference
Expand All @@ -28,6 +29,7 @@ import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
Expand All @@ -39,9 +41,11 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
import kotlin.math.pow

class SearchRepository @Inject constructor(
private val stickerDao: StickerDao,
private val stickerShareTimeDao: StickerShareTimeDao,
) : BaseRepository() {
fun requestStickerWithTagsList(keyword: String): List<StickerWithTags> = runBlocking {
stickerDao.getStickerWithTagsList(genSql(k = keyword)).first()
Expand Down Expand Up @@ -118,49 +122,42 @@ class SearchRepository @Inject constructor(
}
}

fun requestSearchBarPopularTags(count: Int): Flow<List<Pair<String, Float>>> {
return stickerDao.getPopularStickersList(count = count)
fun requestSearchBarPopularTags(count: Int): Flow<List<String>> {
return combine(
stickerDao.getRecentSharedStickers(count = count shr 1),
stickerDao.getPopularStickersList(count = count shr 1),
) { recentSharedStickers, popularStickers ->
recentSharedStickers.toMutableSet().apply {
addAll(popularStickers)
}.map {
it to stickerShareTimeDao.getShareTimeByUuid(it.sticker.uuid)
}
}
.distinctUntilChanged()
.map { popularStickersList ->
val tagsMap: MutableMap<Pair<String, String>, Long> = mutableMapOf()
val tagsCountMap: MutableMap<Pair<String, String>, Long> = mutableMapOf()
val stickerUuidCountMap: MutableMap<String, Long> = mutableMapOf()
popularStickersList.forEach {
it.tags.forEach { tag ->
val tagString = tag.tag
if (tagString.length < 6) {
tagsCountMap[tagString to it.sticker.uuid] = tagsCountMap
.getOrDefault(tagString to it.sticker.uuid, 0) + 1
tagsMap[tagString to it.sticker.uuid] = tagsMap
.getOrDefault(
tagString to it.sticker.uuid,
0
) + it.sticker.shareCount
}
.map { stickersList ->
// Step 2: Sort the list
val sortedDataList = stickersList.sortedWith(
compareByDescending<Pair<StickerWithTags, List<Long>>> {
it.second.sum()
}.thenByDescending { it.first.sticker.shareCount },
)

// Step 3: Count tag frequencies and add weights
val tagFrequency = mutableMapOf<String, Double>()
for ((index, data) in sortedDataList.withIndex()) {
val weight = (sortedDataList.size - index).toDouble().pow(4) // weight factor
for (tag in data.first.tags) {
// As the number of times a tag appears increases,
// reduce its new weight to avoid the first few tags being difficult to change
val newWeight = weight *
(1f / tagFrequency.getOrDefault(tag.tag, 1.0)
.coerceAtLeast(1.0)).pow(4)
tagFrequency[tag.tag] = tagFrequency.getOrDefault(tag.tag, 0.0) + newWeight
}
stickerUuidCountMap[it.sticker.uuid] = 0
}
tagsCountMap.forEach { (t, u) ->
tagsMap[t] = tagsMap.getOrDefault(t, 0) * u
}
var result = tagsMap.toList().sortedByDescending { (_, value) -> value }
result = result.filter {
val stickUuid = it.first.second
val cnt = stickerUuidCountMap[stickUuid]
if (cnt != null) {
// 限制每个表情包只能推荐两个标签
if (cnt >= 2) {
false
} else {
stickerUuidCountMap[stickUuid] = cnt + 1
true
}
} else {
false
}
}.distinctBy { it.first.first }
val maxPopularValue = result.getOrNull(0)?.second ?: 1
result.map { it.first.first to it.second.toFloat() / maxPopularValue }

// Step 4: Sort tags by their frequencies
tagFrequency.entries.sortedByDescending { it.value }.map { it.key }
}.flowOn(Dispatchers.IO)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ internal sealed interface SearchPartialStateChange {
data class Failed(val msg: String) : SearchDataResult
data class Success(
val stickerWithTagsList: List<StickerWithTags>,
val popularTags: List<Pair<String, Float>>
val popularTags: List<String>
) : SearchDataResult
}

Expand Down
39 changes: 9 additions & 30 deletions app/src/main/java/com/skyd/rays/ui/screen/search/SearchScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,13 @@ import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RichTooltip
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.VerticalDivider
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
Expand Down Expand Up @@ -367,33 +363,16 @@ fun TrailingIcon(
@Composable
fun PopularTagsBar(
onTagClicked: (String) -> Unit,
tags: List<Pair<String, Float>>,
tags: List<String>,
) {
val eachTag: @Composable (Pair<String, Float>) -> Unit = { item ->
TooltipBox(
positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
tooltip = {
RichTooltip(
title = { Text(item.first) },
text = {
Text(
text = stringResource(
R.string.home_screen_popular_tags_popular_value, item.second
)
)
}
)
},
state = rememberTooltipState(),
) {
Text(
modifier = Modifier
.clip(RoundedCornerShape(6.dp))
.clickable { onTagClicked(item.first) }
.padding(horizontal = 10.dp, vertical = 6.dp),
text = item.first
)
}
val eachTag: @Composable (String) -> Unit = { item ->
Text(
modifier = Modifier
.clip(RoundedCornerShape(6.dp))
.clickable { onTagClicked(item) }
.padding(horizontal = 10.dp, vertical = 6.dp),
text = item
)
}

Box {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ sealed class SearchDataState {
data object Init : SearchDataState()
data class Success(
val stickerWithTagsList: List<StickerWithTags>,
val popularTags: List<Pair<String, Float>>,
val popularTags: List<String>,
) : SearchDataState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private fun RaysSearchBarPreview() {
AnimatedVisibility(visible = LocalShowPopularTags.current) {
PopularTagsBar(
onTagClicked = {},
tags = listOf("Tag" to 1f, "LOL" to 0.9f),
tags = listOf("Tag", "LOL"),
)
}
SearchResultList(
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@
<string name="image_scale_fill_bounds">Preencher limites</string>
<string name="empty_tip">Vazio~</string>
<string name="webdav_screen_remote_recycle_bin">Lixeira remota</string>
<string name="home_screen_popular_tags_popular_value">Valor popular: %.2f</string>
<string name="send_sticker">Enviar sticker</string>
<string name="webdav_screen_upload_tip">Ao enviar dados locais para o remoto, os stickers locais substituirão o conteúdo remoto se houver um conflito. Se um sticker não estiver existente no local, mas no remoto, o sticker será movido para a lixeira remota.</string>
<string name="webdav_screen_remote_recycle_bin_description">Visualizar a lixeira remota (logicamente excluída)</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@
<string name="detail_screen_sticker_info_last_modified_time">上次修改时间</string>
<string name="home_screen_select_export_folder">选择导出文件夹</string>
<string name="home_screen_select_export_folder_tip">请选择导出文件夹</string>
<string name="home_screen_popular_tags_popular_value">热门值:%.2f</string>
<string name="db_sticker_table">表情包表</string>
<string name="db_sticker_table_uuid">UUID</string>
<string name="db_sticker_table_title">标题</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@
<string name="webdav_screen_progress_delete">刪除本地資料 + 表情包</string>
<string name="api_grant_repo_no_package">沒有軟體包可用:%s</string>
<string name="add_screen_suggest_tag">建議的標籤</string>
<string name="home_screen_popular_tags_popular_value">熱門值:%.2f</string>
<string name="sticker_modify_time">修改時間</string>
<string name="image_scale_inside">內部</string>
<string name="db_tag_table">標籤表</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@
<string name="detail_screen_sticker_info_last_modified_time">Last modified time</string>
<string name="home_screen_select_export_folder">Select export folder</string>
<string name="home_screen_select_export_folder_tip">Please select an export folder</string>
<string name="home_screen_popular_tags_popular_value">Popular value: %.2f</string>
<string name="db_sticker_table">Sticker table</string>
<string name="db_sticker_table_uuid">UUID</string>
<string name="db_sticker_table_title">Title</string>
Expand Down

0 comments on commit 287a55c

Please sign in to comment.