Skip to content

Commit

Permalink
Updated the autosuggest algorithm to include open tabs.
Browse files Browse the repository at this point in the history
  • Loading branch information
anikiki committed Sep 26, 2024
1 parent 717d20a commit 2ee8aac
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ import com.duckduckgo.app.accessibility.data.AccessibilitySettingsDataStore
import com.duckduckgo.app.accessibility.data.AccessibilitySettingsSharedPreferences
import com.duckduckgo.app.autocomplete.api.AutoComplete
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteResult
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteDefaultSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.api.AutoCompleteApi
import com.duckduckgo.app.autocomplete.api.AutoCompleteScorer
import com.duckduckgo.app.autocomplete.api.AutoCompleteService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import android.net.Uri
import androidx.core.net.toUri
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteResult
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteDefaultSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteInAppMessageSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.impl.AutoCompleteRepository
import com.duckduckgo.app.browser.UriString
import com.duckduckgo.app.onboarding.store.AppStage
Expand Down Expand Up @@ -80,19 +81,26 @@ interface AutoComplete {
override val phrase: String,
) : AutoCompleteSuggestion(phrase)

data class AutoCompleteBookmarkSuggestion(
override val phrase: String,
val title: String,
val url: String,
val isFavorite: Boolean = false,
) : AutoCompleteSuggestion(phrase)
sealed class AutoCompleteUrlSuggestion(
phrase: String,
open val title: String,
open val url: String,
) : AutoCompleteSuggestion(phrase) {

data class AutoCompleteSwitchToTabSuggestion(
override val phrase: String,
val title: String,
val url: String,
val tabId: String,
) : AutoCompleteSuggestion(phrase)
data class AutoCompleteBookmarkSuggestion(
override val phrase: String,
override val title: String,
override val url: String,
val isFavorite: Boolean = false,
) : AutoCompleteUrlSuggestion(phrase, title, url)

data class AutoCompleteSwitchToTabSuggestion(
override val phrase: String,
override val title: String,
override val url: String,
val tabId: String,
) : AutoCompleteUrlSuggestion(phrase, title, url)
}

sealed class AutoCompleteHistoryRelatedSuggestion(phrase: String) : AutoCompleteSuggestion(phrase) {
data class AutoCompleteHistorySuggestion(
Expand Down Expand Up @@ -124,7 +132,6 @@ class AutoCompleteApi @Inject constructor(
private val dispatcherProvider: DispatcherProvider,
) : AutoComplete {

// TODO ANA: Add option to "Switch to tab" in the AutoComplete suggestions
override fun autoComplete(query: String): Observable<AutoCompleteResult> {
if (query.isBlank()) {
return Observable.just(AutoCompleteResult(query = query, suggestions = emptyList()))
Expand All @@ -135,27 +142,27 @@ class AutoCompleteApi @Inject constructor(
getAutoCompleteFavoritesResults(query),
) { bookmarks, favorites ->
(favorites + bookmarks.filter { favorites.none { favorite -> (it.suggestion).url == favorite.suggestion.url } })
}.zipWith(
getAutocompleteSwitchToTabResults(query),
) { bookmarksAndFavorites, tabs ->
(tabs + bookmarksAndFavorites) as List<RankedSuggestion<AutoCompleteUrlSuggestion>>
}.zipWith(
getHistoryResults(query),
) { bookmarksAndFavorites, historyItems ->
) { bookmarksAndFavoritesAndTabs, historyItems ->
val searchHistory = historyItems.filter { it.suggestion is AutoCompleteHistorySearchSuggestion }
val navigationHistory = historyItems
.filter { it.suggestion is AutoCompleteHistorySuggestion } as List<RankedSuggestion<AutoCompleteHistorySuggestion>>
(removeDuplicates(navigationHistory, bookmarksAndFavorites) + searchHistory).sortedByDescending { it.score }.map { it.suggestion }
}.zipWith(
getAutocompleteSwitchToTabResults(query),
) { bookmarksAndHistory, tabs ->
// TODO: ANA to handle dedupe, sorting, etc
(tabs.map { it.suggestion } + bookmarksAndHistory)
(removeDuplicates(navigationHistory, bookmarksAndFavoritesAndTabs) + searchHistory)
.sortedByDescending { it.score }
.map { it.suggestion }
}

return savedSitesObservable.zipWith(getAutoCompleteSearchResults(query)) { bookmarksAndTabsAndHistory, searchResults ->
val topHits = (searchResults + bookmarksAndTabsAndHistory).filter {
when (it) {
// TODO: ANA tabs should also be allowed in top hits
is AutoCompleteHistorySearchSuggestion -> it.isAllowedInTopHits
is AutoCompleteHistorySuggestion -> it.isAllowedInTopHits
is AutoCompleteBookmarkSuggestion -> true
is AutoCompleteUrlSuggestion -> true
else -> false
}
}.take(maximumNumberOfTopHits)
Expand All @@ -172,8 +179,12 @@ class AutoCompleteApi @Inject constructor(

val inAppMessage = mutableListOf<AutoCompleteSuggestion>()

val suggestions = (topHits + filteredSearchResults + filteredBookmarks).distinctBy { it.phrase }

val suggestions = (topHits + filteredSearchResults + filteredBookmarks).distinctBy {
Pair<String, Class<out AutoCompleteSuggestion>>(
it.phrase,
it::class.java,
)
}
runBlocking(dispatcherProvider.io()) {
if (shouldShowHistoryInAutoCompleteIAM(suggestions)) {
inAppMessage.add(0, AutoCompleteInAppMessageSuggestion)
Expand All @@ -189,7 +200,7 @@ class AutoCompleteApi @Inject constructor(

private fun removeDuplicates(
historySuggestions: List<RankedSuggestion<AutoCompleteHistorySuggestion>>,
bookmarkSuggestions: List<RankedSuggestion<AutoCompleteBookmarkSuggestion>>,
bookmarkSuggestions: List<RankedSuggestion<AutoCompleteUrlSuggestion>>,
): List<RankedSuggestion<*>> {
val bookmarkMap = bookmarkSuggestions.associateBy { it.suggestion.phrase.lowercase() }

Expand Down Expand Up @@ -295,6 +306,7 @@ class AutoCompleteApi @Inject constructor(
): List<RankedSuggestion<AutoCompleteSwitchToTabSuggestion>> {
return tabs.asSequence()
.filter { it.url != null }
.distinctBy { it.url }
.sortTabsByRank(query)
}

Expand All @@ -321,7 +333,6 @@ class AutoCompleteApi @Inject constructor(
}

private fun Sequence<TabEntity>.sortTabsByRank(query: String): List<RankedSuggestion<AutoCompleteSwitchToTabSuggestion>> {
// TODO: ANA to implement the sorting
return this.map { tabEntity ->
RankedSuggestion(
AutoCompleteSwitchToTabSuggestion(
Expand All @@ -331,7 +342,11 @@ class AutoCompleteApi @Inject constructor(
tabId = tabEntity.tabId,
),
)
}.toList()
}
.map { scoreTitle(it, query) }
.map { scoreTokens(it, query) }
.filter { it.score > 0 }
.toList()
}

private fun Sequence<SavedSite>.sortByRank(query: String): List<RankedSuggestion<AutoCompleteBookmarkSuggestion>> {
Expand Down Expand Up @@ -392,23 +407,23 @@ class AutoCompleteApi @Inject constructor(
.toList()
}

private fun scoreTitle(
rankedBookmark: RankedSuggestion<AutoCompleteBookmarkSuggestion>,
private fun <T : AutoCompleteUrlSuggestion> scoreTitle(
rankedSuggestion: RankedSuggestion<T>,
query: String,
): RankedSuggestion<AutoCompleteBookmarkSuggestion> {
return if (rankedBookmark.suggestion.title.startsWith(query, ignoreCase = true)) {
rankedBookmark.copy(score = rankedBookmark.score + 200)
} else if (rankedBookmark.suggestion.title.contains(" $query", ignoreCase = true)) {
rankedBookmark.copy(score = rankedBookmark.score + 100)
): RankedSuggestion<T> {
return if (rankedSuggestion.suggestion.title.startsWith(query, ignoreCase = true)) {
rankedSuggestion.copy(score = rankedSuggestion.score + 200)
} else if (rankedSuggestion.suggestion.title.contains(" $query", ignoreCase = true)) {
rankedSuggestion.copy(score = rankedSuggestion.score + 100)
} else {
rankedBookmark
rankedSuggestion
}
}

private fun scoreTokens(
rankedBookmark: RankedSuggestion<AutoCompleteBookmarkSuggestion>,
private fun <T : AutoCompleteUrlSuggestion> scoreTokens(
rankedBookmark: RankedSuggestion<T>,
query: String,
): RankedSuggestion<AutoCompleteBookmarkSuggestion> {
): RankedSuggestion<T> {
val suggestion = rankedBookmark.suggestion
val domain = suggestion.url.toUri().baseHost
val tokens = query.split(" ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ import com.duckduckgo.app.accessibility.data.AccessibilitySettingsDataStore
import com.duckduckgo.app.autocomplete.api.AutoComplete
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteResult
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteDefaultSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteInAppMessageSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.browser.LongPressHandler.RequiredAction
import com.duckduckgo.app.browser.SSLErrorType.EXPIRED
import com.duckduckgo.app.browser.SSLErrorType.GENERIC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.recyclerview.widget.RecyclerView
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteDefaultSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteInAppMessageSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.browser.autocomplete.AutoCompleteViewHolder.EmptySuggestionViewHolder
import com.duckduckgo.app.browser.autocomplete.BrowserAutoCompleteSuggestionsAdapter.Type.BOOKMARK_TYPE
import com.duckduckgo.app.browser.autocomplete.BrowserAutoCompleteSuggestionsAdapter.Type.DEFAULT_TYPE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteDefaultSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteBookmarkSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion
import com.duckduckgo.app.browser.R
import com.duckduckgo.app.browser.autocomplete.AutoCompleteViewHolder.InAppMessageViewHolder
import com.duckduckgo.app.browser.databinding.ItemAutocompleteBookmarkSuggestionBinding
Expand Down
Loading

0 comments on commit 2ee8aac

Please sign in to comment.