From 671466a737ae47645bf4b0f93f01c6e26bf6900f Mon Sep 17 00:00:00 2001 From: Syer10 Date: Thu, 6 Apr 2023 21:53:30 -0400 Subject: [PATCH] Complete CategoryQuery --- .../graphql/queries/CategoryQuery.kt | 178 +++++++++++++++--- .../tachidesk/graphql/queries/MangaQuery.kt | 10 +- 2 files changed, 158 insertions(+), 30 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt index 7e47dc20..8374d7cf 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt @@ -9,12 +9,26 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.sql.Column +import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter +import suwayomi.tachidesk.graphql.queries.filter.Filter +import suwayomi.tachidesk.graphql.queries.filter.IntFilter +import suwayomi.tachidesk.graphql.queries.filter.OpAnd +import suwayomi.tachidesk.graphql.queries.filter.StringFilter +import suwayomi.tachidesk.graphql.queries.filter.andFilterWithCompare +import suwayomi.tachidesk.graphql.queries.filter.andFilterWithCompareEntity +import suwayomi.tachidesk.graphql.queries.filter.andFilterWithCompareString +import suwayomi.tachidesk.graphql.queries.filter.getOp +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.types.CategoryNodeList -import suwayomi.tachidesk.graphql.types.CategoryNodeList.Companion.toNodeList import suwayomi.tachidesk.graphql.types.CategoryType import suwayomi.tachidesk.manga.model.table.CategoryTable import java.util.concurrent.CompletableFuture @@ -37,43 +51,157 @@ class CategoryQuery { return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id) } - enum class CategorySort { + enum class CategoryOrderBy { ID, NAME, ORDER } - data class CategoriesQueryInput( - val sort: CategorySort? = null, - val sortOrder: SortOrder? = null, - val ids: List? = null, - val query: String? = null - ) + private fun getAsCursor(orderBy: CategoryOrderBy?, type: CategoryType): Cursor { + val value = when (orderBy) { + CategoryOrderBy.ID, null -> type.id.toString() + CategoryOrderBy.NAME -> type.name + CategoryOrderBy.ORDER -> type.order.toString() + } + return Cursor(value) + } - fun categories(input: CategoriesQueryInput? = null): CategoryNodeList { - val results = transaction { + data class CategoryCondition( + val id: Int? = null, + val order: Int? = null, + val name: String? = null, + val default: Boolean? = null + ) { + fun getOp(): Op? { + val opAnd = OpAnd() + fun eq(value: T?, column: Column) = opAnd.andWhere(value) { column eq it } + fun > eq(value: T?, column: Column>) = opAnd.andWhere(value) { column eq it } + eq(id, CategoryTable.id) + eq(order, CategoryTable.order) + eq(name, CategoryTable.name) + eq(default, CategoryTable.isDefault) + + return opAnd.op + } + } + + data class CategoryFilter( + val id: IntFilter? = null, + val order: IntFilter? = null, + val name: StringFilter? = null, + val default: BooleanFilter? = null, + override val and: List? = null, + override val or: List? = null, + override val not: CategoryFilter? = null + ) : Filter { + override fun getOpList(): List> { + return listOfNotNull( + andFilterWithCompareEntity(CategoryTable.id, id), + andFilterWithCompare(CategoryTable.order, order), + andFilterWithCompareString(CategoryTable.name, name), + andFilterWithCompare(CategoryTable.isDefault, default), + ) + } + } + + fun categories( + condition: CategoryCondition? = null, + filter: CategoryFilter? = null, + orderBy: CategoryOrderBy? = null, + orderByType: SortOrder? = null, + before: Cursor? = null, + after: Cursor? = null, + first: Int? = null, + last: Int? = null, + offset: Int? = null + ): CategoryNodeList { + val queryResults = transaction { val res = CategoryTable.selectAll() - if (input != null) { - if (input.ids != null) { - res.andWhere { CategoryTable.id inList input.ids } + val conditionOp = condition?.getOp() + if (conditionOp != null) { + res.andWhere { conditionOp } + } + val filterOp = filter?.getOp() + if (filterOp != null) { + res.andWhere { filterOp } + } + if (orderBy != null || (last != null || before != null)) { + val orderByColumn = when (orderBy) { + CategoryOrderBy.ID, null -> CategoryTable.id + CategoryOrderBy.NAME -> CategoryTable.name + CategoryOrderBy.ORDER -> CategoryTable.order } - if (!input.query.isNullOrEmpty()) { - res.andWhere { CategoryTable.name like input.query } + val orderType = if (last != null || before != null) { + when (orderByType) { + SortOrder.ASC -> SortOrder.DESC + SortOrder.DESC -> SortOrder.ASC + SortOrder.ASC_NULLS_FIRST -> SortOrder.DESC_NULLS_LAST + SortOrder.DESC_NULLS_FIRST -> SortOrder.ASC_NULLS_LAST + SortOrder.ASC_NULLS_LAST -> SortOrder.DESC_NULLS_FIRST + SortOrder.DESC_NULLS_LAST -> SortOrder.ASC_NULLS_FIRST + null -> SortOrder.DESC + } + } else { + orderByType ?: SortOrder.ASC } - val orderBy = when (input.sort) { - CategorySort.ID -> CategoryTable.id - CategorySort.NAME -> CategoryTable.name - CategorySort.ORDER, null -> CategoryTable.order - } - res.orderBy(orderBy, order = input.sortOrder ?: SortOrder.ASC) - } else { - res.orderBy(CategoryTable.order) + res.orderBy(orderByColumn, order = orderType) } - res.toList() + val total = res.count() + val firstResult = res.first()[CategoryTable.id].value + val lastResult = res.last()[CategoryTable.id].value + + if (after != null) { + when (orderBy) { + CategoryOrderBy.ID, null -> res.andWhere { + CategoryTable.id greater after.value.toInt() + } + CategoryOrderBy.NAME -> res.andWhere { + CategoryTable.name greater after.value + } + CategoryOrderBy.ORDER -> res.andWhere { + CategoryTable.order greater after.value.toInt() + } + } + } else if (before != null) { + when (orderBy) { + CategoryOrderBy.ID, null -> res.andWhere { + CategoryTable.id less before.value.toInt() + } + CategoryOrderBy.NAME -> res.andWhere { + CategoryTable.name less before.value + } + CategoryOrderBy.ORDER -> res.andWhere { + CategoryTable.order less before.value.toInt() + } + } + } + + if (first != null) { + res.limit(first, offset?.toLong() ?: 0) + } else if (last != null) { + res.limit(last) + } + + QueryResults(total, firstResult, lastResult, res.toList()) } - return results.map { CategoryType(it) }.toNodeList() + val resultsAsType = queryResults.results.map { CategoryType(it) } + + return CategoryNodeList( + resultsAsType, + CategoryNodeList.CategoryEdges( + cursor = getAsCursor(orderBy, resultsAsType.last()), + node = resultsAsType.last() + ), + pageInfo = PageInfo( + hasNextPage = queryResults.lastKey != resultsAsType.last().id, + hasPreviousPage = queryResults.firstKey != resultsAsType.first().id, + startCursor = getAsCursor(orderBy, resultsAsType.first()), + endCursor = getAsCursor(orderBy, resultsAsType.last()) + ), + totalCount = queryResults.total.toInt() + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt index f2622df6..f2b487ca 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt @@ -63,12 +63,12 @@ class MangaQuery { LAST_FETCHED_AT } - private fun getAsCursor(orderBy: MangaOrderBy?, manga: MangaType): Cursor { + private fun getAsCursor(orderBy: MangaOrderBy?, type: MangaType): Cursor { val value = when (orderBy) { - MangaOrderBy.ID, null -> manga.id.toString() - MangaOrderBy.TITLE -> manga.title - MangaOrderBy.IN_LIBRARY_AT -> manga.inLibraryAt.toString() - MangaOrderBy.LAST_FETCHED_AT -> manga.lastFetchedAt.toString() + MangaOrderBy.ID, null -> type.id.toString() + MangaOrderBy.TITLE -> type.title + MangaOrderBy.IN_LIBRARY_AT -> type.inLibraryAt.toString() + MangaOrderBy.LAST_FETCHED_AT -> type.lastFetchedAt.toString() } return Cursor(value) }