Skip to content

Commit

Permalink
Add support for bottom section
Browse files Browse the repository at this point in the history
  • Loading branch information
CrisBarreiro committed Mar 26, 2024
1 parent 8c14ea4 commit ae7e07b
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ package com.duckduckgo.app.autocomplete.api
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.AutoCompleteHistorySearchSuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistorySuggestion
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion.AutoCompleteSearchSuggestion
import com.duckduckgo.app.browser.UriString
import com.duckduckgo.common.utils.baseHost
Expand All @@ -28,6 +31,7 @@ import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.savedsites.api.SavedSitesRepository
import com.duckduckgo.savedsites.api.models.SavedSite
import com.duckduckgo.savedsites.api.models.SavedSite.Bookmark
import com.duckduckgo.savedsites.api.models.SavedSite.Favorite
import com.squareup.anvil.annotations.ContributesBinding
import io.reactivex.Observable
import javax.inject.Inject
Expand All @@ -51,6 +55,7 @@ interface AutoComplete {
override val phrase: String,
val title: String,
val url: String,
val isAllowedInTopHits: Boolean = false,
val isFavorite: Boolean = false,
) : AutoCompleteSuggestion(phrase)

Expand All @@ -76,20 +81,43 @@ class AutoCompleteApi @Inject constructor(
if (query.isBlank()) {
return Observable.just(AutoCompleteResult(query = query, suggestions = emptyList()))
}

val savedSitesObservable = getAutoCompleteBookmarkResults(query)
.zipWith(
getAutoCompleteFavoritesResults(query),
) { bookmarks, favorites ->
(favorites + bookmarks).take(2)
}
val savedSitesObservable: Observable<List<AutoCompleteSuggestion>> =
getAutoCompleteBookmarkResults(query)
.zipWith(
getAutoCompleteFavoritesResults(query),
) { bookmarks, favorites ->
(favorites + bookmarks)
}.map {
it.sortedByDescending { it.score }.mapNotNull {
val savedSite = it.savedSite
AutoCompleteBookmarkSuggestion(
phrase = savedSite.url.toUri().toStringDropScheme(),
title = savedSite.title,
url = savedSite.url,
isAllowedInTopHits = savedSite is Favorite,
isFavorite = savedSite is Favorite,
)
}
}

return savedSitesObservable.zipWith(
getAutoCompleteSearchResults(query),
) { bookmarksResults, searchResults ->

val topHits = bookmarksResults.filter {
when (it) {
is AutoCompleteHistorySearchSuggestion -> true
is AutoCompleteHistorySuggestion -> false // TODO (cbarreiro) add logic
is AutoCompleteBookmarkSuggestion -> it.isAllowedInTopHits
else -> false
}
}.take(2)

val filteredSearchResults = searchResults.filterNot { bookmarksResults.any { bookmark -> it.phrase == bookmark.phrase } }

AutoCompleteResult(
query = query,
suggestions = (bookmarksResults + searchResults).distinctBy { it.phrase },
suggestions = (topHits + filteredSearchResults + bookmarksResults.subtract(topHits.toSet())).distinctBy { it.phrase },
)
}
}
Expand All @@ -104,15 +132,11 @@ class AutoCompleteApi @Inject constructor(
.onErrorReturn { emptyList() }
.toObservable()

private fun getAutoCompleteBookmarkResults(query: String) =
private fun getAutoCompleteBookmarkResults(query: String): Observable<MutableList<RankedBookmark>> =
repository.getBookmarksObservable()
.map { rankBookmarks(query, it) }
.flattenAsObservable { it }
.map {
AutoCompleteBookmarkSuggestion(phrase = it.url.toUri().toStringDropScheme(), title = it.title, url = it.url, isFavorite = false)
}
.distinctUntilChanged()
.take(2)
.toList()
.onErrorReturn { emptyList() }
.toObservable()
Expand All @@ -121,27 +145,23 @@ class AutoCompleteApi @Inject constructor(
repository.getFavoritesObservable()
.map { rankFavorites(query, it) }
.flattenAsObservable { it }
.map {
AutoCompleteBookmarkSuggestion(phrase = it.url.toUri().toStringDropScheme(), title = it.title, url = it.url, isFavorite = true)
}
.distinctUntilChanged()
.take(2)
.toList()
.onErrorReturn { emptyList() }
.toObservable()

private fun rankBookmarks(
query: String,
bookmarks: List<Bookmark>,
): List<SavedSite> {
): List<RankedBookmark> {
return bookmarks.asSequence()
.sortByRank(query)
}

private fun rankFavorites(
query: String,
favorites: List<SavedSite.Favorite>,
): List<SavedSite> {
favorites: List<Favorite>,
): List<RankedBookmark> {
return favorites.asSequence().sortByRank(query)
}

Expand Down Expand Up @@ -244,13 +264,12 @@ class AutoCompleteApi @Inject constructor(
return builder.build().toString().removePrefix("//")
}

private fun Sequence<SavedSite>.sortByRank(query: String): List<SavedSite> {
private fun Sequence<SavedSite>.sortByRank(query: String): List<RankedBookmark> {
return this.map { RankedBookmark(savedSite = it) }
.map { scoreTitle(it, query) }
.map { scoreTokens(it, query) }
.filter { it.score >= 0 }
.sortedByDescending { it.score }
.map { it.savedSite }
.toList()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,34 +98,86 @@ class AutoCompleteApiTest {
}

@Test
fun whenAutoCompleteReturnsMultipleBookmarkHitsThenLimitToMaxOfTwo() {
whenever(mockAutoCompleteService.autoComplete("title")).thenReturn(Observable.just(emptyList()))
fun whenAutoCompleteReturnsMultipleBookmarkAndFavoriteHitsOnlyShowFavoritesBeforeSearchSuggestions() {
whenever(mockAutoCompleteService.autoComplete("title")).thenReturn(
Observable.just(
listOf(
AutoCompleteServiceRawResult("foo", isNav = false),
),
),
)
whenever(mockSavedSitesRepository.getFavoritesObservable()).thenReturn(
Single.just(
listOf(
favorite(title = "title", url = "https://example.com"),
favorite(title = "title", url = "https://foo.com"),
),
),
)
whenever(mockSavedSitesRepository.getBookmarksObservable()).thenReturn(
Single.just(
listOf(
bookmark(title = "title", url = "https://example.com"),
bookmark(title = "title", url = "https://foo.com"),
bookmark(title = "title", url = "https://bar.com"),
bookmark(title = "title", url = "https://baz.com"),
),
),
)
whenever(mockSavedSitesRepository.getFavoritesObservable()).thenReturn(Single.just(emptyList()))

val result = testee.autoComplete("title").test()
val value = result.values()[0] as AutoCompleteResult

assertEquals(
listOf(
AutoCompleteBookmarkSuggestion(phrase = "example.com", "title", "https://example.com"),
AutoCompleteBookmarkSuggestion(phrase = "foo.com", "title", "https://foo.com"),
AutoCompleteBookmarkSuggestion(phrase = "example.com", "title", "https://example.com", isAllowedInTopHits = true, isFavorite = true),
AutoCompleteBookmarkSuggestion(phrase = "foo.com", "title", "https://foo.com", isAllowedInTopHits = true, isFavorite = true),
AutoCompleteSearchSuggestion("foo", false),
AutoCompleteBookmarkSuggestion(phrase = "bar.com", "title", "https://bar.com", isAllowedInTopHits = false, isFavorite = false),
AutoCompleteBookmarkSuggestion(phrase = "baz.com", "title", "https://baz.com", isAllowedInTopHits = false, isFavorite = false),
),
value.suggestions,
)
}

@Test
fun whenAutoCompleteReturnsMultipleFavoriteHitsLimitTopHitsTo2() {
whenever(mockAutoCompleteService.autoComplete("title")).thenReturn(
Observable.just(
listOf(
AutoCompleteServiceRawResult("foo", isNav = false),
),
),
)
whenever(mockSavedSitesRepository.getFavoritesObservable()).thenReturn(
Single.just(
listOf(
favorite(title = "title", url = "https://example.com"),
favorite(title = "title", url = "https://foo.com"),
favorite(title = "title", url = "https://bar.com"),
),
),
)
whenever(mockSavedSitesRepository.getBookmarksObservable()).thenReturn(
Single.just(
listOf(),
),
)

val result = testee.autoComplete("title").test()
val value = result.values()[0] as AutoCompleteResult

assertEquals(
listOf(
AutoCompleteBookmarkSuggestion(phrase = "example.com", "title", "https://example.com", isAllowedInTopHits = true, isFavorite = true),
AutoCompleteBookmarkSuggestion(phrase = "foo.com", "title", "https://foo.com", isAllowedInTopHits = true, isFavorite = true),
AutoCompleteSearchSuggestion("foo", false),
AutoCompleteBookmarkSuggestion(phrase = "bar.com", "title", "https://bar.com", isAllowedInTopHits = true, isFavorite = true),
),
value.suggestions,
)
}

@Test
fun whenAutoCompleteReturnsMultipleSavedSitesHitsThenLimitToMaxOfTwoFavoritesFirst() {
fun whenAutoCompleteReturnsMultipleSavedSitesHitsThenShowFavcritesFirst() {
whenever(mockAutoCompleteService.autoComplete("title")).thenReturn(Observable.just(emptyList()))
whenever(mockSavedSitesRepository.getBookmarksObservable()).thenReturn(
Single.just(
Expand All @@ -151,13 +203,14 @@ class AutoCompleteApiTest {
val result = testee.autoComplete("title").test()
val value = result.values()[0] as AutoCompleteResult

assertEquals(
listOf(
AutoCompleteBookmarkSuggestion(phrase = "favexample.com", "title", "https://favexample.com", true),
AutoCompleteBookmarkSuggestion(phrase = "favfoo.com", "title", "https://favfoo.com", true),
),
value.suggestions,
)
assertTrue((value.suggestions[0] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertTrue((value.suggestions[1] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertTrue((value.suggestions[2] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertTrue((value.suggestions[3] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertFalse((value.suggestions[4] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertFalse((value.suggestions[5] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertFalse((value.suggestions[6] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
assertFalse((value.suggestions[7] as AutoCompleteBookmarkSuggestion).isAllowedInTopHits)
}

@Test
Expand Down Expand Up @@ -198,16 +251,35 @@ class AutoCompleteApiTest {

assertEquals(
listOf(
AutoCompleteBookmarkSuggestion(phrase = "example.com", "title example", "https://example.com", true),
AutoCompleteBookmarkSuggestion(
phrase = "example.com",
"title example",
"https://example.com",
isAllowedInTopHits = true,
isFavorite = true,
),
AutoCompleteBookmarkSuggestion(
phrase = "foo.com/path/to/foo",
"title foo",
"https://foo.com/path/to/foo",
true,
isAllowedInTopHits = true,
isFavorite = true,
),
AutoCompleteSearchSuggestion(phrase = "foo.com", true),
AutoCompleteSearchSuggestion(phrase = "bar.com", true),
AutoCompleteSearchSuggestion(phrase = "baz.com", true),
AutoCompleteBookmarkSuggestion(
phrase = "foo.com",
title = "title foo",
url = "https://foo.com",
isAllowedInTopHits = true,
isFavorite = true,
),
AutoCompleteBookmarkSuggestion(
phrase = "bar.com",
title = "title bar",
url = "https://bar.com",
isAllowedInTopHits = true,
isFavorite = true,
),
),
value.suggestions,
)
Expand Down Expand Up @@ -242,8 +314,20 @@ class AutoCompleteApiTest {

assertEquals(
listOf(
AutoCompleteBookmarkSuggestion(phrase = "favexample.com", "title", "https://favexample.com", true),
AutoCompleteBookmarkSuggestion(phrase = "example.com", "title", "https://example.com", false),
AutoCompleteBookmarkSuggestion(
phrase = "favexample.com",
title = "title",
url = "https://favexample.com",
isAllowedInTopHits = true,
isFavorite = true,
),
AutoCompleteBookmarkSuggestion(
phrase = "example.com",
title = "title",
url = "https://example.com",
isAllowedInTopHits = false,
isFavorite = false,
),
),
value.suggestions,
)
Expand Down Expand Up @@ -277,15 +361,15 @@ class AutoCompleteApiTest {

assertEquals(
listOf(
AutoCompleteSearchSuggestion(phrase = "example.com", false),
AutoCompleteSearchSuggestion(phrase = "baz.com", true),
AutoCompleteBookmarkSuggestion(
phrase = "foo.com?key=value",
"title foo",
"https://foo.com?key=value",
),
AutoCompleteBookmarkSuggestion(phrase = "foo.com", "title foo", "https://foo.com"),
AutoCompleteSearchSuggestion(phrase = "example.com", false),
AutoCompleteSearchSuggestion(phrase = "bar.com", true),
AutoCompleteSearchSuggestion(phrase = "baz.com", true),
AutoCompleteBookmarkSuggestion(phrase = "bar.com", "title bar", "https://bar.com"),
),
value.suggestions,
)
Expand All @@ -309,16 +393,14 @@ class AutoCompleteApiTest {
val result = testee.autoComplete("title").test()
val value = result.values()[0] as AutoCompleteResult

assertEquals(AutoCompleteBookmarkSuggestion(phrase = "bar.com", "title bar", "https://bar.com"), value.suggestions[0])
assertEquals(
listOf(
AutoCompleteBookmarkSuggestion(phrase = "bar.com", "title bar", "https://bar.com"),
AutoCompleteBookmarkSuggestion(
phrase = "example.com",
"the title example",
"https://example.com",
),
AutoCompleteBookmarkSuggestion(
phrase = "example.com",
"the title example",
"https://example.com",
),
value.suggestions,
value.suggestions[1],
)
}

Expand Down

0 comments on commit ae7e07b

Please sign in to comment.