mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-08 05:14:37 -05:00
Basically finish MangaQuery, only paging left
This commit is contained in:
@@ -9,23 +9,34 @@ package suwayomi.tachidesk.graphql.queries
|
|||||||
|
|
||||||
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
||||||
import graphql.schema.DataFetchingEnvironment
|
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.SortOrder
|
||||||
import org.jetbrains.exposed.sql.andWhere
|
import org.jetbrains.exposed.sql.andWhere
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import suwayomi.tachidesk.graphql.queries.util.GreaterOrLessThanLong
|
import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter
|
||||||
import suwayomi.tachidesk.graphql.queries.util.andWhereGreaterOrLessThen
|
import suwayomi.tachidesk.graphql.queries.filter.Filter
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.IntFilter
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.LongFilter
|
||||||
|
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.types.MangaNodeList
|
import suwayomi.tachidesk.graphql.types.MangaNodeList
|
||||||
import suwayomi.tachidesk.graphql.types.MangaNodeList.Companion.toNodeList
|
import suwayomi.tachidesk.graphql.types.MangaNodeList.Companion.toNodeList
|
||||||
import suwayomi.tachidesk.graphql.types.MangaType
|
import suwayomi.tachidesk.graphql.types.MangaType
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.MangaStatus
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO Queries
|
* TODO Queries
|
||||||
* - Query options(optionally query the title, description, or/and)
|
|
||||||
*
|
*
|
||||||
* TODO Mutations
|
* TODO Mutations
|
||||||
* - Favorite
|
* - Favorite
|
||||||
@@ -43,62 +54,130 @@ class MangaQuery {
|
|||||||
return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
|
return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class MangaSort {
|
enum class MangaOrderBy {
|
||||||
ID,
|
ID,
|
||||||
TITLE,
|
TITLE,
|
||||||
IN_LIBRARY_AT,
|
IN_LIBRARY_AT,
|
||||||
LAST_FETCHED_AT
|
LAST_FETCHED_AT
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MangaQueryInput(
|
data class MangaCondition(
|
||||||
val ids: List<Int>? = null,
|
val id: Int? = null,
|
||||||
val categoryIds: List<Int>? = null,
|
val sourceId: Long? = null,
|
||||||
val sourceIds: List<Long>? = null,
|
val url: String? = null,
|
||||||
|
val title: String? = null,
|
||||||
|
val thumbnailUrl: String? = null,
|
||||||
|
val initialized: Boolean? = null,
|
||||||
|
val artist: String? = null,
|
||||||
|
val author: String? = null,
|
||||||
|
val description: String? = null,
|
||||||
|
val genre: List<String>? = null,
|
||||||
|
val status: MangaStatus? = null,
|
||||||
val inLibrary: Boolean? = null,
|
val inLibrary: Boolean? = null,
|
||||||
val inLibraryAt: GreaterOrLessThanLong? = null,
|
val inLibraryAt: Long? = null,
|
||||||
val sort: MangaSort? = null,
|
val realUrl: String? = null,
|
||||||
val sortOrder: SortOrder? = null,
|
var lastFetchedAt: Long? = null,
|
||||||
val page: Int? = null,
|
var chaptersLastFetchedAt: Long? = null
|
||||||
val count: Int? = null
|
) {
|
||||||
)
|
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)
|
||||||
|
|
||||||
fun mangas(input: MangaQueryInput? = null): MangaNodeList {
|
return opAnd.op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MangaFilter(
|
||||||
|
val id: IntFilter? = null,
|
||||||
|
val sourceId: LongFilter? = null,
|
||||||
|
val url: StringFilter? = null,
|
||||||
|
val title: StringFilter? = null,
|
||||||
|
val thumbnailUrl: StringFilter? = null,
|
||||||
|
val initialized: BooleanFilter? = null,
|
||||||
|
val artist: StringFilter? = null,
|
||||||
|
val author: StringFilter? = null,
|
||||||
|
val description: StringFilter? = null,
|
||||||
|
// val genre: List<String>? = null, // todo
|
||||||
|
// val status: MangaStatus? = null, // todo
|
||||||
|
val inLibrary: BooleanFilter? = null,
|
||||||
|
val inLibraryAt: LongFilter? = null,
|
||||||
|
val realUrl: StringFilter? = null,
|
||||||
|
var lastFetchedAt: LongFilter? = null,
|
||||||
|
var chaptersLastFetchedAt: LongFilter? = null,
|
||||||
|
val category: IntFilter? = null,
|
||||||
|
override val and: List<MangaFilter>? = null,
|
||||||
|
override val or: List<MangaFilter>? = null,
|
||||||
|
override val not: MangaFilter? = null
|
||||||
|
) : Filter<MangaFilter> {
|
||||||
|
override fun getOpList(): List<Op<Boolean>> {
|
||||||
|
return listOfNotNull(
|
||||||
|
andFilterWithCompareEntity(MangaTable.id, id),
|
||||||
|
andFilterWithCompare(MangaTable.sourceReference, sourceId),
|
||||||
|
andFilterWithCompareString(MangaTable.url, url),
|
||||||
|
andFilterWithCompareString(MangaTable.title, title),
|
||||||
|
andFilterWithCompareString(MangaTable.thumbnail_url, thumbnailUrl),
|
||||||
|
andFilterWithCompare(MangaTable.initialized, initialized),
|
||||||
|
andFilterWithCompareString(MangaTable.artist, artist),
|
||||||
|
andFilterWithCompareString(MangaTable.author, author),
|
||||||
|
andFilterWithCompareString(MangaTable.description, description),
|
||||||
|
andFilterWithCompare(MangaTable.inLibrary, inLibrary),
|
||||||
|
andFilterWithCompare(MangaTable.inLibraryAt, inLibraryAt),
|
||||||
|
andFilterWithCompareString(MangaTable.realUrl, realUrl),
|
||||||
|
andFilterWithCompare(MangaTable.inLibraryAt, lastFetchedAt),
|
||||||
|
andFilterWithCompare(MangaTable.inLibraryAt, chaptersLastFetchedAt)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCategoryOp() = andFilterWithCompareEntity(CategoryMangaTable.category, category)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mangas(
|
||||||
|
condition: MangaCondition? = null,
|
||||||
|
filter: MangaFilter? = null,
|
||||||
|
orderBy: MangaOrderBy? = null,
|
||||||
|
orderByType: SortOrder? = null
|
||||||
|
): MangaNodeList {
|
||||||
val results = transaction {
|
val results = transaction {
|
||||||
var res = MangaTable.selectAll()
|
var res = MangaTable.selectAll()
|
||||||
|
|
||||||
if (input != null) {
|
val categoryOp = filter?.getCategoryOp()
|
||||||
if (input.categoryIds != null) {
|
if (categoryOp != null) {
|
||||||
res = MangaTable.innerJoin(CategoryMangaTable)
|
res = MangaTable.innerJoin(CategoryMangaTable)
|
||||||
.select { CategoryMangaTable.category inList input.categoryIds }
|
.select { categoryOp }
|
||||||
}
|
}
|
||||||
if (input.ids != null) {
|
val conditionOp = condition?.getOp()
|
||||||
res.andWhere { MangaTable.id inList input.ids }
|
if (conditionOp != null) {
|
||||||
}
|
res.andWhere { conditionOp }
|
||||||
if (input.sourceIds != null) {
|
}
|
||||||
res.andWhere { MangaTable.sourceReference inList input.sourceIds }
|
val filterOp = filter?.getOp()
|
||||||
}
|
if (filterOp != null) {
|
||||||
if (input.inLibrary != null) {
|
res.andWhere { filterOp }
|
||||||
res.andWhere { MangaTable.inLibrary eq input.inLibrary }
|
}
|
||||||
}
|
if (orderBy != null) {
|
||||||
if (input.inLibraryAt != null) {
|
val orderByColumn = when (orderBy) {
|
||||||
res.andWhereGreaterOrLessThen(
|
MangaOrderBy.ID -> MangaTable.id
|
||||||
column = MangaTable.inLibraryAt,
|
MangaOrderBy.TITLE -> MangaTable.title
|
||||||
greaterOrLessThan = input.inLibraryAt
|
MangaOrderBy.IN_LIBRARY_AT -> MangaTable.inLibraryAt
|
||||||
)
|
MangaOrderBy.LAST_FETCHED_AT -> MangaTable.lastFetchedAt
|
||||||
}
|
|
||||||
if (input.sort != null) {
|
|
||||||
val orderBy = when (input.sort) {
|
|
||||||
MangaSort.ID -> MangaTable.id
|
|
||||||
MangaSort.TITLE -> MangaTable.title
|
|
||||||
MangaSort.IN_LIBRARY_AT -> MangaTable.inLibraryAt
|
|
||||||
MangaSort.LAST_FETCHED_AT -> MangaTable.lastFetchedAt
|
|
||||||
}
|
|
||||||
res.orderBy(orderBy, order = input.sortOrder ?: SortOrder.ASC)
|
|
||||||
}
|
|
||||||
if (input.count != null) {
|
|
||||||
val offset = if (input.page == null) 0 else (input.page * input.count).toLong()
|
|
||||||
res.limit(input.count, offset)
|
|
||||||
}
|
}
|
||||||
|
res.orderBy(orderByColumn, order = orderByType ?: SortOrder.ASC)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.toList()
|
res.toList()
|
||||||
|
|||||||
@@ -0,0 +1,329 @@
|
|||||||
|
package suwayomi.tachidesk.graphql.queries.filter
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.EntityID
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import org.jetbrains.exposed.sql.ComparisonOp
|
||||||
|
import org.jetbrains.exposed.sql.Expression
|
||||||
|
import org.jetbrains.exposed.sql.ExpressionWithColumnType
|
||||||
|
import org.jetbrains.exposed.sql.LikeEscapeOp
|
||||||
|
import org.jetbrains.exposed.sql.LikePattern
|
||||||
|
import org.jetbrains.exposed.sql.Op
|
||||||
|
import org.jetbrains.exposed.sql.QueryBuilder
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.wrap
|
||||||
|
import org.jetbrains.exposed.sql.and
|
||||||
|
import org.jetbrains.exposed.sql.not
|
||||||
|
import org.jetbrains.exposed.sql.or
|
||||||
|
import org.jetbrains.exposed.sql.stringParam
|
||||||
|
import org.jetbrains.exposed.sql.upperCase
|
||||||
|
|
||||||
|
class ILikeEscapeOp(expr1: Expression<*>, expr2: Expression<*>, like: Boolean, val escapeChar: Char?) : ComparisonOp(expr1, expr2, if (like) "ILIKE" else "NOT ILIKE") {
|
||||||
|
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
|
||||||
|
super.toQueryBuilder(queryBuilder)
|
||||||
|
if (escapeChar != null) {
|
||||||
|
with(queryBuilder) {
|
||||||
|
+" ESCAPE "
|
||||||
|
+stringParam(escapeChar.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <T : String?> iLike(expression: Expression<T>, pattern: String): LikeEscapeOp = iLike(expression, LikePattern(pattern))
|
||||||
|
fun <T : String?> iNotLike(expression: Expression<T>, pattern: String): LikeEscapeOp = iNotLike(expression, LikePattern(pattern))
|
||||||
|
fun <T : String?> iLike(expression: Expression<T>, pattern: LikePattern): LikeEscapeOp = LikeEscapeOp(expression, stringParam(pattern.pattern), true, pattern.escapeChar)
|
||||||
|
fun <T : String?> iNotLike(expression: Expression<T>, pattern: LikePattern): LikeEscapeOp = LikeEscapeOp(expression, stringParam(pattern.pattern), false, pattern.escapeChar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DistinctFromOp(expr1: Expression<*>, expr2: Expression<*>, not: Boolean) : ComparisonOp(expr1, expr2, if (not) "IS NOT DISTINCT FROM" else "IS DISTINCT FROM") {
|
||||||
|
companion object {
|
||||||
|
fun <T> distinctFrom(expression: ExpressionWithColumnType<T>, t: T): DistinctFromOp = DistinctFromOp(
|
||||||
|
expression,
|
||||||
|
with(SqlExpressionBuilder) {
|
||||||
|
expression.wrap(t)
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
fun <T> notDistinctFrom(expression: ExpressionWithColumnType<T>, t: T): DistinctFromOp = DistinctFromOp(
|
||||||
|
expression,
|
||||||
|
with(SqlExpressionBuilder) {
|
||||||
|
expression.wrap(t)
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
fun <T : Comparable<T>> distinctFrom(expression: ExpressionWithColumnType<EntityID<T>>, t: T): DistinctFromOp = DistinctFromOp(
|
||||||
|
expression,
|
||||||
|
with(SqlExpressionBuilder) {
|
||||||
|
expression.wrap(t)
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
fun <T : Comparable<T>> notDistinctFrom(expression: ExpressionWithColumnType<EntityID<T>>, t: T): DistinctFromOp = DistinctFromOp(
|
||||||
|
expression,
|
||||||
|
with(SqlExpressionBuilder) {
|
||||||
|
expression.wrap(t)
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Filter<T : Filter<T>> {
|
||||||
|
val and: List<T>?
|
||||||
|
val or: List<T>?
|
||||||
|
val not: T?
|
||||||
|
|
||||||
|
fun getOpList(): List<Op<Boolean>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScalarFilter<T> {
|
||||||
|
val isNull: Boolean?
|
||||||
|
val equalTo: T?
|
||||||
|
val notEqualTo: T?
|
||||||
|
val distinctFrom: T?
|
||||||
|
val notDistinctFrom: T?
|
||||||
|
val `in`: List<T>?
|
||||||
|
val notIn: List<T>?
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComparableScalarFilter<T : Comparable<T>> : ScalarFilter<T> {
|
||||||
|
val lessThan: T?
|
||||||
|
val lessThanOrEqualTo: T?
|
||||||
|
val greaterThan: T?
|
||||||
|
val greaterThanOrEqualTo: T?
|
||||||
|
}
|
||||||
|
|
||||||
|
data class LongFilter(
|
||||||
|
override val isNull: Boolean? = null,
|
||||||
|
override val equalTo: Long? = null,
|
||||||
|
override val notEqualTo: Long? = null,
|
||||||
|
override val distinctFrom: Long? = null,
|
||||||
|
override val notDistinctFrom: Long? = null,
|
||||||
|
override val `in`: List<Long>? = null,
|
||||||
|
override val notIn: List<Long>? = null,
|
||||||
|
override val lessThan: Long? = null,
|
||||||
|
override val lessThanOrEqualTo: Long? = null,
|
||||||
|
override val greaterThan: Long? = null,
|
||||||
|
override val greaterThanOrEqualTo: Long? = null
|
||||||
|
) : ComparableScalarFilter<Long>
|
||||||
|
|
||||||
|
data class BooleanFilter(
|
||||||
|
override val isNull: Boolean? = null,
|
||||||
|
override val equalTo: Boolean? = null,
|
||||||
|
override val notEqualTo: Boolean? = null,
|
||||||
|
override val distinctFrom: Boolean? = null,
|
||||||
|
override val notDistinctFrom: Boolean? = null,
|
||||||
|
override val `in`: List<Boolean>? = null,
|
||||||
|
override val notIn: List<Boolean>? = null,
|
||||||
|
override val lessThan: Boolean? = null,
|
||||||
|
override val lessThanOrEqualTo: Boolean? = null,
|
||||||
|
override val greaterThan: Boolean? = null,
|
||||||
|
override val greaterThanOrEqualTo: Boolean? = null
|
||||||
|
) : ComparableScalarFilter<Boolean>
|
||||||
|
|
||||||
|
data class IntFilter(
|
||||||
|
override val isNull: Boolean? = null,
|
||||||
|
override val equalTo: Int? = null,
|
||||||
|
override val notEqualTo: Int? = null,
|
||||||
|
override val distinctFrom: Int? = null,
|
||||||
|
override val notDistinctFrom: Int? = null,
|
||||||
|
override val `in`: List<Int>? = null,
|
||||||
|
override val notIn: List<Int>? = null,
|
||||||
|
override val lessThan: Int? = null,
|
||||||
|
override val lessThanOrEqualTo: Int? = null,
|
||||||
|
override val greaterThan: Int? = null,
|
||||||
|
override val greaterThanOrEqualTo: Int? = null
|
||||||
|
) : ComparableScalarFilter<Int>
|
||||||
|
|
||||||
|
data class StringFilter(
|
||||||
|
override val isNull: Boolean? = null,
|
||||||
|
override val equalTo: String? = null,
|
||||||
|
override val notEqualTo: String? = null,
|
||||||
|
override val distinctFrom: String? = null,
|
||||||
|
override val notDistinctFrom: String? = null,
|
||||||
|
override val `in`: List<String>? = null,
|
||||||
|
override val notIn: List<String>? = null,
|
||||||
|
override val lessThan: String? = null,
|
||||||
|
override val lessThanOrEqualTo: String? = null,
|
||||||
|
override val greaterThan: String? = null,
|
||||||
|
override val greaterThanOrEqualTo: String? = null,
|
||||||
|
val includes: String? = null,
|
||||||
|
val notIncludes: String? = null,
|
||||||
|
val includesInsensitive: String? = null,
|
||||||
|
val notIncludesInsensitive: String? = null,
|
||||||
|
val startsWith: String? = null,
|
||||||
|
val notStartsWith: String? = null,
|
||||||
|
val startsWithInsensitive: String? = null,
|
||||||
|
val notStartsWithInsensitive: String? = null,
|
||||||
|
val endsWith: String? = null,
|
||||||
|
val notEndsWith: String? = null,
|
||||||
|
val endsWithInsensitive: String? = null,
|
||||||
|
val notEndsWithInsensitive: String? = null,
|
||||||
|
val like: String? = null,
|
||||||
|
val notLike: String? = null,
|
||||||
|
val likeInsensitive: String? = null,
|
||||||
|
val notLikeInsensitive: String? = null,
|
||||||
|
val distinctFromInsensitive: String? = null,
|
||||||
|
val notDistinctFromInsensitive: String? = null,
|
||||||
|
val inInsensitive: List<String>? = null,
|
||||||
|
val notInInsensitive: List<String>? = null,
|
||||||
|
val lessThanInsensitive: String? = null,
|
||||||
|
val lessThanOrEqualToInsensitive: String? = null,
|
||||||
|
val greaterThanInsensitive: String? = null,
|
||||||
|
val greaterThanOrEqualToInsensitive: String? = null
|
||||||
|
) : ComparableScalarFilter<String>
|
||||||
|
|
||||||
|
fun <T : String?> andFilterWithCompareString(
|
||||||
|
column: Column<T>,
|
||||||
|
filter: StringFilter?
|
||||||
|
): Op<Boolean>? {
|
||||||
|
filter ?: return null
|
||||||
|
val opAnd = OpAnd()
|
||||||
|
opAnd.andWhere(filter.includes) { column like "%$it%" }
|
||||||
|
opAnd.andWhere(filter.notIncludes) { column notLike "%$it%" }
|
||||||
|
opAnd.andWhere(filter.includesInsensitive) { ILikeEscapeOp.iLike(column, "%$it%") }
|
||||||
|
opAnd.andWhere(filter.notIncludesInsensitive) { ILikeEscapeOp.iNotLike(column, "%$it%") }
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.startsWith) { column like "$it%" }
|
||||||
|
opAnd.andWhere(filter.notStartsWith) { column notLike "$it%" }
|
||||||
|
opAnd.andWhere(filter.startsWithInsensitive) { ILikeEscapeOp.iLike(column, "$it%") }
|
||||||
|
opAnd.andWhere(filter.notStartsWithInsensitive) { ILikeEscapeOp.iNotLike(column, "$it%") }
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.endsWith) { column like "%$it" }
|
||||||
|
opAnd.andWhere(filter.notEndsWith) { column notLike "%$it" }
|
||||||
|
opAnd.andWhere(filter.endsWithInsensitive) { ILikeEscapeOp.iLike(column, "%$it") }
|
||||||
|
opAnd.andWhere(filter.notEndsWithInsensitive) { ILikeEscapeOp.iNotLike(column, "%$it") }
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.like) { column like it }
|
||||||
|
opAnd.andWhere(filter.notLike) { column notLike it }
|
||||||
|
opAnd.andWhere(filter.likeInsensitive) { ILikeEscapeOp.iLike(column, it) }
|
||||||
|
opAnd.andWhere(filter.notLikeInsensitive) { ILikeEscapeOp.iNotLike(column, it) }
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.distinctFromInsensitive) { DistinctFromOp.distinctFrom(column.upperCase(), it.uppercase() as T) }
|
||||||
|
opAnd.andWhere(filter.notDistinctFromInsensitive) { DistinctFromOp.notDistinctFrom(column.upperCase(), it.uppercase() as T) }
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.inInsensitive) { column.upperCase() inList (it.map { it.uppercase() } as List<T>) }
|
||||||
|
opAnd.andWhere(filter.notInInsensitive) { column.upperCase() notInList (it.map { it.uppercase() } as List<T>) }
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.lessThanInsensitive) { column.upperCase() less it.uppercase() }
|
||||||
|
opAnd.andWhere(filter.lessThanOrEqualToInsensitive) { column.upperCase() lessEq it.uppercase() }
|
||||||
|
opAnd.andWhere(filter.greaterThanInsensitive) { column.upperCase() greater it.uppercase() }
|
||||||
|
opAnd.andWhere(filter.greaterThanOrEqualToInsensitive) { column.upperCase() greaterEq it.uppercase() }
|
||||||
|
|
||||||
|
return opAnd.op
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpAnd(var op: Op<Boolean>? = null) {
|
||||||
|
fun <T> andWhere(value: T?, andPart: SqlExpressionBuilder.(T) -> Op<Boolean>) {
|
||||||
|
value ?: return
|
||||||
|
val expr = Op.build { andPart(value) }
|
||||||
|
op = if (op == null) expr else (op!! and expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Comparable<T>> andFilterWithCompare(
|
||||||
|
column: Column<T>,
|
||||||
|
filter: ComparableScalarFilter<T>?
|
||||||
|
): Op<Boolean>? {
|
||||||
|
filter ?: return null
|
||||||
|
val opAnd = OpAnd(andFilter(column, filter))
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.lessThan) { column less it }
|
||||||
|
opAnd.andWhere(filter.lessThanOrEqualTo) { column lessEq it }
|
||||||
|
opAnd.andWhere(filter.greaterThan) { column greater it }
|
||||||
|
opAnd.andWhere(filter.greaterThanOrEqualTo) { column greaterEq it }
|
||||||
|
|
||||||
|
return opAnd.op
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Comparable<T>> andFilterWithCompareEntity(
|
||||||
|
column: Column<EntityID<T>>,
|
||||||
|
filter: ComparableScalarFilter<T>?
|
||||||
|
): Op<Boolean>? {
|
||||||
|
filter ?: return null
|
||||||
|
val opAnd = OpAnd(andFilterEntity(column, filter))
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.lessThan) { column less it }
|
||||||
|
opAnd.andWhere(filter.lessThanOrEqualTo) { column lessEq it }
|
||||||
|
opAnd.andWhere(filter.greaterThan) { column greater it }
|
||||||
|
opAnd.andWhere(filter.greaterThanOrEqualTo) { column greaterEq it }
|
||||||
|
|
||||||
|
return opAnd.op
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Comparable<T>> andFilter(
|
||||||
|
column: Column<T>,
|
||||||
|
filter: ScalarFilter<T>?
|
||||||
|
): Op<Boolean>? {
|
||||||
|
filter ?: return null
|
||||||
|
val opAnd = OpAnd()
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.isNull) { if (it) column.isNull() else column.isNotNull() }
|
||||||
|
opAnd.andWhere(filter.equalTo) { column eq it }
|
||||||
|
opAnd.andWhere(filter.notEqualTo) { column neq it }
|
||||||
|
opAnd.andWhere(filter.distinctFrom) { DistinctFromOp.distinctFrom(column, it) }
|
||||||
|
opAnd.andWhere(filter.notDistinctFrom) { DistinctFromOp.notDistinctFrom(column, it) }
|
||||||
|
if (!filter.`in`.isNullOrEmpty()) {
|
||||||
|
opAnd.andWhere(filter.`in`) { column inList it }
|
||||||
|
}
|
||||||
|
if (!filter.notIn.isNullOrEmpty()) {
|
||||||
|
opAnd.andWhere(filter.notIn) { column notInList it }
|
||||||
|
}
|
||||||
|
return opAnd.op
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Comparable<T>> andFilterEntity(
|
||||||
|
column: Column<EntityID<T>>,
|
||||||
|
filter: ScalarFilter<T>?
|
||||||
|
): Op<Boolean>? {
|
||||||
|
filter ?: return null
|
||||||
|
val opAnd = OpAnd()
|
||||||
|
|
||||||
|
opAnd.andWhere(filter.isNull) { if (filter.isNull!!) column.isNull() else column.isNotNull() }
|
||||||
|
opAnd.andWhere(filter.equalTo) { column eq filter.equalTo!! }
|
||||||
|
opAnd.andWhere(filter.notEqualTo) { column neq filter.notEqualTo!! }
|
||||||
|
opAnd.andWhere(filter.distinctFrom) { DistinctFromOp.distinctFrom(column, it) }
|
||||||
|
opAnd.andWhere(filter.notDistinctFrom) { DistinctFromOp.notDistinctFrom(column, it) }
|
||||||
|
if (!filter.`in`.isNullOrEmpty()) {
|
||||||
|
opAnd.andWhere(filter.`in`) { column inList filter.`in`!! }
|
||||||
|
}
|
||||||
|
if (!filter.notIn.isNullOrEmpty()) {
|
||||||
|
opAnd.andWhere(filter.notIn) { column notInList filter.notIn!! }
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package suwayomi.tachidesk.graphql.queries.util
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.sql.Column
|
|
||||||
import org.jetbrains.exposed.sql.Query
|
|
||||||
import org.jetbrains.exposed.sql.andWhere
|
|
||||||
|
|
||||||
interface GreaterOrLessThan<T : Comparable<T>> {
|
|
||||||
val value: T
|
|
||||||
val type: GreaterOrLessThanType
|
|
||||||
}
|
|
||||||
|
|
||||||
data class GreaterOrLessThanLong(
|
|
||||||
override val value: Long,
|
|
||||||
override val type: GreaterOrLessThanType
|
|
||||||
) : GreaterOrLessThan<Long>
|
|
||||||
|
|
||||||
enum class GreaterOrLessThanType {
|
|
||||||
GREATER_THAN,
|
|
||||||
GREATER_THAN_OR_EQ,
|
|
||||||
LESS_THAN,
|
|
||||||
LESS_THAN_OR_EQ
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : Comparable<T>> Query.andWhereGreaterOrLessThen(
|
|
||||||
column: Column<T>,
|
|
||||||
greaterOrLessThan: GreaterOrLessThan<T>
|
|
||||||
) {
|
|
||||||
when (greaterOrLessThan.type) {
|
|
||||||
GreaterOrLessThanType.GREATER_THAN -> andWhere {
|
|
||||||
column greater greaterOrLessThan.value // toValue()
|
|
||||||
}
|
|
||||||
GreaterOrLessThanType.GREATER_THAN_OR_EQ -> andWhere {
|
|
||||||
column greaterEq greaterOrLessThan.value // toValue()
|
|
||||||
}
|
|
||||||
GreaterOrLessThanType.LESS_THAN -> andWhere {
|
|
||||||
column less greaterOrLessThan.value // toValue()
|
|
||||||
}
|
|
||||||
GreaterOrLessThanType.LESS_THAN_OR_EQ -> andWhere {
|
|
||||||
column lessEq greaterOrLessThan.value // toValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user