From 2b5ab1c0547da1be1da797d6343ebed966217e2b Mon Sep 17 00:00:00 2001 From: "nise.moe" Date: Fri, 14 Jun 2024 12:59:02 +0200 Subject: [PATCH] Search by user ban_date, refactor of search --- .../main/kotlin/com/nisemoe/nise/Format.kt | 6 ++ .../nisemoe/nise/search/PredicateBuilder.kt | 87 +++++++++++++++++++ .../search/score/ScoreSearchController.kt | 1 + .../score/ScoreSearchSchemaController.kt | 1 + .../nise/search/score/ScoreSearchService.kt | 67 ++------------ .../nise/search/user/UserSearchController.kt | 3 +- .../search/user/UserSearchSchemaController.kt | 3 +- .../nise/search/user/UserSearchService.kt | 71 +++------------ 8 files changed, 116 insertions(+), 123 deletions(-) create mode 100644 nise-backend/src/main/kotlin/com/nisemoe/nise/search/PredicateBuilder.kt diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/Format.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/Format.kt index 6761b7f..193b76a 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/Format.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/Format.kt @@ -3,6 +3,7 @@ package com.nisemoe.nise import com.nisemoe.generated.enums.JudgementType import org.nisemoe.mari.judgements.Judgement import java.time.LocalDateTime +import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter import java.util.* @@ -20,6 +21,11 @@ class Format { .format(targetFormatter) } + fun formatOffsetDateTime(dateTime: OffsetDateTime): String { + return LocalDateTime.ofInstant(dateTime.toInstant(), ZoneOffset.UTC) + .format(targetFormatter) + } + fun parseStringToDate(dateTimeStr: String): Date { val localDateTime = LocalDateTime.parse(dateTimeStr, targetFormatter) return Date.from(localDateTime.atZone(ZoneOffset.UTC).toInstant()) diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/PredicateBuilder.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/PredicateBuilder.kt new file mode 100644 index 0000000..a1591a0 --- /dev/null +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/PredicateBuilder.kt @@ -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, operator: String, value: Boolean): Condition { + return when (operator) { + "=" -> field.eq(value) + "!=" -> field.ne(value) + else -> throw IllegalArgumentException("Invalid operator") + } + } + + fun buildGradeCondition(field: Field, 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, 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, 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, operator: String, value: String): Condition { + + if(field.type == OffsetDateTime::class.java) { + val fieldOffset = field as Field + 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") + } + } + + } + +} \ No newline at end of file diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchController.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchController.kt index 33340ed..444f93a 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchController.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchController.kt @@ -63,6 +63,7 @@ class ScoreSearchController( val user_count_50: Long?, val user_count_miss: Long?, val user_is_banned: Boolean?, + val user_approx_ban_date: String?, // Score fields val id: Int?, diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchSchemaController.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchSchemaController.kt index cd419c4..683d799 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchSchemaController.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchSchemaController.kt @@ -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_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_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 InternalSchemaField("is_banned", "Banned", Category.score, Type.boolean, false, "has to score been deleted?", databaseField = SCORES.IS_BANNED), diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchService.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchService.kt index d93b33b..267ad3a 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchService.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/score/ScoreSearchService.kt @@ -5,6 +5,11 @@ import com.nisemoe.generated.tables.references.SCORES import com.nisemoe.generated.tables.references.USERS import com.nisemoe.nise.Format 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 org.jooq.* import org.jooq.impl.DSL @@ -47,6 +52,7 @@ class ScoreSearchService( USERS.COUNT_50, USERS.COUNT_MISS, USERS.IS_BANNED, + USERS.APPROX_BAN_DATE, // Scores fields SCORES.ID, @@ -155,6 +161,7 @@ class ScoreSearchService( user_count_50 = it.get(USERS.COUNT_50), user_count_miss = it.get(USERS.COUNT_MISS), 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 id = it.get(SCORES.ID), @@ -260,64 +267,4 @@ class ScoreSearchService( } return databaseField.databaseField!! } - - private fun buildBooleanCondition(field: Field, operator: String, value: Boolean): Condition { - return when (operator) { - "=" -> field.eq(value) - "!=" -> field.ne(value) - else -> throw IllegalArgumentException("Invalid operator") - } - } - - private fun buildGradeCondition(field: Field, 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, 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, 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, operator: String, value: String): Condition { - return when (operator.lowercase()) { - "before" -> field.lessThan(value) - "after" -> field.greaterThan(value) - else -> throw IllegalArgumentException("Invalid operator") - } - } - } \ No newline at end of file diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchController.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchController.kt index 83ab3a1..70e2688 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchController.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchController.kt @@ -61,7 +61,8 @@ class UserSearchController( val count_100: Long?, val count_50: Long?, val count_miss: Long?, - val is_banned: Boolean? + val is_banned: Boolean?, + val approx_ban_date: String?, ) data class SearchRequest( diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchSchemaController.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchSchemaController.kt index f4708b7..42316cc 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchSchemaController.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchSchemaController.kt @@ -33,7 +33,8 @@ class UserSearchSchemaController( 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("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), + ) } diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchService.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchService.kt index d7cd261..726281d 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchService.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/search/user/UserSearchService.kt @@ -3,11 +3,17 @@ package com.nisemoe.nise.search.user import com.nisemoe.generated.tables.references.SCORES import com.nisemoe.generated.tables.references.USERS 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.impl.DSL import org.springframework.stereotype.Service import kotlin.math.roundToInt + @Service class UserSearchService( private val dslContext: DSLContext @@ -41,7 +47,8 @@ class UserSearchService( USERS.COUNT_100, USERS.COUNT_50, USERS.COUNT_MISS, - USERS.IS_BANNED + USERS.IS_BANNED, + USERS.APPROX_BAN_DATE ) val query = dslContext @@ -93,7 +100,8 @@ class UserSearchService( count_100 = it.get(USERS.COUNT_100), count_50 = it.get(USERS.COUNT_50), 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!! } - private fun buildBooleanCondition(field: Field, operator: String, value: Boolean): Condition { - return when (operator) { - "=" -> field.eq(value) - "!=" -> field.ne(value) - else -> throw IllegalArgumentException("Invalid operator") - } - } - - private fun buildGradeCondition(field: Field, 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, 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, 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, operator: String, value: String): Condition { - return when (operator.lowercase()) { - "before" -> field.lessThan(value) - "after" -> field.greaterThan(value) - else -> throw IllegalArgumentException("Invalid operator") - } - } - } \ No newline at end of file