diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt index 3bacb8a14..8b0650120 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt @@ -20,7 +20,6 @@ import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao 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.PopulatedNewsResource 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.model.data.SearchResult @@ -46,15 +45,68 @@ internal class DefaultSearchContentsRepository @Inject constructor( override suspend fun populateFtsData() { withContext(ioDispatcher) { - newsResourceFtsDao.insertAll( - newsResourceDao.getNewsResources( - useFilterTopicIds = false, - useFilterNewsIds = false, - ) - .first() - .map(PopulatedNewsResource::asFtsEntity), + newsResourceDao.getNewsResources( + useFilterTopicIds = false, + useFilterNewsIds = false, ) - topicFtsDao.insertAll(topicDao.getOneOffTopicEntities().map { it.asFtsEntity() }) + .first() + .forEach { populatedNewsResource -> + val oldNewsResourceFtsEntities = + newsResourceFtsDao.getFtsEntitiesById(populatedNewsResource.entity.id) + val size = oldNewsResourceFtsEntities.size + + val newsResourceFtsEntity = populatedNewsResource.asFtsEntity() + + if (size == 0) { + newsResourceFtsDao.insert(newsResourceFtsEntity) + return@forEach + } + + // Do it for migration, multiple same id entity exists in the fts database. + if (size > 1) { + newsResourceFtsDao.deleteById(newsResourceFtsEntity.newsResourceId) + newsResourceFtsDao.insert(newsResourceFtsEntity) + return@forEach + } + + if (oldNewsResourceFtsEntities.firstOrNull() == newsResourceFtsEntity) { + return@forEach + } + + newsResourceFtsDao.update( + title = newsResourceFtsEntity.title, + content = newsResourceFtsEntity.content, + newsResourceId = newsResourceFtsEntity.newsResourceId, + ) + } + + topicDao.getOneOffTopicEntities().forEach { topicEntity -> + val oldTopicFtsEntities = topicFtsDao.getFtsEntitiesById(topicEntity.id) + val size = oldTopicFtsEntities.size + + val topicFtsEntity = topicEntity.asFtsEntity() + + if (size == 0) { + topicFtsDao.insert(topicFtsEntity) + return@forEach + } + + // Do it for migration, multiple same id entity exists in the fts database. + if (size > 1) { + topicFtsDao.deleteById(topicEntity.id) + topicFtsDao.insert(topicFtsEntity) + return@forEach + } + + if (oldTopicFtsEntities.firstOrNull() == topicFtsEntity) return@forEach + + topicFtsDao.update( + name = topicEntity.name, + shortDescription = topicEntity.shortDescription, + longDescription = topicEntity.longDescription, + topicId = topicEntity.id, + ) + } } } diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt index 86cc5529e..33ae93ace 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt @@ -28,8 +28,41 @@ import kotlinx.coroutines.flow.Flow */ @Dao interface NewsResourceFtsDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAll(newsResources: List) + suspend fun insert(newsResource: NewsResourceFtsEntity) + + @Query( + """ + DELETE + FROM newsResourcesFts + WHERE newsResourceId = :newsResourceId + """, + ) + suspend fun deleteById(newsResourceId: String) + + @Query( + """ + SELECT * + FROM newsResourcesFts + WHERE newsResourceId = :newsResourceId + """, + ) + suspend fun getFtsEntitiesById(newsResourceId: String): List + + @Query( + """ + UPDATE newsResourcesFts + SET title = :title, + content = :content + WHERE newsResourceId = :newsResourceId + """, + ) + suspend fun update( + title: String, + content: String, + newsResourceId: String, + ) @Query("SELECT newsResourceId FROM newsResourcesFts WHERE newsResourcesFts MATCH :query") fun searchAllNewsResources(query: String): Flow> diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt index 25dea5b83..35bc8ba0c 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt @@ -28,8 +28,43 @@ import kotlinx.coroutines.flow.Flow */ @Dao interface TopicFtsDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAll(topics: List) + suspend fun insert(topic: TopicFtsEntity) + + @Query( + """ + DELETE + FROM topicsFts + WHERE topicId = :topicId + """, + ) + suspend fun deleteById(topicId: String) + + @Query( + """ + SELECT * + FROM topicsFts + WHERE topicId = :topicId + """, + ) + suspend fun getFtsEntitiesById(topicId: String): List + + @Query( + """ + UPDATE topicsFts + SET name = :name, + shortDescription = :shortDescription, + longDescription = :longDescription + WHERE topicId = :topicId + """, + ) + suspend fun update( + name: String, + shortDescription: String, + longDescription: String, + topicId: String, + ) @Query("SELECT topicId FROM topicsFts WHERE topicsFts MATCH :query") fun searchAllTopics(query: String): Flow>