Change the result type of searchAll* in *FtsDao as Flow to not block the

calling thread
recent_search
Takeshi Hagikura 1 year ago
parent 477838f9da
commit 333a248327

@ -22,8 +22,13 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flattenConcat
import kotlinx.coroutines.withContext
import javax.inject.Inject
class DefaultSearchContentsRepository @Inject constructor(
@ -31,11 +36,16 @@ class DefaultSearchContentsRepository @Inject constructor(
private val newsResourceFtsDao: NewsResourceFtsDao,
private val topicDao: TopicDao,
private val topicFtsDao: TopicFtsDao,
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
) : SearchContentsRepository {
override fun populateFtsData() {
newsResourceFtsDao.insertAll(newsResourceDao.getOneOffNewsResources().map { it.asFtsEntity() })
topicFtsDao.insertAll(topicDao.getOneOffTopicEntities().map { it.asFtsEntity() })
override suspend fun populateFtsData() {
withContext(ioDispatcher) {
newsResourceFtsDao.insertAll(
newsResourceDao.getOneOffNewsResources().map { it.asFtsEntity() },
)
topicFtsDao.insertAll(topicDao.getOneOffTopicEntities().map { it.asFtsEntity() })
}
}
override fun searchContents(searchQuery: String): Flow<SearchResult> {
@ -44,14 +54,16 @@ class DefaultSearchContentsRepository @Inject constructor(
val newsResourceIds = newsResourceFtsDao.searchAllNewsResources("*$searchQuery*")
val topicIds = topicFtsDao.searchAllTopics("*$searchQuery*")
return combine(
newsResourceDao.getNewsResources(filterNewsIds = newsResourceIds.toSet()),
topicDao.getTopicEntities(topicIds.toSet()),
) { newsResources, topics ->
SearchResult(
topics = topics.map { it.asExternalModel() },
newsResources = newsResources.map { it.asExternalModel() },
)
}
return combine(newsResourceIds, topicIds) { news, topics ->
combine(
newsResourceDao.getNewsResources(filterNewsIds = news.toSet()),
topicDao.getTopicEntities(topics.toSet()),
) { newsResources, topics ->
SearchResult(
topics = topics.map { it.asExternalModel() },
newsResources = newsResources.map { it.asExternalModel() },
)
}
}.flattenConcat()
}
}

@ -20,8 +20,19 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.coroutines.flow.Flow
/**
* Data layer interface for the search feature.
*/
interface SearchContentsRepository {
fun populateFtsData()
/**
* Populate the fts tables for the search contents.
*/
suspend fun populateFtsData()
/**
* Query the contents matched with the [searchQuery] and returns it as a [Flow] of [SearchResult]
*/
fun searchContents(searchQuery: String): Flow<SearchResult>
}

@ -26,7 +26,7 @@ import javax.inject.Inject
*/
class FakeSearchContentsRepository @Inject constructor() : SearchContentsRepository {
override fun populateFtsData() {}
override suspend fun populateFtsData() {}
override fun searchContents(searchQuery: String): Flow<SearchResult> {
TODO("Not yet implemented")
}

@ -38,5 +38,5 @@ object DatabaseModule {
"nia-database",
// TODO: This is a workaround for executing read query in the main thread for search.
// Figure out how other use cases avoid that
).allowMainThreadQueries().build()
).build()
}

@ -21,6 +21,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsEntity
import kotlinx.coroutines.flow.Flow
/**
* DAO for [NewsResourceFtsEntity] access.
@ -28,11 +29,11 @@ import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsE
@Dao
interface NewsResourceFtsDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(topics: List<NewsResourceFtsEntity>)
suspend fun insertAll(topics: List<NewsResourceFtsEntity>)
@Query("SELECT newsResourceId FROM newsResourcesFts WHERE newsResourcesFts MATCH :query")
fun searchAllNewsResources(query: String): List<String>
fun searchAllNewsResources(query: String): Flow<List<String>>
@Query("SELECT count(*) FROM newsResourcesFts")
fun getCount(): Int
suspend fun getCount(): Int
}

@ -21,6 +21,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.google.samples.apps.nowinandroid.core.database.model.TopicFtsEntity
import kotlinx.coroutines.flow.Flow
/**
* DAO for [TopicFtsEntity] access.
@ -28,11 +29,11 @@ import com.google.samples.apps.nowinandroid.core.database.model.TopicFtsEntity
@Dao
interface TopicFtsDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(topics: List<TopicFtsEntity>)
suspend fun insertAll(topics: List<TopicFtsEntity>)
@Query("SELECT topicId FROM topicsFts WHERE topicsFts MATCH :query")
fun searchAllTopics(query: String): List<String>
fun searchAllTopics(query: String): Flow<List<String>>
@Query("SELECT count(*) FROM topicsFts")
fun getCount(): Int
suspend fun getCount(): Int
}

@ -26,6 +26,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import javax.inject.Inject
/**
* A use case which returns the searched contents matched with the search query.
*/
class GetSearchContentsUseCase @Inject constructor(
private val searchContentsRepository: SearchContentsRepository,
private val userDataRepository: UserDataRepository,

@ -28,7 +28,7 @@ class TestSearchContentsRepository : SearchContentsRepository {
private val cachedTopics: MutableList<Topic> = mutableListOf()
private val cachedNewsResources: MutableList<NewsResource> = mutableListOf()
override fun populateFtsData() {}
override suspend fun populateFtsData() {}
override fun searchContents(searchQuery: String): Flow<SearchResult> {
return flowOf(

Loading…
Cancel
Save