Simplify queries
This commit is contained in:
@@ -9,25 +9,29 @@ 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.SqlExpressionBuilder.greater
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
|
||||
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.HasGetOp
|
||||
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.queries.filter.applyOps
|
||||
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
||||
import suwayomi.tachidesk.graphql.server.primitives.OrderBy
|
||||
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.CategoryNodeList
|
||||
import suwayomi.tachidesk.graphql.types.CategoryType
|
||||
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||
@@ -51,19 +55,35 @@ class CategoryQuery {
|
||||
return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id)
|
||||
}
|
||||
|
||||
enum class CategoryOrderBy {
|
||||
ID,
|
||||
NAME,
|
||||
ORDER
|
||||
}
|
||||
enum class CategoryOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<CategoryType> {
|
||||
ID(CategoryTable.id),
|
||||
NAME(CategoryTable.name),
|
||||
ORDER(CategoryTable.order);
|
||||
|
||||
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()
|
||||
override fun greater(cursor: Cursor): Op<Boolean> {
|
||||
return when (this) {
|
||||
ID -> CategoryTable.id greater cursor.value.toInt()
|
||||
NAME -> CategoryTable.name greater cursor.value
|
||||
ORDER -> CategoryTable.order greater cursor.value.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun less(cursor: Cursor): Op<Boolean> {
|
||||
return when (this) {
|
||||
ID -> CategoryTable.id less cursor.value.toInt()
|
||||
NAME -> CategoryTable.name less cursor.value
|
||||
ORDER -> CategoryTable.order less cursor.value.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun asCursor(type: CategoryType): Cursor {
|
||||
val value = when (this) {
|
||||
ID -> type.id.toString()
|
||||
NAME -> type.name
|
||||
ORDER -> type.order.toString()
|
||||
}
|
||||
return Cursor(value)
|
||||
}
|
||||
return Cursor(value)
|
||||
}
|
||||
|
||||
data class CategoryCondition(
|
||||
@@ -71,15 +91,13 @@ class CategoryQuery {
|
||||
val order: Int? = null,
|
||||
val name: String? = null,
|
||||
val default: Boolean? = null
|
||||
) {
|
||||
fun getOp(): Op<Boolean>? {
|
||||
) : HasGetOp {
|
||||
override fun getOp(): Op<Boolean>? {
|
||||
val opAnd = OpAnd()
|
||||
fun <T> eq(value: T?, column: Column<T>) = opAnd.andWhere(value) { column eq it }
|
||||
fun <T : Comparable<T>> eq(value: T?, column: Column<EntityID<T>>) = opAnd.andWhere(value) { column eq it }
|
||||
eq(id, CategoryTable.id)
|
||||
eq(order, CategoryTable.order)
|
||||
eq(name, CategoryTable.name)
|
||||
eq(default, CategoryTable.isDefault)
|
||||
opAnd.eq(id, CategoryTable.id)
|
||||
opAnd.eq(order, CategoryTable.order)
|
||||
opAnd.eq(name, CategoryTable.name)
|
||||
opAnd.eq(default, CategoryTable.isDefault)
|
||||
|
||||
return opAnd.op
|
||||
}
|
||||
@@ -118,63 +136,26 @@ class CategoryQuery {
|
||||
val queryResults = transaction {
|
||||
val res = CategoryTable.selectAll()
|
||||
|
||||
val conditionOp = condition?.getOp()
|
||||
if (conditionOp != null) {
|
||||
res.andWhere { conditionOp }
|
||||
}
|
||||
val filterOp = filter?.getOp()
|
||||
if (filterOp != null) {
|
||||
res.andWhere { filterOp }
|
||||
}
|
||||
res.applyOps(condition, filter)
|
||||
|
||||
if (orderBy != null || (last != null || before != null)) {
|
||||
val orderByColumn = when (orderBy) {
|
||||
CategoryOrderBy.ID, null -> CategoryTable.id
|
||||
CategoryOrderBy.NAME -> CategoryTable.name
|
||||
CategoryOrderBy.ORDER -> CategoryTable.order
|
||||
}
|
||||
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 orderByColumn = orderBy?.column ?: CategoryTable.id
|
||||
val orderType = orderByType.maybeSwap(last ?: before)
|
||||
|
||||
res.orderBy(orderByColumn, order = orderType)
|
||||
}
|
||||
|
||||
val total = res.count()
|
||||
val firstResult = res.first()[CategoryTable.id].value
|
||||
val lastResult = res.last()[CategoryTable.id].value
|
||||
val firstResult = res.firstOrNull()?.get(CategoryTable.id)?.value
|
||||
val lastResult = res.lastOrNull()?.get(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()
|
||||
}
|
||||
res.andWhere {
|
||||
(orderBy ?: CategoryOrderBy.ID).greater(after)
|
||||
}
|
||||
} 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()
|
||||
}
|
||||
res.andWhere {
|
||||
(orderBy ?: CategoryOrderBy.ID).less(before)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +168,8 @@ class CategoryQuery {
|
||||
QueryResults(total, firstResult, lastResult, res.toList())
|
||||
}
|
||||
|
||||
val getAsCursor: (CategoryType) -> Cursor = (orderBy ?: CategoryOrderBy.ID)::asCursor
|
||||
|
||||
val resultsAsType = queryResults.results.map { CategoryType(it) }
|
||||
|
||||
return CategoryNodeList(
|
||||
@@ -194,26 +177,26 @@ class CategoryQuery {
|
||||
if (resultsAsType.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(
|
||||
resultsAsType.first().let {
|
||||
listOfNotNull(
|
||||
resultsAsType.firstOrNull()?.let {
|
||||
CategoryNodeList.CategoryEdge(
|
||||
getAsCursor(orderBy, it),
|
||||
getAsCursor(it),
|
||||
it
|
||||
)
|
||||
},
|
||||
resultsAsType.last().let {
|
||||
resultsAsType.lastOrNull()?.let {
|
||||
CategoryNodeList.CategoryEdge(
|
||||
getAsCursor(orderBy, it),
|
||||
getAsCursor(it),
|
||||
it
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
pageInfo = PageInfo(
|
||||
hasNextPage = queryResults.lastKey != resultsAsType.last().id,
|
||||
hasPreviousPage = queryResults.firstKey != resultsAsType.first().id,
|
||||
startCursor = resultsAsType.firstOrNull()?.let { getAsCursor(orderBy, it) },
|
||||
endCursor = resultsAsType.lastOrNull()?.let { getAsCursor(orderBy, it) }
|
||||
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()
|
||||
)
|
||||
|
||||
@@ -9,16 +9,18 @@ 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.SqlExpressionBuilder.greater
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
|
||||
import org.jetbrains.exposed.sql.andWhere
|
||||
import org.jetbrains.exposed.sql.select
|
||||
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.HasGetOp
|
||||
import suwayomi.tachidesk.graphql.queries.filter.IntFilter
|
||||
import suwayomi.tachidesk.graphql.queries.filter.LongFilter
|
||||
import suwayomi.tachidesk.graphql.queries.filter.OpAnd
|
||||
@@ -26,10 +28,13 @@ 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.queries.filter.applyOps
|
||||
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
||||
import suwayomi.tachidesk.graphql.server.primitives.OrderBy
|
||||
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.CategoryType
|
||||
import suwayomi.tachidesk.graphql.types.MangaNodeList
|
||||
import suwayomi.tachidesk.graphql.types.MangaType
|
||||
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
||||
@@ -56,21 +61,39 @@ class MangaQuery {
|
||||
return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
|
||||
}
|
||||
|
||||
enum class MangaOrderBy {
|
||||
ID,
|
||||
TITLE,
|
||||
IN_LIBRARY_AT,
|
||||
LAST_FETCHED_AT
|
||||
}
|
||||
enum class MangaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<MangaType> {
|
||||
ID(MangaTable.id),
|
||||
TITLE(MangaTable.title),
|
||||
IN_LIBRARY_AT(MangaTable.inLibraryAt),
|
||||
LAST_FETCHED_AT(MangaTable.lastFetchedAt);
|
||||
|
||||
private fun getAsCursor(orderBy: MangaOrderBy?, type: MangaType): Cursor {
|
||||
val value = when (orderBy) {
|
||||
MangaOrderBy.ID, null -> type.id.toString()
|
||||
MangaOrderBy.TITLE -> type.title
|
||||
MangaOrderBy.IN_LIBRARY_AT -> type.inLibraryAt.toString()
|
||||
MangaOrderBy.LAST_FETCHED_AT -> type.lastFetchedAt.toString()
|
||||
override fun greater(cursor: Cursor): Op<Boolean> {
|
||||
return when (this) {
|
||||
ID -> MangaTable.id greater cursor.value.toInt()
|
||||
TITLE -> MangaTable.title greater cursor.value
|
||||
IN_LIBRARY_AT -> MangaTable.inLibraryAt greater cursor.value.toLong()
|
||||
LAST_FETCHED_AT -> MangaTable.lastFetchedAt greater cursor.value.toLong()
|
||||
}
|
||||
}
|
||||
|
||||
override fun less(cursor: Cursor): Op<Boolean> {
|
||||
return when (this) {
|
||||
ID -> MangaTable.id less cursor.value.toInt()
|
||||
TITLE -> MangaTable.title less cursor.value
|
||||
IN_LIBRARY_AT -> MangaTable.inLibraryAt less cursor.value.toLong()
|
||||
LAST_FETCHED_AT -> MangaTable.lastFetchedAt less cursor.value.toLong()
|
||||
}
|
||||
}
|
||||
|
||||
override fun asCursor(type: MangaType): Cursor {
|
||||
val value = when (this) {
|
||||
ID -> type.id.toString()
|
||||
TITLE -> type.title
|
||||
IN_LIBRARY_AT -> type.inLibraryAt.toString()
|
||||
LAST_FETCHED_AT -> type.lastFetchedAt.toString()
|
||||
}
|
||||
return Cursor(value)
|
||||
}
|
||||
return Cursor(value)
|
||||
}
|
||||
|
||||
data class MangaCondition(
|
||||
@@ -88,29 +111,27 @@ class MangaQuery {
|
||||
val inLibrary: Boolean? = null,
|
||||
val inLibraryAt: Long? = null,
|
||||
val realUrl: String? = null,
|
||||
var lastFetchedAt: Long? = null,
|
||||
var chaptersLastFetchedAt: Long? = null
|
||||
) {
|
||||
fun getOp(): Op<Boolean>? {
|
||||
val lastFetchedAt: Long? = null,
|
||||
val chaptersLastFetchedAt: Long? = null
|
||||
) : HasGetOp {
|
||||
override fun getOp(): Op<Boolean>? {
|
||||
val opAnd = OpAnd()
|
||||
fun <T> eq(value: T?, column: Column<T>) = opAnd.andWhere(value) { column eq it }
|
||||
fun <T : Comparable<T>> eq(value: T?, column: Column<EntityID<T>>) = opAnd.andWhere(value) { column eq it }
|
||||
eq(id, MangaTable.id)
|
||||
eq(sourceId, MangaTable.sourceReference)
|
||||
eq(url, MangaTable.url)
|
||||
eq(title, MangaTable.title)
|
||||
eq(thumbnailUrl, MangaTable.thumbnail_url)
|
||||
eq(initialized, MangaTable.initialized)
|
||||
eq(artist, MangaTable.artist)
|
||||
eq(author, MangaTable.author)
|
||||
eq(description, MangaTable.description)
|
||||
eq(genre?.joinToString(), MangaTable.genre)
|
||||
eq(status?.value, MangaTable.status)
|
||||
eq(inLibrary, MangaTable.inLibrary)
|
||||
eq(inLibraryAt, MangaTable.inLibraryAt)
|
||||
eq(realUrl, MangaTable.realUrl)
|
||||
eq(lastFetchedAt, MangaTable.lastFetchedAt)
|
||||
eq(chaptersLastFetchedAt, MangaTable.chaptersLastFetchedAt)
|
||||
opAnd.eq(id, MangaTable.id)
|
||||
opAnd.eq(sourceId, MangaTable.sourceReference)
|
||||
opAnd.eq(url, MangaTable.url)
|
||||
opAnd.eq(title, MangaTable.title)
|
||||
opAnd.eq(thumbnailUrl, MangaTable.thumbnail_url)
|
||||
opAnd.eq(initialized, MangaTable.initialized)
|
||||
opAnd.eq(artist, MangaTable.artist)
|
||||
opAnd.eq(author, MangaTable.author)
|
||||
opAnd.eq(description, MangaTable.description)
|
||||
opAnd.eq(genre?.joinToString(), MangaTable.genre)
|
||||
opAnd.eq(status?.value, MangaTable.status)
|
||||
opAnd.eq(inLibrary, MangaTable.inLibrary)
|
||||
opAnd.eq(inLibraryAt, MangaTable.inLibraryAt)
|
||||
opAnd.eq(realUrl, MangaTable.realUrl)
|
||||
opAnd.eq(lastFetchedAt, MangaTable.lastFetchedAt)
|
||||
opAnd.eq(chaptersLastFetchedAt, MangaTable.chaptersLastFetchedAt)
|
||||
|
||||
return opAnd.op
|
||||
}
|
||||
@@ -131,8 +152,8 @@ class MangaQuery {
|
||||
val inLibrary: BooleanFilter? = null,
|
||||
val inLibraryAt: LongFilter? = null,
|
||||
val realUrl: StringFilter? = null,
|
||||
var lastFetchedAt: LongFilter? = null,
|
||||
var chaptersLastFetchedAt: LongFilter? = null,
|
||||
val lastFetchedAt: LongFilter? = null,
|
||||
val chaptersLastFetchedAt: LongFilter? = null,
|
||||
val category: IntFilter? = null,
|
||||
override val and: List<MangaFilter>? = null,
|
||||
override val or: List<MangaFilter>? = null,
|
||||
@@ -179,70 +200,27 @@ class MangaQuery {
|
||||
res = MangaTable.innerJoin(CategoryMangaTable)
|
||||
.select { categoryOp }
|
||||
}
|
||||
val conditionOp = condition?.getOp()
|
||||
if (conditionOp != null) {
|
||||
res.andWhere { conditionOp }
|
||||
}
|
||||
val filterOp = filter?.getOp()
|
||||
if (filterOp != null) {
|
||||
res.andWhere { filterOp }
|
||||
}
|
||||
|
||||
res.applyOps(condition, filter)
|
||||
|
||||
if (orderBy != null || (last != null || before != null)) {
|
||||
val orderByColumn = when (orderBy) {
|
||||
MangaOrderBy.ID, null -> MangaTable.id
|
||||
MangaOrderBy.TITLE -> MangaTable.title
|
||||
MangaOrderBy.IN_LIBRARY_AT -> MangaTable.inLibraryAt
|
||||
MangaOrderBy.LAST_FETCHED_AT -> MangaTable.lastFetchedAt
|
||||
}
|
||||
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 orderByColumn = orderBy?.column ?: MangaTable.id
|
||||
val orderType = orderByType.maybeSwap(last ?: before)
|
||||
|
||||
res.orderBy(orderByColumn, order = orderType)
|
||||
}
|
||||
|
||||
val total = res.count()
|
||||
val firstResult = res.first()[MangaTable.id].value
|
||||
val lastResult = res.last()[MangaTable.id].value
|
||||
val firstResult = res.firstOrNull()?.get(MangaTable.id)?.value
|
||||
val lastResult = res.lastOrNull()?.get(MangaTable.id)?.value
|
||||
|
||||
if (after != null) {
|
||||
when (orderBy) {
|
||||
MangaOrderBy.ID, null -> res.andWhere {
|
||||
MangaTable.id greater after.value.toInt()
|
||||
}
|
||||
MangaOrderBy.TITLE -> res.andWhere {
|
||||
MangaTable.title greater after.value
|
||||
}
|
||||
MangaOrderBy.IN_LIBRARY_AT -> res.andWhere {
|
||||
MangaTable.inLibraryAt greater after.value.toLong()
|
||||
}
|
||||
MangaOrderBy.LAST_FETCHED_AT -> res.andWhere {
|
||||
MangaTable.lastFetchedAt greater after.value.toLong()
|
||||
}
|
||||
res.andWhere {
|
||||
(orderBy ?: MangaOrderBy.ID).greater(after)
|
||||
}
|
||||
} else if (before != null) {
|
||||
when (orderBy) {
|
||||
MangaOrderBy.ID, null -> res.andWhere {
|
||||
MangaTable.id less before.value.toInt()
|
||||
}
|
||||
MangaOrderBy.TITLE -> res.andWhere {
|
||||
MangaTable.title less before.value
|
||||
}
|
||||
MangaOrderBy.IN_LIBRARY_AT -> res.andWhere {
|
||||
MangaTable.inLibraryAt less before.value.toLong()
|
||||
}
|
||||
MangaOrderBy.LAST_FETCHED_AT -> res.andWhere {
|
||||
MangaTable.lastFetchedAt less before.value.toLong()
|
||||
}
|
||||
res.andWhere {
|
||||
(orderBy ?: MangaOrderBy.ID).less(before)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +233,8 @@ class MangaQuery {
|
||||
QueryResults(total, firstResult, lastResult, res.toList())
|
||||
}
|
||||
|
||||
val getAsCursor: (MangaType) -> Cursor = (orderBy ?: MangaOrderBy.ID)::asCursor
|
||||
|
||||
val resultsAsType = queryResults.results.map { MangaType(it) }
|
||||
|
||||
return MangaNodeList(
|
||||
@@ -262,26 +242,26 @@ class MangaQuery {
|
||||
if (resultsAsType.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(
|
||||
resultsAsType.first().let {
|
||||
listOfNotNull(
|
||||
resultsAsType.firstOrNull()?.let {
|
||||
MangaNodeList.MangaEdge(
|
||||
getAsCursor(orderBy, it),
|
||||
getAsCursor(it),
|
||||
it
|
||||
)
|
||||
},
|
||||
resultsAsType.last().let {
|
||||
resultsAsType.lastOrNull()?.let {
|
||||
MangaNodeList.MangaEdge(
|
||||
getAsCursor(orderBy, it),
|
||||
getAsCursor(it),
|
||||
it
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
pageInfo = PageInfo(
|
||||
hasNextPage = queryResults.lastKey != resultsAsType.last().id,
|
||||
hasPreviousPage = queryResults.firstKey != resultsAsType.first().id,
|
||||
startCursor = resultsAsType.firstOrNull()?.let { getAsCursor(orderBy, it) },
|
||||
endCursor = resultsAsType.lastOrNull()?.let { getAsCursor(orderBy, it) }
|
||||
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()
|
||||
)
|
||||
|
||||
@@ -7,10 +7,13 @@ import org.jetbrains.exposed.sql.Expression
|
||||
import org.jetbrains.exposed.sql.ExpressionWithColumnType
|
||||
import org.jetbrains.exposed.sql.LikePattern
|
||||
import org.jetbrains.exposed.sql.Op
|
||||
import org.jetbrains.exposed.sql.Query
|
||||
import org.jetbrains.exposed.sql.QueryBuilder
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.wrap
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.andWhere
|
||||
import org.jetbrains.exposed.sql.not
|
||||
import org.jetbrains.exposed.sql.or
|
||||
import org.jetbrains.exposed.sql.stringParam
|
||||
@@ -68,12 +71,57 @@ class DistinctFromOp(expr1: Expression<*>, expr2: Expression<*>, not: Boolean) :
|
||||
}
|
||||
}
|
||||
|
||||
interface Filter<T : Filter<T>> {
|
||||
interface HasGetOp {
|
||||
fun getOp(): Op<Boolean>?
|
||||
}
|
||||
|
||||
fun Query.applyOps(vararg ops: HasGetOp?) {
|
||||
ops.mapNotNull { it?.getOp() }.forEach {
|
||||
andWhere { it }
|
||||
}
|
||||
}
|
||||
|
||||
interface Filter<T : Filter<T>> : HasGetOp {
|
||||
val and: List<T>?
|
||||
val or: List<T>?
|
||||
val not: T?
|
||||
|
||||
fun getOpList(): List<Op<Boolean>>
|
||||
|
||||
override fun getOp(): Op<Boolean>? {
|
||||
var op: Op<Boolean>? = null
|
||||
fun newOp(
|
||||
otherOp: Op<Boolean>?,
|
||||
operator: (Op<Boolean>, Op<Boolean>) -> Op<Boolean>
|
||||
) {
|
||||
when {
|
||||
op == null && otherOp == null -> Unit
|
||||
op == null && otherOp != null -> op = otherOp
|
||||
op != null && otherOp == null -> Unit
|
||||
op != null && otherOp != null -> op = operator(op!!, otherOp)
|
||||
}
|
||||
}
|
||||
fun andOp(andOp: Op<Boolean>?) {
|
||||
newOp(andOp, Op<Boolean>::and)
|
||||
}
|
||||
fun orOp(orOp: Op<Boolean>?) {
|
||||
newOp(orOp, Op<Boolean>::or)
|
||||
}
|
||||
getOpList().forEach {
|
||||
andOp(it)
|
||||
}
|
||||
and?.forEach {
|
||||
andOp(it.getOp())
|
||||
}
|
||||
or?.forEach {
|
||||
orOp(it.getOp())
|
||||
}
|
||||
if (not != null) {
|
||||
andOp(not!!.getOp()?.let(::not))
|
||||
}
|
||||
return op
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ScalarFilter<T> {
|
||||
@@ -220,6 +268,9 @@ class OpAnd(var op: Op<Boolean>? = null) {
|
||||
val expr = Op.build { andPart(value) }
|
||||
op = if (op == null) expr else (op!! and expr)
|
||||
}
|
||||
|
||||
fun <T> eq(value: T?, column: Column<T>) = andWhere(value) { column eq it }
|
||||
fun <T : Comparable<T>> eq(value: T?, column: Column<EntityID<T>>) = andWhere(value) { column eq it }
|
||||
}
|
||||
|
||||
fun <T : Comparable<T>> andFilterWithCompare(
|
||||
@@ -293,37 +344,3 @@ fun <T : Comparable<T>> andFilterEntity(
|
||||
}
|
||||
return opAnd.op
|
||||
}
|
||||
|
||||
fun <T : Filter<T>> Filter<T>.getOp(): Op<Boolean>? {
|
||||
var op: Op<Boolean>? = null
|
||||
fun newOp(
|
||||
otherOp: Op<Boolean>?,
|
||||
operator: (Op<Boolean>, Op<Boolean>) -> Op<Boolean>
|
||||
) {
|
||||
when {
|
||||
op == null && otherOp == null -> Unit
|
||||
op == null && otherOp != null -> op = otherOp
|
||||
op != null && otherOp == null -> Unit
|
||||
op != null && otherOp != null -> op = operator(op!!, otherOp)
|
||||
}
|
||||
}
|
||||
fun andOp(andOp: Op<Boolean>?) {
|
||||
newOp(andOp, Op<Boolean>::and)
|
||||
}
|
||||
fun orOp(orOp: Op<Boolean>?) {
|
||||
newOp(orOp, Op<Boolean>::or)
|
||||
}
|
||||
getOpList().forEach {
|
||||
andOp(it)
|
||||
}
|
||||
and?.forEach {
|
||||
andOp(it.getOp())
|
||||
}
|
||||
or?.forEach {
|
||||
orOp(it.getOp())
|
||||
}
|
||||
if (not != null) {
|
||||
andOp(not!!.getOp()?.let(::not))
|
||||
}
|
||||
return op
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package suwayomi.tachidesk.graphql.server.primitives
|
||||
|
||||
import org.jetbrains.exposed.sql.Column
|
||||
import org.jetbrains.exposed.sql.Op
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
|
||||
interface OrderBy<T> {
|
||||
val column: Column<out Comparable<*>>
|
||||
|
||||
fun asCursor(type: T): Cursor
|
||||
|
||||
fun greater(cursor: Cursor): Op<Boolean>
|
||||
|
||||
fun less(cursor: Cursor): Op<Boolean>
|
||||
}
|
||||
|
||||
|
||||
fun SortOrder?.maybeSwap(value: Any?): SortOrder {
|
||||
return if (value != null) {
|
||||
when (this) {
|
||||
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 {
|
||||
this ?: SortOrder.ASC
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user