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 13deecdc..68dc9515 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt @@ -126,14 +126,14 @@ class MangaQuery { opAnd.eq(artist, MangaTable.artist) opAnd.eq(author, MangaTable.author) opAnd.eq(description, MangaTable.description) - genre?.forEach { opAnd.like("%$it%", MangaTable.genre) } + opAnd.andWhereAll(genre) { MangaTable.genre like "%$it%" } 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) - opAnd.inList(categoryIds, CategoryMangaTable.category) + opAnd.andWhere(categoryIds) { CategoryMangaTable.category inList it } return opAnd.op } @@ -143,7 +143,11 @@ class MangaQuery { override val isNull: Boolean? = null, override val equalTo: MangaStatus? = null, override val notEqualTo: MangaStatus? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: MangaStatus? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: MangaStatus? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -156,7 +160,11 @@ class MangaQuery { IntFilter( equalTo = equalTo?.value, notEqualTo = notEqualTo?.value, + notEqualToAll = notEqualToAll?.map { it.value }, + notEqualToAny = notEqualToAny?.map { it.value }, distinctFrom = distinctFrom?.value, + distinctFromAll = distinctFromAll?.map { it.value }, + distinctFromAny = distinctFromAny?.map { it.value }, notDistinctFrom = notDistinctFrom?.value, `in` = `in`?.map { it.value }, notIn = notIn?.map { it.value }, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt index 8de9fb9d..7ebd9fbc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt @@ -182,7 +182,11 @@ interface ScalarFilter { val isNull: Boolean? val equalTo: T? val notEqualTo: T? + val notEqualToAll: List? + val notEqualToAny: List? val distinctFrom: T? + val distinctFromAll: List? + val distinctFromAny: List? val notDistinctFrom: T? @Suppress("ktlint:standard:property-naming") @@ -207,7 +211,11 @@ data class LongFilter( override val isNull: Boolean? = null, override val equalTo: Long? = null, override val notEqualTo: Long? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: Long? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: Long? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -221,7 +229,11 @@ data class BooleanFilter( override val isNull: Boolean? = null, override val equalTo: Boolean? = null, override val notEqualTo: Boolean? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: Boolean? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: Boolean? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -235,7 +247,11 @@ data class IntFilter( override val isNull: Boolean? = null, override val equalTo: Int? = null, override val notEqualTo: Int? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: Int? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: Int? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -249,7 +265,11 @@ data class FloatFilter( override val isNull: Boolean? = null, override val equalTo: Float? = null, override val notEqualTo: Float? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: Float? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: Float? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -263,7 +283,11 @@ data class DoubleFilter( override val isNull: Boolean? = null, override val equalTo: Double? = null, override val notEqualTo: Double? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: Double? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: Double? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -277,7 +301,11 @@ data class StringFilter( override val isNull: Boolean? = null, override val equalTo: String? = null, override val notEqualTo: String? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: String? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: String? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -286,22 +314,56 @@ data class StringFilter( override val greaterThan: String? = null, override val greaterThanOrEqualTo: String? = null, val includes: String? = null, + val includesAll: List? = null, + val includesAny: List? = null, val notIncludes: String? = null, + val notIncludesAll: List? = null, + val notIncludesAny: List? = null, val includesInsensitive: String? = null, + val includesInsensitiveAll: List? = null, + val includesInsensitiveAny: List? = null, val notIncludesInsensitive: String? = null, + val notIncludesInsensitiveAll: List? = null, + val notIncludesInsensitiveAny: List? = null, val startsWith: String? = null, + val startsWithAll: List? = null, + val startsWithAny: List? = null, val notStartsWith: String? = null, + val notStartsWithAll: List? = null, + val notStartsWithAny: List? = null, val startsWithInsensitive: String? = null, + val startsWithInsensitiveAll: List? = null, + val startsWithInsensitiveAny: List? = null, val notStartsWithInsensitive: String? = null, + val notStartsWithInsensitiveAll: List? = null, + val notStartsWithInsensitiveAny: List? = null, val endsWith: String? = null, + val endsWithAll: List? = null, + val endsWithAny: List? = null, val notEndsWith: String? = null, + val notEndsWithAll: List? = null, + val notEndsWithAny: List? = null, val endsWithInsensitive: String? = null, + val endsWithInsensitiveAll: List? = null, + val endsWithInsensitiveAny: List? = null, val notEndsWithInsensitive: String? = null, + val notEndsWithInsensitiveAll: List? = null, + val notEndsWithInsensitiveAny: List? = null, val like: String? = null, + val likeAll: List? = null, + val likeAny: List? = null, val notLike: String? = null, + val notLikeAll: List? = null, + val notLikeAny: List? = null, val likeInsensitive: String? = null, + val likeInsensitiveAll: List? = null, + val likeInsensitiveAny: List? = null, val notLikeInsensitive: String? = null, + val notLikeInsensitiveAll: List? = null, + val notLikeInsensitiveAny: List? = null, val distinctFromInsensitive: String? = null, + val distinctFromInsensitiveAll: List? = null, + val distinctFromInsensitiveAny: List? = null, val notDistinctFromInsensitive: String? = null, val inInsensitive: List? = null, val notInInsensitive: List? = null, @@ -315,7 +377,11 @@ data class StringListFilter( override val isNull: Boolean? = null, override val equalTo: String? = null, override val notEqualTo: String? = null, + override val notEqualToAll: List? = null, + override val notEqualToAny: List? = null, override val distinctFrom: String? = null, + override val distinctFromAll: List? = null, + override val distinctFromAny: List? = null, override val notDistinctFrom: String? = null, override val `in`: List? = null, override val notIn: List? = null, @@ -337,8 +403,12 @@ fun andFilterWithCompareString( opAnd.andWhere(filter.isNull) { if (it) column.isNull() else column.isNotNull() } opAnd.andWhere(filter.equalTo) { column eq it as S } - opAnd.andWhere(filter.notEqualTo) { column neq it as S } - opAnd.andWhere(filter.distinctFrom) { DistinctFromOp.distinctFrom(column, it as S) } + opAnd.andWhere(filter.notEqualTo, filter.notEqualToAll, filter.notEqualToAny) { column neq it as S } + opAnd.andWhere( + filter.distinctFrom, + filter.distinctFromAll, + filter.distinctFromAny, + ) { DistinctFromOp.distinctFrom(column, it as S) } opAnd.andWhere(filter.notDistinctFrom) { DistinctFromOp.notDistinctFrom(column, it as S) } if (!filter.`in`.isNullOrEmpty()) { opAnd.andWhere(filter.`in`) { column inList it as List } @@ -352,27 +422,43 @@ fun andFilterWithCompareString( opAnd.andWhere(filter.greaterThan) { column greater it } opAnd.andWhere(filter.greaterThanOrEqualTo) { column greaterEq it } - 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.includes, filter.includesAll, filter.includesAny) { column like "%$it%" } + opAnd.andWhere(filter.notIncludes, filter.notIncludesAll, filter.notIncludesAny) { column notLike "%$it%" } + opAnd.andWhere(filter.includesInsensitive, filter.includesInsensitiveAll, filter.includesInsensitiveAny) { + ILikeEscapeOp.iLike(column, "%$it%") + } + opAnd.andWhere(filter.notIncludesInsensitive, filter.notIncludesInsensitiveAll, filter.notIncludesInsensitiveAny) { + 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.startsWith, filter.startsWithAll, filter.startsWithAny) { column like "$it%" } + opAnd.andWhere(filter.notStartsWith, filter.notStartsWithAll, filter.notStartsWithAny) { column notLike "$it%" } + opAnd.andWhere(filter.startsWithInsensitive, filter.startsWithInsensitiveAll, filter.startsWithInsensitiveAny) { + ILikeEscapeOp.iLike(column, "$it%") + } + opAnd.andWhere(filter.notStartsWithInsensitive, filter.notStartsWithInsensitiveAll, filter.notStartsWithInsensitiveAny) { + 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.endsWith, filter.endsWithAll, filter.endsWithAny) { column like "%$it" } + opAnd.andWhere(filter.notEndsWith, filter.notEndsWithAll, filter.notEndsWithAny) { column notLike "%$it" } + opAnd.andWhere(filter.endsWithInsensitive, filter.endsWithInsensitiveAll, filter.endsWithInsensitiveAny) { + ILikeEscapeOp.iLike(column, "%$it") + } + opAnd.andWhere(filter.notEndsWithInsensitive, filter.notEndsWithInsensitiveAll, filter.notEndsWithInsensitiveAny) { + 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.like, filter.likeAll, filter.likeAny) { column like it } + opAnd.andWhere(filter.notLike, filter.notLikeAll, filter.notLikeAny) { column notLike it } + opAnd.andWhere(filter.likeInsensitive, filter.likeInsensitiveAll, filter.likeInsensitiveAny) { ILikeEscapeOp.iLike(column, it) } + opAnd.andWhere(filter.notLikeInsensitive, filter.notLikeInsensitiveAll, filter.notLikeInsensitiveAny) { + ILikeEscapeOp.iNotLike(column, it) + } - opAnd.andWhere(filter.distinctFromInsensitive) { DistinctFromOp.distinctFrom(column.upperCase(), it.uppercase() as S) } + opAnd.andWhere(filter.distinctFromInsensitive, filter.distinctFromInsensitiveAll, filter.distinctFromInsensitiveAny) { + DistinctFromOp.distinctFrom(column.upperCase(), it.uppercase() as S) + } opAnd.andWhere(filter.notDistinctFromInsensitive) { DistinctFromOp.notDistinctFrom(column.upperCase(), it.uppercase() as S) } opAnd.andWhere(filter.inInsensitive) { column.upperCase() inList (it.map { it.uppercase() } as List) } @@ -399,9 +485,35 @@ class OpAnd(var op: Op? = null) { fun andWhere( values: List?, andPart: SqlExpressionBuilder.(List) -> Op, + ) { + @Suppress("UNCHECKED_CAST") + return andWhere(values as T?, andPart as SqlExpressionBuilder.(Any) -> Op) + } + + fun andWhere( + valueDefault: T?, + valueAll: List?, + valueAny: List?, + expr: SqlExpressionBuilder.(T) -> Op, + ) { + andWhere(valueDefault, expr) + andWhereAll(valueAll, expr) + andWhereAny(valueAny, expr) + } + + fun andWhereAll( + values: List?, + andPart: SqlExpressionBuilder.(T) -> Op, + ) { + values?.map { andWhere(it, andPart) } + } + + fun andWhereAny( + values: List?, + andPart: SqlExpressionBuilder.(T) -> Op, ) { values ?: return - val expr = Op.build { andPart(values) } + val expr = values.map { Op.build { andPart(it) } }.reduce { acc, op -> acc or op } op = if (op == null) expr else (op!! and expr) } @@ -414,22 +526,6 @@ class OpAnd(var op: Op? = null) { value: T?, column: Column>, ) = andWhere(value) { column eq it } - - fun inList( - values: List?, - column: Column, - ) = andWhere(values) { column inList it } - - @JvmName("inListComparable") - fun > inList( - values: List?, - column: Column>, - ) = andWhere(values) { column inList it } - - fun like( - value: String?, - column: Column, - ) = andWhere(value) { column like it } } fun , S : T?> andFilterWithCompare( @@ -451,15 +547,8 @@ fun > andFilterWithCompareEntity( column: Column>, filter: ComparableScalarFilter?, ): Op? { - 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 + @Suppress("UNCHECKED_CAST") + return andFilterWithCompare(column as Column, filter) } @Suppress("UNCHECKED_CAST") @@ -472,8 +561,8 @@ fun , S : T?> andFilter( opAnd.andWhere(filter.isNull) { if (it) column.isNull() else column.isNotNull() } opAnd.andWhere(filter.equalTo) { column eq it as S } - opAnd.andWhere(filter.notEqualTo) { column neq it as S } - opAnd.andWhere(filter.distinctFrom) { DistinctFromOp.distinctFrom(column, it as S) } + opAnd.andWhere(filter.notEqualTo, filter.notEqualToAll, filter.notEqualToAny) { column neq it as S } + opAnd.andWhere(filter.distinctFrom, filter.distinctFromAll, filter.distinctFromAny) { DistinctFromOp.distinctFrom(column, it as S) } opAnd.andWhere(filter.notDistinctFrom) { DistinctFromOp.notDistinctFrom(column, it as S) } if (!filter.`in`.isNullOrEmpty()) { opAnd.andWhere(filter.`in`) { column inList it as List } @@ -483,24 +572,3 @@ fun , S : T?> andFilter( } return opAnd.op } - -fun > andFilterEntity( - column: Column>, - filter: ScalarFilter?, -): Op? { - 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 -}