From 624c788a2358ad18718824d81754f540d8f90ba5 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Mon, 28 Aug 2023 23:49:11 +0200 Subject: [PATCH] tmp --- .../graphql/dataLoaders/ChapterDataLoader.kt | 112 +++++++++++++++++- .../tachidesk/graphql/queries/ChapterQuery.kt | 87 ++++++++++---- .../tachidesk/graphql/types/MangaType.kt | 25 +++- 3 files changed, 195 insertions(+), 29 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt index 89a2af0a23..d54fd0adfc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt @@ -8,14 +8,24 @@ package suwayomi.tachidesk.graphql.dataLoaders import com.expediagroup.graphql.dataloader.KotlinDataLoader +import mu.KotlinLogging import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger +import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.addLogger +import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.queries.ChapterQuery.BaseChapterCondition +import suwayomi.tachidesk.graphql.queries.ChapterQuery.ChapterFilter +import suwayomi.tachidesk.graphql.queries.ChapterQuery.ChapterOrderBy +import suwayomi.tachidesk.graphql.queries.filter.applyOps +import suwayomi.tachidesk.graphql.server.primitives.Cursor +import suwayomi.tachidesk.graphql.server.primitives.PageInfo +import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.types.ChapterNodeList -import suwayomi.tachidesk.graphql.types.ChapterNodeList.Companion.toNodeList import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.server.JavalinSetup.future @@ -35,16 +45,106 @@ class ChapterDataLoader : KotlinDataLoader { } } +data class ChaptersContext( + val condition: Condition? = null, + val filter: ChapterFilter? = null, + val orderBy: ChapterOrderBy? = null, + val orderByType: SortOrder? = null, + val before: Cursor? = null, + val after: Cursor? = null, + val first: Int? = null, + val last: Int? = null, + val offset: Int? = null +) + class ChaptersForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "ChaptersForMangaDataLoader" - override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids, env -> + KotlinLogging.logger { }.info { "@Daniel chapters loader <-> ${env.keyContexts} <<<<-------->>>>> ${env.keyContextsList}" } future { transaction { addLogger(Slf4jSqlDebugLogger) - val chaptersByMangaId = ChapterTable.select { ChapterTable.manga inList ids } - .map { ChapterType(it) } - .groupBy { it.mangaId } - ids.map { (chaptersByMangaId[it] ?: emptyList()).toNodeList() } + val baseQuery = ChapterTable.select { (ChapterTable.manga inList ids) } + val mangaToQueryMap = ids.associateWith { baseQuery.copy().andWhere { ChapterTable.manga eq it } } + + val result = mangaToQueryMap.map { (mangaId, res) -> + val (condition, filter, orderBy, orderByType, before, after, first, last, offset) = env.keyContexts[mangaId]!! as ChaptersContext<*> + + res.applyOps(condition, filter) + + if (orderBy != null || (last != null || before != null)) { + val orderByColumn = orderBy?.column ?: ChapterTable.id + val orderType = orderByType.maybeSwap(last ?: before) + + if (orderBy == ChapterOrderBy.ID || orderBy == null) { + res.orderBy(orderByColumn to orderType) + } else { + res.orderBy( + orderByColumn to orderType, + ChapterTable.id to SortOrder.ASC + ) + } + } + + val total = res.count() + val firstResult = res.firstOrNull()?.get(ChapterTable.id)?.value + val lastResult = res.lastOrNull()?.get(ChapterTable.id)?.value + + if (after != null) { + res.andWhere { + (orderBy ?: ChapterOrderBy.ID).greater(after) + } + } else if (before != null) { + res.andWhere { + (orderBy ?: ChapterOrderBy.ID).less(before) + } + } + + if (first != null) { + res.limit(first, offset?.toLong() ?: 0) + } else if (last != null) { + res.limit(last) + } + + val queryResults = QueryResults(total, firstResult, lastResult, res.toList()) + + val getAsCursor: (ChapterType) -> Cursor = (orderBy ?: ChapterOrderBy.ID)::asCursor + + val resultsAsType = queryResults.results.map { ChapterType(it) } + + ChapterNodeList( + resultsAsType, + if (resultsAsType.isEmpty()) { + emptyList() + } else { + listOfNotNull( + resultsAsType.firstOrNull()?.let { + ChapterNodeList.ChapterEdge( + getAsCursor(it), + it + ) + }, + resultsAsType.lastOrNull()?.let { + ChapterNodeList.ChapterEdge( + getAsCursor(it), + it + ) + } + ) + }, + pageInfo = PageInfo( + hasNextPage = queryResults.lastKey != resultsAsType.lastOrNull()?.id, + hasPreviousPage = queryResults.firstKey != resultsAsType.firstOrNull()?.id, + startCursor = resultsAsType.firstOrNull()?.let { getAsCursor(it) }, + endCursor = resultsAsType.lastOrNull()?.let { getAsCursor(it) } + ), + totalCount = queryResults.total.toInt() + ) + } + + KotlinLogging.logger { }.info { "@Daniel $result" } + + result } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt index cc0e931f51..b2fabe7c1e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt @@ -12,6 +12,7 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.andWhere @@ -99,25 +100,23 @@ class ChapterQuery { } } - data class ChapterCondition( - val id: Int? = null, - val url: String? = null, - val name: String? = null, - val uploadDate: Long? = null, - val chapterNumber: Float? = null, - val scanlator: String? = null, - val mangaId: Int? = null, - val isRead: Boolean? = null, - val isBookmarked: Boolean? = null, - val lastPageRead: Int? = null, - val lastReadAt: Long? = null, - val sourceOrder: Int? = null, - val realUrl: String? = null, - val fetchedAt: Long? = null, - val isDownloaded: Boolean? = null, - val pageCount: Int? = null - ) : HasGetOp { - override fun getOp(): Op? { + abstract class BaseChapterCondition : HasGetOp { + abstract val id: Int? + abstract val url: String? + abstract val name: String? + abstract val uploadDate: Long? + abstract val chapterNumber: Float? + abstract val scanlator: String? + abstract val isRead: Boolean? + abstract val isBookmarked: Boolean? + abstract val lastPageRead: Int? + abstract val lastReadAt: Long? + abstract val sourceOrder: Int? + abstract val realUrl: String? + abstract val fetchedAt: Long? + abstract val isDownloaded: Boolean? + abstract val pageCount: Int? + open fun buildOp(): OpAnd { val opAnd = OpAnd() opAnd.eq(id, ChapterTable.id) opAnd.eq(url, ChapterTable.url) @@ -125,7 +124,6 @@ class ChapterQuery { opAnd.eq(uploadDate, ChapterTable.date_upload) opAnd.eq(chapterNumber, ChapterTable.chapter_number) opAnd.eq(scanlator, ChapterTable.scanlator) - opAnd.eq(mangaId, ChapterTable.manga) opAnd.eq(isRead, ChapterTable.isRead) opAnd.eq(isBookmarked, ChapterTable.isBookmarked) opAnd.eq(lastPageRead, ChapterTable.lastPageRead) @@ -136,7 +134,54 @@ class ChapterQuery { opAnd.eq(isDownloaded, ChapterTable.isDownloaded) opAnd.eq(pageCount, ChapterTable.pageCount) - return opAnd.op + return opAnd + } + + override fun getOp(): Op? { + return buildOp().op + } + } + + data class MangaChapterCondition( + override val id: Int? = null, + override val url: String? = null, + override val name: String? = null, + override val uploadDate: Long? = null, + override val chapterNumber: Float? = null, + override val scanlator: String? = null, + override val isRead: Boolean? = null, + override val isBookmarked: Boolean? = null, + override val lastPageRead: Int? = null, + override val lastReadAt: Long? = null, + override val sourceOrder: Int? = null, + override val realUrl: String? = null, + override val fetchedAt: Long? = null, + override val isDownloaded: Boolean? = null, + override val pageCount: Int? = null + ) : BaseChapterCondition(), HasGetOp + + data class ChapterCondition( + override val id: Int? = null, + override val url: String? = null, + override val name: String? = null, + override val uploadDate: Long? = null, + override val chapterNumber: Float? = null, + override val scanlator: String? = null, + override val isRead: Boolean? = null, + override val isBookmarked: Boolean? = null, + override val lastPageRead: Int? = null, + override val lastReadAt: Long? = null, + override val sourceOrder: Int? = null, + override val realUrl: String? = null, + override val fetchedAt: Long? = null, + override val isDownloaded: Boolean? = null, + override val pageCount: Int? = null, + val mangaId: Int? = null + ) : BaseChapterCondition(), HasGetOp { + override fun buildOp(): OpAnd { + val opAnd = super.buildOp() + opAnd.eq(mangaId, ChapterTable.manga) + return opAnd } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt index c20f23243b..5ae5d09541 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt @@ -9,7 +9,13 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment +import mu.KotlinLogging import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.SortOrder +import suwayomi.tachidesk.graphql.dataLoaders.ChaptersContext +import suwayomi.tachidesk.graphql.queries.ChapterQuery.ChapterFilter +import suwayomi.tachidesk.graphql.queries.ChapterQuery.ChapterOrderBy +import suwayomi.tachidesk.graphql.queries.ChapterQuery.MangaChapterCondition import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge import suwayomi.tachidesk.graphql.server.primitives.Node @@ -79,8 +85,23 @@ class MangaType( dataClass.chaptersLastFetchedAt ) - fun chapters(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("ChaptersForMangaDataLoader", id) + fun chapters( + dataFetchingEnvironment: DataFetchingEnvironment, + condition: MangaChapterCondition? = null, + filter: ChapterFilter? = null, + orderBy: ChapterOrderBy? = null, + orderByType: SortOrder? = null, + before: Cursor? = null, + after: Cursor? = null, + first: Int? = null, + last: Int? = null, + offset: Int? = null + ): CompletableFuture { + val context = ChaptersContext(condition, filter, orderBy, orderByType, before, after, first, last, offset) + KotlinLogging.logger { }.info { "@Daniel $context" } + val dataLoader = dataFetchingEnvironment.getDataLoader("ChaptersForMangaDataLoader") + return dataLoader.load(id, context) +// return dataFetchingEnvironment.getValueFromDataLoader("ChaptersForMangaDataLoader", id) } fun age(): Long? {