--- a/app/src/androidTest/java/com/geekorum/ttrss/data/migrations/ArticlesDatabaseMigrationTest.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/androidTest/java/com/geekorum/ttrss/data/migrations/ArticlesDatabaseMigrationTest.kt Sat May 09 22:38:05 2020 -0400
@@ -249,6 +249,15 @@
}
}
+ private fun assertMigration12To13DataIntegrity(db: SupportSQLiteDatabase) {
+ assertMigration7To8DataIntegrity(db)
+ db.query("SELECT * FROM ${Tables.ARTICLES_TAGS}").use {
+ assertThat(it.count).isEqualTo(1)
+ it.moveToFirst()
+ assertThat(it.getValue<String>("tag")).isEqualTo("article tags")
+ }
+ }
+
private fun createSomeArticlesFromVersion8(db: SupportSQLiteDatabase) {
var values = contentValuesOf(
ArticlesContract.Category.TITLE to "category",
@@ -416,6 +425,24 @@
}
}
+ @Test
+ fun migrate12To13() {
+ helper.createDatabase(TEST_DB, 12).use {
+ // db has schema version 8. insert some contentData using SQL queries.
+ // You cannot use DAO classes because they expect the latest schema.
+ // as our schema for this migration doesn't change much from the previous
+ // we can reuse the same function
+ createSomeArticlesFromVersion10(it)
+ }
+
+ helper.runMigrationsAndValidate(TEST_DB, 13, true,
+ *ALL_MIGRATIONS.toTypedArray()).use {
+ // MigrationTestHelper automatically verifies the schema changes,
+ // but you need to validate that the contentData was migrated properly.
+ assertMigration12To13DataIntegrity(it)
+ }
+ }
+
private inline fun <reified T> Cursor.getValue(columnName: String) : T {
val index = getColumnIndexOrThrow(columnName)
--- a/app/src/androidTest/java/com/geekorum/ttrss/sync/workers/mocks.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/androidTest/java/com/geekorum/ttrss/sync/workers/mocks.kt Sat May 09 22:38:05 2020 -0400
@@ -23,6 +23,7 @@
import com.geekorum.ttrss.data.AccountInfo
import com.geekorum.ttrss.data.Article
import com.geekorum.ttrss.data.ArticleWithAttachments
+import com.geekorum.ttrss.data.ArticlesTags
import com.geekorum.ttrss.data.Attachment
import com.geekorum.ttrss.data.Category
import com.geekorum.ttrss.data.Feed
@@ -75,6 +76,7 @@
private val articles = mutableListOf<Article>()
private val attachments = mutableListOf<Attachment>()
private val transactions = mutableListOf<Transaction>()
+ private val articlesTags = mutableListOf<ArticlesTags>()
override suspend fun <R> runInTransaction(block: suspend () -> R) {
block()
@@ -132,6 +134,10 @@
this.articles.addAll(articles)
}
+ override suspend fun insertArticleTags(articlesTags: List<ArticlesTags>) {
+ this.articlesTags.addAll(articlesTags)
+ }
+
override suspend fun updateArticle(article: Article) {
val present = articles.first {
it.id == article.id
--- a/app/src/main/java/com/geekorum/ttrss/data/ArticlesDatabase.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/data/ArticlesDatabase.kt Sat May 09 22:38:05 2020 -0400
@@ -24,9 +24,9 @@
import androidx.room.RoomDatabase
import com.geekorum.ttrss.providers.PurgeArticlesDao
-@Database(entities = [Article::class, ArticleFTS::class, Attachment::class,
+@Database(entities = [Article::class, ArticleFTS::class, ArticlesTags::class, Attachment::class,
Category::class, Feed::class, Transaction::class, AccountInfo::class],
- version = 12)
+ version = 13)
abstract class ArticlesDatabase : RoomDatabase() {
abstract fun articleDao(): ArticleDao
abstract fun accountInfoDao(): AccountInfoDao
@@ -45,5 +45,6 @@
const val TRANSACTIONS = "transactions"
const val FEEDS = "feeds"
const val CATEGORIES = "categories"
+ const val ARTICLES_TAGS = "articles_tags"
}
}
--- a/app/src/main/java/com/geekorum/ttrss/data/SynchronizationDao.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/data/SynchronizationDao.kt Sat May 09 22:38:05 2020 -0400
@@ -89,6 +89,9 @@
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract suspend fun insertArticles(dataArticles: List<Article>)
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ abstract suspend fun insertArticlesTags(articlesTags: List<ArticlesTags>)
+
@Update(entity = Article::class)
abstract suspend fun updateArticlesMetadata(metadata: List<Metadata>)
--- a/app/src/main/java/com/geekorum/ttrss/data/Types.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/data/Types.kt Sat May 09 22:38:05 2020 -0400
@@ -26,6 +26,8 @@
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Fts4
+import androidx.room.Index
+import androidx.room.Junction
import androidx.room.PrimaryKey
import androidx.room.Relation
import java.text.DateFormat
@@ -234,6 +236,28 @@
var unreadCount: Int = 0
)
+@Entity(tableName = "articles_tags", primaryKeys = ["tag", "article_id"],
+ foreignKeys = [ForeignKey(
+ entity = Article::class,
+ parentColumns = ["_id"],
+ childColumns = ["article_id"],
+ onDelete = ForeignKey.CASCADE
+ )])
+data class ArticlesTags(
+ @ColumnInfo(name = "article_id", index = true)
+ val articleId: Long,
+ @ColumnInfo(index = true)
+ val tag: String
+)
+
+data class TagWithArticles(
+ val tag: String,
+// val articleId: Long,
+ @Relation(parentColumn = "tag", entityColumn = "_id",
+ associateBy = Junction(ArticlesTags::class, entityColumn = "article_id"))
+ val article: List<Article>
+)
+
@Entity(tableName = "feeds",
foreignKeys = [ForeignKey(entity = Category::class,
--- a/app/src/main/java/com/geekorum/ttrss/data/migrations/ArticlesDatabase.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/data/migrations/ArticlesDatabase.kt Sat May 09 22:38:05 2020 -0400
@@ -504,6 +504,47 @@
}
+/**
+ * This migration adds a relation table for articles_tags
+ */
+object MigrationFrom12To13 : Migration(12, 13) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ addArticleTagsTable(database)
+ migrateArticleTags(database)
+ }
+
+ private fun addArticleTagsTable(database: SupportSQLiteDatabase) {
+ with(database) {
+ execSQL("""CREATE TABLE IF NOT EXISTS `articles_tags` (
+ |`article_id` INTEGER NOT NULL,
+ |`tag` TEXT NOT NULL,
+ |PRIMARY KEY(`tag`, `article_id`)
+ |FOREIGN KEY(`article_id`) REFERENCES `articles`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE
+ |)""".trimMargin())
+ execSQL("CREATE INDEX IF NOT EXISTS `index_articles_tags_article_id` ON `articles_tags` (`article_id`)")
+ execSQL("CREATE INDEX IF NOT EXISTS `index_articles_tags_tag` ON `articles_tags` (`tag`)")
+ }
+ }
+
+ private fun migrateArticleTags(database: SupportSQLiteDatabase) {
+ with(database) {
+ query("SELECT _id, tags FROM articles").use {
+ while (it.moveToNext()) {
+ val articleId = it.getLong(0)
+ val tags = it.getString(1)
+ val tagsList = tags.split(",")
+ .map(String::trim)
+ .filter(String::isNotEmpty)
+ .distinct()
+ for (tag in tagsList) {
+ execSQL("INSERT INTO `articles_tags` (`article_id`, `tag`) VALUES (?, ?)", arrayOf(articleId, tag))
+ }
+ }
+ }
+ }
+ }
+}
+
internal val ALL_MIGRATIONS = listOf(MigrationFrom1To2,
MigrationFrom2To3,
MigrationFrom3To4,
@@ -514,4 +555,5 @@
MigrationFrom8To9,
MigrationFrom9To10,
MigrationFrom10To11,
- MigrationFrom11To12)
+ MigrationFrom11To12,
+ MigrationFrom12To13)
--- a/app/src/main/java/com/geekorum/ttrss/data/plugins/SynchronizationFacade.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/data/plugins/SynchronizationFacade.kt Sat May 09 22:38:05 2020 -0400
@@ -25,6 +25,7 @@
import com.geekorum.ttrss.data.AccountInfoDao
import com.geekorum.ttrss.data.Article
import com.geekorum.ttrss.data.ArticlesDatabase
+import com.geekorum.ttrss.data.ArticlesTags
import com.geekorum.ttrss.data.Attachment
import com.geekorum.ttrss.data.Category
import com.geekorum.ttrss.data.Feed
@@ -62,6 +63,9 @@
override suspend fun getRandomArticleFromFeed(feedId: Long): Article? = synchronizationDao.getArticleFromFeed(feedId)
override suspend fun insertArticles(articles: List<Article>) = synchronizationDao.insertArticles(articles)
+ override suspend fun insertArticleTags(articlesTags: List<ArticlesTags>) {
+ synchronizationDao.insertArticlesTags(articlesTags)
+ }
override suspend fun getCategories(): List<Category> = synchronizationDao.getAllCategories()
--- a/app/src/main/java/com/geekorum/ttrss/sync/DatabaseService.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/sync/DatabaseService.kt Sat May 09 22:38:05 2020 -0400
@@ -22,6 +22,7 @@
import com.geekorum.ttrss.data.AccountInfo
import com.geekorum.ttrss.data.Article
+import com.geekorum.ttrss.data.ArticlesTags
import com.geekorum.ttrss.data.Attachment
import com.geekorum.ttrss.data.Category
import com.geekorum.ttrss.data.Feed
@@ -49,6 +50,7 @@
suspend fun getArticle(id: Long): Article?
suspend fun getRandomArticleFromFeed(feedId: Long): Article?
suspend fun insertArticles(articles: List<Article>)
+ suspend fun insertArticleTags(articlesTags: List<ArticlesTags>)
suspend fun updateArticle(article: Article)
suspend fun getLatestArticleId(): Long?
suspend fun getLatestArticleIdFromFeed(feedId: Long): Long?
--- a/app/src/main/java/com/geekorum/ttrss/sync/workers/CollectNewArticlesWorker.kt Wed May 13 11:26:44 2020 -0400
+++ b/app/src/main/java/com/geekorum/ttrss/sync/workers/CollectNewArticlesWorker.kt Sat May 09 22:38:05 2020 -0400
@@ -32,6 +32,7 @@
import com.geekorum.ttrss.core.CoroutineDispatchersProvider
import com.geekorum.ttrss.data.Article
import com.geekorum.ttrss.data.ArticleWithAttachments
+import com.geekorum.ttrss.data.ArticlesTags
import com.geekorum.ttrss.htmlparsers.ImageUrlExtractor
import com.geekorum.ttrss.network.ApiService
import com.geekorum.ttrss.sync.BackgroundDataUsageManager
@@ -156,7 +157,15 @@
}
private suspend fun insertArticles(articles: List<ArticleWithAttachments>) {
- databaseService.insertArticles(articles.map { it.article })
+ val articlesOnly = articles.map { it.article }
+ databaseService.insertArticles(articlesOnly)
+ val articlesTags = articlesOnly.flatMap {
+ val tags = it.tags.split(",").map(String::trim)
+ tags.map {tag ->
+ ArticlesTags(it.id, tag)
+ }
+ }
+ databaseService.insertArticleTags(articlesTags)
val attachments = articles.flatMap { it.attachments }
databaseService.insertAttachments(attachments)
}