From 783787e5141ebc3e469d3a27c504ce7c210c0a7b Mon Sep 17 00:00:00 2001 From: akabhirav <108609220+akabhirav@users.noreply.github.com> Date: Tue, 21 Feb 2023 04:47:45 +0530 Subject: [PATCH] Send last read chapter in Mangas in Category API (#507) * Send last read chapter with manga * optimize query * introduce new field for better performance --- .../tachidesk/manga/impl/CategoryManga.kt | 64 +++++++++---------- .../manga/model/dataclass/MangaDataClass.kt | 1 + 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt index 8e1a2f76..a1cc808d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt @@ -10,14 +10,16 @@ package suwayomi.tachidesk.manga.impl import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.alias import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.count import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.leftJoin +import org.jetbrains.exposed.sql.max import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update -import org.jetbrains.exposed.sql.wrapAsExpression import suwayomi.tachidesk.manga.impl.Category.DEFAULT_CATEGORY_ID import suwayomi.tachidesk.manga.impl.util.lang.isEmpty import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass @@ -61,46 +63,40 @@ object CategoryManga { * list of mangas that belong to a category */ fun getCategoryMangaList(categoryId: Int): List { - val unreadExpression = wrapAsExpression( - ChapterTable - .slice(ChapterTable.id.count()) - .select { (MangaTable.id eq ChapterTable.manga) and (ChapterTable.isRead eq false) } - ) - val downloadExpression = wrapAsExpression( - ChapterTable - .slice(ChapterTable.id.count()) - .select { (MangaTable.id eq ChapterTable.manga) and (ChapterTable.isDownloaded eq true) } - ) - val chapterCountExpression = wrapAsExpression( - ChapterTable - .slice(ChapterTable.id.count()) - .select { (MangaTable.id eq ChapterTable.manga) } - ) - - val selectedColumns = MangaTable.columns + unreadExpression + downloadExpression + chapterCountExpression + // Select the required columns from the MangaTable and add the aggregate functions to compute unread, download, and chapter counts + val unreadCountEx = ChapterTable.isRead.count().alias("unread_count") + val downloadedCount = ChapterTable.isDownloaded.count().alias("download_count") + val chapterCount = ChapterTable.id.count().alias("chapter_count") + val lastReadAt = ChapterTable.lastReadAt.max().alias("last_read_at") + val selectedColumns = MangaTable.columns + unreadCountEx + downloadedCount + chapterCount + lastReadAt val transform: (ResultRow) -> MangaDataClass = { + // Map the data from the result row to the MangaDataClass val dataClass = MangaTable.toDataClass(it) - dataClass.unreadCount = it[unreadExpression] - dataClass.downloadCount = it[downloadExpression] - dataClass.chapterCount = it[chapterCountExpression] + dataClass.lastReadAt = it[lastReadAt] + dataClass.unreadCount = it[unreadCountEx] + dataClass.downloadCount = it[downloadedCount] + dataClass.chapterCount = it[chapterCount] dataClass } - if (categoryId == DEFAULT_CATEGORY_ID) { - return transaction { - MangaTable - .slice(selectedColumns) - .select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) } - .map(transform) - } - } - return transaction { - CategoryMangaTable.innerJoin(MangaTable) - .slice(selectedColumns) - .select { (MangaTable.inLibrary eq true) and (CategoryMangaTable.category eq categoryId) } - .map(transform) + // Fetch data from the MangaTable and join with the CategoryMangaTable, if a category is specified + val query = if (categoryId == DEFAULT_CATEGORY_ID) { + MangaTable + .leftJoin(ChapterTable, { MangaTable.id }, { ChapterTable.manga }) + .slice(columns = selectedColumns) + .select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) } + } else { + MangaTable + .innerJoin(CategoryMangaTable) + .leftJoin(ChapterTable, { MangaTable.id }, { ChapterTable.manga }) + .slice(columns = selectedColumns) + .select { (MangaTable.inLibrary eq true) and (CategoryMangaTable.category eq categoryId) } + } + + // Join with the ChapterTable to fetch the last read chapter for each manga + query.groupBy(*MangaTable.columns.toTypedArray()).map(transform) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt index 84a89339..05ea72dd 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt @@ -45,6 +45,7 @@ data class MangaDataClass( var unreadCount: Long? = null, var downloadCount: Long? = null, var chapterCount: Long? = null, + var lastReadAt: Long? = null, var lastChapterRead: ChapterDataClass? = null, val age: Long? = if (lastFetchedAt == null) 0 else Instant.now().epochSecond.minus(lastFetchedAt),