diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt deleted file mode 100644 index 7979ffbbf..000000000 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.database - -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.RecentSearchQueryDao -import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao -import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao - -internal abstract class NiaDatabase : RoomDatabase() { - abstract fun topicDao(): TopicDao - abstract fun newsResourceDao(): NewsResourceDao - abstract fun topicFtsDao(): TopicFtsDao - abstract fun newsResourceFtsDao(): NewsResourceFtsDao - abstract fun recentSearchQueryDao(): RecentSearchQueryDao -} diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt index 0ad1e4f7d..a49e44ca3 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt @@ -16,111 +16,124 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction -import androidx.room.Upsert +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.datetime.Instant /** * DAO for [NewsResource] and [NewsResourceEntity] access */ -@Dao -interface NewsResourceDao { +class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { + private val query = db.newsResourceQueries /** * Fetches news resources that match the query parameters */ - @Transaction - @Query( - value = """ - SELECT * FROM news_resources - WHERE - CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) - ELSE 1 - END - AND - CASE WHEN :useFilterTopicIds - THEN id IN - ( - SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) - ) - ELSE 1 - END - ORDER BY publish_date DESC - """, - ) fun getNewsResources( useFilterTopicIds: Boolean = false, filterTopicIds: Set = emptySet(), useFilterNewsIds: Boolean = false, filterNewsIds: Set = emptySet(), - ): Flow> + ): Flow> { + return query.getNewsResources( + useFilterTopicIds = useFilterTopicIds, + filterTopicIds = filterTopicIds, + useFilterNewsIds = useFilterNewsIds, + filterNewsIds = filterNewsIds, + ) { id, title, content, url, headerImageUrl, publishDate, type -> + PopulatedNewsResource( + entity = NewsResourceEntity( + id = id, + title = title, + content = content, + url = url, + headerImageUrl = headerImageUrl, + publishDate = Instant.fromEpochMilliseconds(publishDate), + type = type, + ), + // TODO Dealing with NewsResources <-> Topics relationship + topics = emptyList(), + ) + } + .asFlow() + .mapToList(dispatcher) + } /** * Fetches ids of news resources that match the query parameters */ - @Transaction - @Query( - value = """ - SELECT id FROM news_resources - WHERE - CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) - ELSE 1 - END - AND - CASE WHEN :useFilterTopicIds - THEN id IN - ( - SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) - ) - ELSE 1 - END - ORDER BY publish_date DESC - """, - ) fun getNewsResourceIds( useFilterTopicIds: Boolean = false, filterTopicIds: Set = emptySet(), useFilterNewsIds: Boolean = false, filterNewsIds: Set = emptySet(), - ): Flow> + ): Flow> { + return query.getNewsResourceIds( + useFilterTopicIds = useFilterTopicIds, + filterTopicIds = filterTopicIds, + useFilterNewsIds = useFilterNewsIds, + filterNewsIds = filterNewsIds, + ) + .asFlow() + .mapToList(dispatcher) + } /** * Inserts [entities] into the db if they don't exist, and ignores those that do */ - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreNewsResources(entities: List): List + suspend fun insertOrIgnoreNewsResources(entities: List): List { + entities.forEach { + query.insertOrIgnoreNewsResource( + id = it.id, + title = it.title, + content = it.content, + url = it.url, + header_image_url = it.headerImageUrl, + publish_date = it.publishDate.toEpochMilliseconds(), + type = it.type, + ) + } + // TODO Return the inserted ids + return entities.mapNotNull { + it.id.toLongOrNull() + } + } /** * Inserts or updates [newsResourceEntities] in the db under the specified primary keys */ - @Upsert - suspend fun upsertNewsResources(newsResourceEntities: List) + suspend fun upsertNewsResources(newsResourceEntities: List) { + newsResourceEntities.forEach { + query.upsertNewsResource( + id = it.id, + title = it.title, + content = it.content, + url = it.url, + header_image_url = it.headerImageUrl, + publish_date = it.publishDate.toEpochMilliseconds(), + type = it.type, + ) + } + } - @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertOrIgnoreTopicCrossRefEntities( newsResourceTopicCrossReferences: List, - ) + ) { + // TODO Consider removing cross references +// query.insertOrIgnoreNewsResourceTopicCrossRefs(newsResourceTopicCrossReferences) + } /** * Deletes rows in the db matching the specified [ids] */ - @Query( - value = """ - DELETE FROM news_resources - WHERE id in (:ids) - """, - ) - suspend fun deleteNewsResources(ids: List) + suspend fun deleteNewsResources(ids: List) { + query.deleteNewsResources(ids) + } } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt index 86cc5529e..0834446e3 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt @@ -16,24 +16,36 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import app.cash.sqldelight.coroutines.mapToOneNotNull +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsEntity +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow /** * DAO for [NewsResourceFtsEntity] access. */ -@Dao -interface NewsResourceFtsDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAll(newsResources: List) +class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { + private val query = db.newsResourceFtsQueries + suspend fun insertAll(newsResources: List) { + newsResources.forEach { + query.insert( + news_resource_id = it.newsResourceId, + title = it.title, + content = it.content, + ) + } + } - @Query("SELECT newsResourceId FROM newsResourcesFts WHERE newsResourcesFts MATCH :query") - fun searchAllNewsResources(query: String): Flow> + fun searchAllNewsResources(query: String): Flow> { + return query.searchAllNewsResources(query) + } - @Query("SELECT count(*) FROM newsResourcesFts") - fun getCount(): Flow + fun getCount(): Flow { + return query.getCount() + .asFlow() + .mapToOneNotNull(dispatcher) + } } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt index 043d110cd..f18279987 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt @@ -16,28 +16,18 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import com.google.samples.apps.nowinandroid.core.model.data.Topic /** * Defines a topic a user may follow. * It has a many to many relationship with [NewsResourceEntity] */ -@Entity( - tableName = "topics", -) data class TopicEntity( - @PrimaryKey val id: String, val name: String, val shortDescription: String, - @ColumnInfo(defaultValue = "") val longDescription: String, - @ColumnInfo(defaultValue = "") val url: String, - @ColumnInfo(defaultValue = "") val imageUrl: String, ) diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq index a3c897183..85cef42cc 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq @@ -12,7 +12,7 @@ getNewsResources: SELECT * FROM news_resource WHERE CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) + THEN id IN :filterNewsIds ELSE 1 END AND @@ -20,7 +20,7 @@ WHERE THEN id IN ( SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) + WHERE topic_id IN :filterTopicIds ) ELSE 1 END @@ -30,7 +30,7 @@ getNewsResourceIds: SELECT id FROM news_resource WHERE CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) + THEN id IN :filterNewsIds ELSE 1 END AND @@ -38,17 +38,17 @@ WHERE THEN id IN ( SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) + WHERE topic_id IN :filterTopicIds ) ELSE 1 END ORDER BY publish_date DESC; -insertOrIgnoreNewsResources: +insertOrIgnoreNewsResource: INSERT OR IGNORE INTO news_resource (id, title, content, url, header_image_url, publish_date, type) VALUES (?, ?, ?, ?, ?, ?, ?); -upsertNewsResources: +upsertNewsResource: INSERT INTO news_resource (id, title, content, url, header_image_url, publish_date, type) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET @@ -65,4 +65,4 @@ VALUES (?, ?); deleteNewsResources: DELETE FROM news_resource -WHERE id IN (:ids); +WHERE id IN :ids; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq index 04349f386..f14fcb96c 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq @@ -18,11 +18,11 @@ CREATE TRIGGER news_resource_au AFTER UPDATE ON news_resource BEGIN INSERT INTO news_resource_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); END; -insertAll: -INSERT INTO news_resource_fts (news_resource_id, title, content) SELECT id, title, content FROM news_resource; +insert: +INSERT INTO news_resource_fts (news_resource_id, title, content) VALUES (:news_resource_id, :title, :content); searchAllNewsResources: -SELECT news_resource_id FROM news_resource_fts WHERE news_resource_fts MATCH :query; +SELECT news_resource_id FROM news_resource_fts WHERE news_resource_fts.title MATCH :query; getCount: SELECT count(*) FROM news_resource_fts;