Search by user ban_date, refactor of search
This commit is contained in:
parent
947f8cac02
commit
2b5ab1c054
@ -3,6 +3,7 @@ package com.nisemoe.nise
|
|||||||
import com.nisemoe.generated.enums.JudgementType
|
import com.nisemoe.generated.enums.JudgementType
|
||||||
import org.nisemoe.mari.judgements.Judgement
|
import org.nisemoe.mari.judgements.Judgement
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.OffsetDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -20,6 +21,11 @@ class Format {
|
|||||||
.format(targetFormatter)
|
.format(targetFormatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun formatOffsetDateTime(dateTime: OffsetDateTime): String {
|
||||||
|
return LocalDateTime.ofInstant(dateTime.toInstant(), ZoneOffset.UTC)
|
||||||
|
.format(targetFormatter)
|
||||||
|
}
|
||||||
|
|
||||||
fun parseStringToDate(dateTimeStr: String): Date {
|
fun parseStringToDate(dateTimeStr: String): Date {
|
||||||
val localDateTime = LocalDateTime.parse(dateTimeStr, targetFormatter)
|
val localDateTime = LocalDateTime.parse(dateTimeStr, targetFormatter)
|
||||||
return Date.from(localDateTime.atZone(ZoneOffset.UTC).toInstant())
|
return Date.from(localDateTime.atZone(ZoneOffset.UTC).toInstant())
|
||||||
|
|||||||
@ -0,0 +1,87 @@
|
|||||||
|
package com.nisemoe.nise.search
|
||||||
|
|
||||||
|
import org.jooq.Condition
|
||||||
|
import org.jooq.Field
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
class PredicateBuilder {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun buildBooleanCondition(field: Field<Boolean>, operator: String, value: Boolean): Condition {
|
||||||
|
return when (operator) {
|
||||||
|
"=" -> field.eq(value)
|
||||||
|
"!=" -> field.ne(value)
|
||||||
|
else -> throw IllegalArgumentException("Invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildGradeCondition(field: Field<String>, operator: String, value: String): Condition {
|
||||||
|
return when (value) {
|
||||||
|
"SS", "S", "A", "B", "C", "D" -> {
|
||||||
|
val valuesToMatch = when (value) {
|
||||||
|
"SS" -> listOf("Grade.SS", "Grade.SSH")
|
||||||
|
"S" -> listOf("Grade.S", "Grade.SH")
|
||||||
|
else -> listOf("Grade.$value")
|
||||||
|
}
|
||||||
|
when (operator) {
|
||||||
|
"=" -> field.`in`(valuesToMatch)
|
||||||
|
"!=" -> field.notIn(valuesToMatch)
|
||||||
|
else -> throw IllegalArgumentException("Invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Invalid grade value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildNumberCondition(field: Field<Double>, operator: String, value: Double): Condition {
|
||||||
|
return when (operator) {
|
||||||
|
"=" -> field.eq(value)
|
||||||
|
">" -> field.gt(value)
|
||||||
|
"<" -> field.lt(value)
|
||||||
|
">=" -> field.ge(value)
|
||||||
|
"<=" -> field.le(value)
|
||||||
|
"!=" -> field.ne(value)
|
||||||
|
else -> throw IllegalArgumentException("Invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildStringCondition(field: Field<String>, operator: String, value: String): Condition {
|
||||||
|
return when (operator.lowercase()) {
|
||||||
|
"=" -> field.eq(value)
|
||||||
|
"contains" -> field.containsIgnoreCase(value)
|
||||||
|
"like" -> field.likeIgnoreCase(
|
||||||
|
// Escape special characters for LIKE if needed
|
||||||
|
value.replace("%", "\\%").replace("_", "\\_")
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildDatetimeCondition(field: Field<String>, operator: String, value: String): Condition {
|
||||||
|
|
||||||
|
if(field.type == OffsetDateTime::class.java) {
|
||||||
|
val fieldOffset = field as Field<OffsetDateTime>
|
||||||
|
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")
|
||||||
|
val parsedDate = OffsetDateTime.parse(value, formatter.withZone(ZoneOffset.UTC))
|
||||||
|
|
||||||
|
return when (operator.lowercase()) {
|
||||||
|
"before" -> fieldOffset.lessThan(parsedDate)
|
||||||
|
"after" -> fieldOffset.greaterThan(parsedDate)
|
||||||
|
else -> throw IllegalArgumentException("Invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (operator.lowercase()) {
|
||||||
|
"before" -> field.lessThan(value)
|
||||||
|
"after" -> field.greaterThan(value)
|
||||||
|
else -> throw IllegalArgumentException("Invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -63,6 +63,7 @@ class ScoreSearchController(
|
|||||||
val user_count_50: Long?,
|
val user_count_50: Long?,
|
||||||
val user_count_miss: Long?,
|
val user_count_miss: Long?,
|
||||||
val user_is_banned: Boolean?,
|
val user_is_banned: Boolean?,
|
||||||
|
val user_approx_ban_date: String?,
|
||||||
|
|
||||||
// Score fields
|
// Score fields
|
||||||
val id: Int?,
|
val id: Int?,
|
||||||
|
|||||||
@ -35,6 +35,7 @@ class ScoreSearchSchemaController(
|
|||||||
InternalSchemaField("user_count_50", "50s", Category.user, Type.number, false, "number of 50 hits", databaseField = USERS.COUNT_50),
|
InternalSchemaField("user_count_50", "50s", Category.user, Type.number, false, "number of 50 hits", databaseField = USERS.COUNT_50),
|
||||||
InternalSchemaField("user_count_miss", "Misses", Category.user, Type.number, false, "missed hits", databaseField = USERS.COUNT_MISS),
|
InternalSchemaField("user_count_miss", "Misses", Category.user, Type.number, false, "missed hits", databaseField = USERS.COUNT_MISS),
|
||||||
InternalSchemaField("user_is_banned", "Is Banned", Category.user, Type.boolean, false, "is the user banned?", databaseField = USERS.IS_BANNED),
|
InternalSchemaField("user_is_banned", "Is Banned", Category.user, Type.boolean, false, "is the user banned?", databaseField = USERS.IS_BANNED),
|
||||||
|
InternalSchemaField("user_approx_ban_date", "Approx. ban date", Category.user, Type.datetime, false, "approximate date the user was banned at.", databaseField = USERS.APPROX_BAN_DATE),
|
||||||
|
|
||||||
// Score fields
|
// Score fields
|
||||||
InternalSchemaField("is_banned", "Banned", Category.score, Type.boolean, false, "has to score been deleted?", databaseField = SCORES.IS_BANNED),
|
InternalSchemaField("is_banned", "Banned", Category.score, Type.boolean, false, "has to score been deleted?", databaseField = SCORES.IS_BANNED),
|
||||||
|
|||||||
@ -5,6 +5,11 @@ import com.nisemoe.generated.tables.references.SCORES
|
|||||||
import com.nisemoe.generated.tables.references.USERS
|
import com.nisemoe.generated.tables.references.USERS
|
||||||
import com.nisemoe.nise.Format
|
import com.nisemoe.nise.Format
|
||||||
import com.nisemoe.nise.osu.Mod
|
import com.nisemoe.nise.osu.Mod
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildBooleanCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildDatetimeCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildGradeCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildNumberCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildStringCondition
|
||||||
import com.nisemoe.nise.service.AuthService
|
import com.nisemoe.nise.service.AuthService
|
||||||
import org.jooq.*
|
import org.jooq.*
|
||||||
import org.jooq.impl.DSL
|
import org.jooq.impl.DSL
|
||||||
@ -47,6 +52,7 @@ class ScoreSearchService(
|
|||||||
USERS.COUNT_50,
|
USERS.COUNT_50,
|
||||||
USERS.COUNT_MISS,
|
USERS.COUNT_MISS,
|
||||||
USERS.IS_BANNED,
|
USERS.IS_BANNED,
|
||||||
|
USERS.APPROX_BAN_DATE,
|
||||||
|
|
||||||
// Scores fields
|
// Scores fields
|
||||||
SCORES.ID,
|
SCORES.ID,
|
||||||
@ -155,6 +161,7 @@ class ScoreSearchService(
|
|||||||
user_count_50 = it.get(USERS.COUNT_50),
|
user_count_50 = it.get(USERS.COUNT_50),
|
||||||
user_count_miss = it.get(USERS.COUNT_MISS),
|
user_count_miss = it.get(USERS.COUNT_MISS),
|
||||||
user_is_banned = it.get(USERS.IS_BANNED),
|
user_is_banned = it.get(USERS.IS_BANNED),
|
||||||
|
user_approx_ban_date = it.get(USERS.APPROX_BAN_DATE)?.let { it1 -> Format.formatOffsetDateTime(it1) },
|
||||||
|
|
||||||
// Score fields
|
// Score fields
|
||||||
id = it.get(SCORES.ID),
|
id = it.get(SCORES.ID),
|
||||||
@ -260,64 +267,4 @@ class ScoreSearchService(
|
|||||||
}
|
}
|
||||||
return databaseField.databaseField!!
|
return databaseField.databaseField!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildBooleanCondition(field: Field<Boolean>, operator: String, value: Boolean): Condition {
|
|
||||||
return when (operator) {
|
|
||||||
"=" -> field.eq(value)
|
|
||||||
"!=" -> field.ne(value)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildGradeCondition(field: Field<String>, operator: String, value: String): Condition {
|
|
||||||
return when (value) {
|
|
||||||
"SS", "S", "A", "B", "C", "D" -> {
|
|
||||||
val valuesToMatch = when (value) {
|
|
||||||
"SS" -> listOf("Grade.SS", "Grade.SSH")
|
|
||||||
"S" -> listOf("Grade.S", "Grade.SH")
|
|
||||||
else -> listOf("Grade.$value")
|
|
||||||
}
|
|
||||||
when (operator) {
|
|
||||||
"=" -> field.`in`(valuesToMatch)
|
|
||||||
"!=" -> field.notIn(valuesToMatch)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException("Invalid grade value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildNumberCondition(field: Field<Double>, operator: String, value: Double): Condition {
|
|
||||||
return when (operator) {
|
|
||||||
"=" -> field.eq(value)
|
|
||||||
">" -> field.gt(value)
|
|
||||||
"<" -> field.lt(value)
|
|
||||||
">=" -> field.ge(value)
|
|
||||||
"<=" -> field.le(value)
|
|
||||||
"!=" -> field.ne(value)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildStringCondition(field: Field<String>, operator: String, value: String): Condition {
|
|
||||||
return when (operator.lowercase()) {
|
|
||||||
"=" -> field.eq(value)
|
|
||||||
"contains" -> field.containsIgnoreCase(value)
|
|
||||||
"like" -> field.likeIgnoreCase(
|
|
||||||
// Escape special characters for LIKE if needed
|
|
||||||
value.replace("%", "\\%").replace("_", "\\_")
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildDatetimeCondition(field: Field<String>, operator: String, value: String): Condition {
|
|
||||||
return when (operator.lowercase()) {
|
|
||||||
"before" -> field.lessThan(value)
|
|
||||||
"after" -> field.greaterThan(value)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -61,7 +61,8 @@ class UserSearchController(
|
|||||||
val count_100: Long?,
|
val count_100: Long?,
|
||||||
val count_50: Long?,
|
val count_50: Long?,
|
||||||
val count_miss: Long?,
|
val count_miss: Long?,
|
||||||
val is_banned: Boolean?
|
val is_banned: Boolean?,
|
||||||
|
val approx_ban_date: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SearchRequest(
|
data class SearchRequest(
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class UserSearchSchemaController(
|
|||||||
InternalSchemaField("count_50", "50s", Category.user, Type.number, false, "number of 50 hits", databaseField = USERS.COUNT_50),
|
InternalSchemaField("count_50", "50s", Category.user, Type.number, false, "number of 50 hits", databaseField = USERS.COUNT_50),
|
||||||
InternalSchemaField("count_miss", "Misses", Category.user, Type.number, false, "missed hits", databaseField = USERS.COUNT_MISS),
|
InternalSchemaField("count_miss", "Misses", Category.user, Type.number, false, "missed hits", databaseField = USERS.COUNT_MISS),
|
||||||
InternalSchemaField("is_banned", "Is Banned", Category.user, Type.boolean, false, "is the user banned?", databaseField = USERS.IS_BANNED),
|
InternalSchemaField("is_banned", "Is Banned", Category.user, Type.boolean, false, "is the user banned?", databaseField = USERS.IS_BANNED),
|
||||||
|
InternalSchemaField("approx_ban_date", "Approx. ban date", Category.user, Type.datetime, false, "approximate date the user was banned at.", databaseField = USERS.APPROX_BAN_DATE),
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,17 @@ package com.nisemoe.nise.search.user
|
|||||||
import com.nisemoe.generated.tables.references.SCORES
|
import com.nisemoe.generated.tables.references.SCORES
|
||||||
import com.nisemoe.generated.tables.references.USERS
|
import com.nisemoe.generated.tables.references.USERS
|
||||||
import com.nisemoe.nise.Format
|
import com.nisemoe.nise.Format
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildBooleanCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildDatetimeCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildGradeCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildNumberCondition
|
||||||
|
import com.nisemoe.nise.search.PredicateBuilder.Companion.buildStringCondition
|
||||||
import org.jooq.*
|
import org.jooq.*
|
||||||
import org.jooq.impl.DSL
|
import org.jooq.impl.DSL
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class UserSearchService(
|
class UserSearchService(
|
||||||
private val dslContext: DSLContext
|
private val dslContext: DSLContext
|
||||||
@ -41,7 +47,8 @@ class UserSearchService(
|
|||||||
USERS.COUNT_100,
|
USERS.COUNT_100,
|
||||||
USERS.COUNT_50,
|
USERS.COUNT_50,
|
||||||
USERS.COUNT_MISS,
|
USERS.COUNT_MISS,
|
||||||
USERS.IS_BANNED
|
USERS.IS_BANNED,
|
||||||
|
USERS.APPROX_BAN_DATE
|
||||||
)
|
)
|
||||||
|
|
||||||
val query = dslContext
|
val query = dslContext
|
||||||
@ -93,7 +100,8 @@ class UserSearchService(
|
|||||||
count_100 = it.get(USERS.COUNT_100),
|
count_100 = it.get(USERS.COUNT_100),
|
||||||
count_50 = it.get(USERS.COUNT_50),
|
count_50 = it.get(USERS.COUNT_50),
|
||||||
count_miss = it.get(USERS.COUNT_MISS),
|
count_miss = it.get(USERS.COUNT_MISS),
|
||||||
is_banned = it.get(USERS.IS_BANNED)
|
is_banned = it.get(USERS.IS_BANNED),
|
||||||
|
approx_ban_date = it.get(USERS.APPROX_BAN_DATE)?.let { it1 -> Format.formatOffsetDateTime(it1) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,63 +162,4 @@ class UserSearchService(
|
|||||||
return databaseField.databaseField!!
|
return databaseField.databaseField!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildBooleanCondition(field: Field<Boolean>, operator: String, value: Boolean): Condition {
|
|
||||||
return when (operator) {
|
|
||||||
"=" -> field.eq(value)
|
|
||||||
"!=" -> field.ne(value)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildGradeCondition(field: Field<String>, operator: String, value: String): Condition {
|
|
||||||
return when (value) {
|
|
||||||
"SS", "S", "A", "B", "C", "D" -> {
|
|
||||||
val valuesToMatch = when (value) {
|
|
||||||
"SS" -> listOf("Grade.SS", "Grade.SSH")
|
|
||||||
"S" -> listOf("Grade.S", "Grade.SH")
|
|
||||||
else -> listOf("Grade.$value")
|
|
||||||
}
|
|
||||||
when (operator) {
|
|
||||||
"=" -> field.`in`(valuesToMatch)
|
|
||||||
"!=" -> field.notIn(valuesToMatch)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException("Invalid grade value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildNumberCondition(field: Field<Double>, operator: String, value: Double): Condition {
|
|
||||||
return when (operator) {
|
|
||||||
"=" -> field.eq(value)
|
|
||||||
">" -> field.gt(value)
|
|
||||||
"<" -> field.lt(value)
|
|
||||||
">=" -> field.ge(value)
|
|
||||||
"<=" -> field.le(value)
|
|
||||||
"!=" -> field.ne(value)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildStringCondition(field: Field<String>, operator: String, value: String): Condition {
|
|
||||||
return when (operator.lowercase()) {
|
|
||||||
"=" -> field.eq(value)
|
|
||||||
"contains" -> field.containsIgnoreCase(value)
|
|
||||||
"like" -> field.likeIgnoreCase(
|
|
||||||
// Escape special characters for LIKE if needed
|
|
||||||
value.replace("%", "\\%").replace("_", "\\_")
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildDatetimeCondition(field: Field<String>, operator: String, value: String): Condition {
|
|
||||||
return when (operator.lowercase()) {
|
|
||||||
"before" -> field.lessThan(value)
|
|
||||||
"after" -> field.greaterThan(value)
|
|
||||||
else -> throw IllegalArgumentException("Invalid operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user