Use SuggestionSearch instead of Search for better search functionality.

* Since `Search` is not compatible with those zim files which does not have Xapian index but `SuggestionSearch` have this functionality to search inside those zim files so we have used this.
* Update test cases for test new search functionality.
This commit is contained in:
MohitMali 2023-08-08 16:15:14 +05:30
parent 6235ba612a
commit c4dbe478b8
10 changed files with 34 additions and 37 deletions

View File

@ -37,9 +37,8 @@ import org.kiwix.libkiwix.JNIKiwixException
import org.kiwix.libzim.Archive
import org.kiwix.libzim.DirectAccessInfo
import org.kiwix.libzim.Item
import org.kiwix.libzim.Query
import org.kiwix.libzim.Search
import org.kiwix.libzim.Searcher
import org.kiwix.libzim.SuggestionSearch
import org.kiwix.libzim.SuggestionSearcher
import java.io.File
import java.io.FileInputStream
import java.io.IOException
@ -56,7 +55,7 @@ class ZimFileReader constructor(
val zimFile: File,
private val jniKiwixReader: Archive,
private val nightModeConfig: NightModeConfig,
private val searcher: Searcher = Searcher(jniKiwixReader)
private val searcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader)
) {
interface Factory {
fun create(file: File): ZimFileReader?
@ -130,9 +129,9 @@ class ZimFileReader constructor(
null
}
fun searchSuggestions(prefix: String): Search? =
fun searchSuggestions(prefix: String): SuggestionSearch? =
try {
searcher.search(Query(prefix))
searcher.suggest(prefix)
} catch (ignore: Exception) {
// to handled the exception if there is no FT Xapian index found in the current zim file
null

View File

@ -22,14 +22,14 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import org.kiwix.libzim.Search
import org.kiwix.libzim.SuggestionSearch
import javax.inject.Inject
interface SearchResultGenerator {
suspend fun generateSearchResults(
searchTerm: String,
zimFileReader: ZimFileReader?
): Search?
): SuggestionSearch?
}
class ZimSearchResultGenerator @Inject constructor() : SearchResultGenerator {

View File

@ -30,7 +30,7 @@ data class SearchState(
if (searchTerm.isEmpty()) {
recentResults
} else {
searchResultsWithTerm.search?.let {
searchResultsWithTerm.suggestionSearch?.let {
val maximumResults = it.estimatedMatches
val safeEndIndex =
if (startIndex + 100 < maximumResults) startIndex + 100 else maximumResults

View File

@ -59,7 +59,7 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchInPreviousScree
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowDeleteSearchDialog
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowToast
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.StartSpeechInput
import org.kiwix.libzim.Search
import org.kiwix.libzim.SuggestionSearch
import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
@ -174,4 +174,4 @@ class SearchViewModel @Inject constructor(
}
}
data class SearchResultsWithTerm(val searchTerm: String, val search: Search?)
data class SearchResultsWithTerm(val searchTerm: String, val suggestionSearch: SuggestionSearch?)

View File

@ -30,11 +30,11 @@ internal class SearchStateTest {
@Test
internal fun `visibleResults use searchResults when searchTerm is not empty`() {
val searchTerm = "notEmpty"
val searchWrapper: SearchWrapper = mockk()
val searchIteratorWrapper: SearchIteratorWrapper = mockk()
val entryWrapper: EntryWrapper = mockk()
val suggestionSearchWrapper: SuggestionSearchWrapper = mockk()
val searchIteratorWrapper: SuggestionIteratorWrapper = mockk()
val entryWrapper: SuggestionItemWrapper = mockk()
val estimatedMatches = 1
every { searchWrapper.estimatedMatches } returns estimatedMatches.toLong()
every { suggestionSearchWrapper.estimatedMatches } returns estimatedMatches.toLong()
// Settings list to hasNext() to ensure it returns true only for the first call.
// Otherwise, if we do not set this, the method will always return true when checking if the iterator has a next value,
// causing our test case to get stuck in an infinite loop due to this explicit setting.
@ -42,7 +42,7 @@ internal class SearchStateTest {
every { searchIteratorWrapper.next() } returns entryWrapper
every { entryWrapper.title } returns searchTerm
every {
searchWrapper.getResults(
suggestionSearchWrapper.getResults(
0,
estimatedMatches
)
@ -50,7 +50,7 @@ internal class SearchStateTest {
assertThat(
SearchState(
searchTerm,
SearchResultsWithTerm("", searchWrapper),
SearchResultsWithTerm("", suggestionSearchWrapper),
emptyList(),
FromWebView
).getVisibleResults(0)

View File

@ -74,7 +74,7 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchInPreviousScree
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowDeleteSearchDialog
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowToast
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.StartSpeechInput
import org.kiwix.libzim.Search
import org.kiwix.libzim.SuggestionSearch
@OptIn(ExperimentalCoroutinesApi::class)
internal class SearchViewModelTest {
@ -122,12 +122,12 @@ internal class SearchViewModelTest {
val item = ZimSearchResultListItem("")
val searchTerm = "searchTerm"
val searchOrigin = FromWebView
val search: Search = mockk()
val suggestionSearch: SuggestionSearch = mockk()
viewModel.state.test(this)
.also {
emissionOf(
searchTerm = searchTerm,
search = search,
suggestionSearch = suggestionSearch,
databaseResults = listOf(RecentSearchListItem("")),
searchOrigin = searchOrigin
)
@ -135,7 +135,7 @@ internal class SearchViewModelTest {
.assertValue(
SearchState(
searchTerm,
SearchResultsWithTerm(searchTerm, search),
SearchResultsWithTerm(searchTerm, suggestionSearch),
listOf(RecentSearchListItem("")),
searchOrigin
)
@ -243,14 +243,14 @@ internal class SearchViewModelTest {
private fun TestScope.emissionOf(
searchTerm: String,
search: Search,
suggestionSearch: SuggestionSearch,
databaseResults: List<RecentSearchListItem>,
searchOrigin: SearchOrigin
) {
coEvery {
searchResultGenerator.generateSearchResults(searchTerm, zimFileReader)
} returns search
} returns suggestionSearch
viewModel.actions.trySend(Filter(searchTerm)).isSuccess
recentsFromDb.trySend(databaseResults).isSuccess
viewModel.actions.trySend(ScreenWasStartedFrom(searchOrigin)).isSuccess

View File

@ -18,8 +18,8 @@
package org.kiwix.kiwixmobile.core.search.viewmodel
import org.kiwix.libzim.Entry
import org.kiwix.libzim.SuggestionItem
internal class EntryWrapper : Entry() {
class SuggestionItemWrapper : SuggestionItem() {
override fun getTitle(): String = super.getTitle()
}

View File

@ -18,12 +18,11 @@
package org.kiwix.kiwixmobile.core.search.viewmodel
import org.kiwix.libzim.SearchIterator
import org.kiwix.libzim.SuggestionIterator
// Create this as a helper class, as we can not directly use libkiwix/libzim functions in testing
internal class SearchIteratorWrapper : SearchIterator() {
class SuggestionIteratorWrapper : SuggestionIterator() {
override fun remove() {}
override fun hasNext(): Boolean = super.hasNext()
override fun next(): EntryWrapper = super.next() as EntryWrapper
override fun next(): SuggestionItemWrapper = super.next() as SuggestionItemWrapper
}

View File

@ -18,12 +18,11 @@
package org.kiwix.kiwixmobile.core.search.viewmodel
import org.kiwix.libzim.Search
import org.kiwix.libzim.SuggestionSearch
// Create this as a helper class, as we can not directly use libkiwix/libzim functions in testing
internal class SearchWrapper : Search() {
class SuggestionSearchWrapper : SuggestionSearch() {
override fun getEstimatedMatches(): Long = super.getEstimatedMatches()
override fun getResults(start: Int, maxResults: Int): SearchIteratorWrapper =
super.getResults(start, maxResults) as SearchIteratorWrapper
override fun getResults(start: Int, maxResults: Int): SuggestionIteratorWrapper =
super.getResults(start, maxResults) as SuggestionIteratorWrapper
}

View File

@ -44,11 +44,11 @@ internal class ZimSearchResultGeneratorTest {
@Test
internal fun `suggestion results are distinct`() {
val searchTerm = " "
val searchWrapper: SearchWrapper = mockk()
every { zimFileReader.searchSuggestions(searchTerm) } returns searchWrapper
val suggestionSearchWrapper: SuggestionSearchWrapper = mockk()
every { zimFileReader.searchSuggestions(searchTerm) } returns suggestionSearchWrapper
runBlocking {
assertThat(zimSearchResultGenerator.generateSearchResults(searchTerm, zimFileReader))
.isEqualTo(searchWrapper)
.isEqualTo(suggestionSearchWrapper)
verify {
zimFileReader.searchSuggestions(searchTerm)
}