From 6e0b89a2b94463af354c45f62e9adca58bfa72ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 6 Jun 2026 23:04:03 +0200 Subject: [PATCH] feat(dsl): Add $mod (filter) --- .../commonMain/kotlin/query/FilterQuery.kt | 64 +++++++++ .../kotlin/query/FilterQueryPredicate.kt | 31 +++++ .../kotlin/query/FilterQueryPredicateImpl.kt | 24 ++++ .../commonMain/kotlin/query/FilterQuery.kt | 124 ++++++++++++++++++ .../kotlin/query/FilterQueryPredicate.kt | 31 +++++ .../kotlin/query/FilterQueryPredicateImpl.kt | 24 ++++ .../query/filter/ComparisonFilterTest.kt | 12 ++ 7 files changed, 310 insertions(+) diff --git a/dsl-template/src/commonMain/kotlin/query/FilterQuery.kt b/dsl-template/src/commonMain/kotlin/query/FilterQuery.kt index 6a3daa88..a1eae401 100644 --- a/dsl-template/src/commonMain/kotlin/query/FilterQuery.kt +++ b/dsl-template/src/commonMain/kotlin/query/FilterQuery.kt @@ -111,6 +111,7 @@ import kotlin.reflect.typeOf * - [`$lt`][lt] * - [`$lte`][lte] * - [`$ne`][ne] + * - [`$mod`][mod] * * Logical query: * - [`$and`][and] @@ -1526,6 +1527,69 @@ interface FilterQuery : CompoundBsonNode, FieldDsl { } // endregion + // endregion + // region $mod + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age.mod(2, 1) + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun Field.mod(divisor: Long, remainder: Long) { + this { mod(divisor, remainder) } + } + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age.mod(2, 1) + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun Field.mod(divisor: Int, remainder: Int) { + this.mod(divisor.toLong(), remainder.toLong()) + } + // endregion // region $in diff --git a/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicate.kt b/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicate.kt index 1e4f670a..34499786 100644 --- a/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicate.kt +++ b/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicate.kt @@ -637,6 +637,37 @@ interface FilterQueryPredicate : CompoundBsonNode, FieldDsl { lte(value) } + // endregion + // region $mod + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age { mod(2, 1) } + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun mod(divisor: Long, remainder: Long) + // endregion // region $in diff --git a/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt b/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt index 3c98412d..3c1e8665 100644 --- a/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt +++ b/dsl-template/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt @@ -250,6 +250,30 @@ private class FilterQueryPredicateImpl( } } + // endregion + // region $mod + + @OptIn(LowLevelApi::class, DangerousMongoApi::class) + override fun mod(divisor: Long, remainder: Long) { + accept(ModPredicateBsonNode(divisor, remainder, context)) + } + + @LowLevelApi + private class ModPredicateBsonNode( + private val divisor: Long, + private val remainder: Long, + context: BsonContext, + ) : PredicateBsonNodeNode(context) { + + @LowLevelApi + override fun write(writer: BsonFieldWriter) = with(writer) { + writeArray($$"$mod") { + writeSafe(divisor) + writeSafe(remainder) + } + } + } + // endregion // region $in diff --git a/dsl/src/commonMain/kotlin/query/FilterQuery.kt b/dsl/src/commonMain/kotlin/query/FilterQuery.kt index f72f67b5..9d21ba8c 100644 --- a/dsl/src/commonMain/kotlin/query/FilterQuery.kt +++ b/dsl/src/commonMain/kotlin/query/FilterQuery.kt @@ -114,6 +114,7 @@ import kotlin.reflect.typeOf * - [`$lt`][lt] * - [`$lte`][lte] * - [`$ne`][ne] + * - [`$mod`][mod] * * Logical query: * - [`$and`][and] @@ -2308,6 +2309,129 @@ interface FilterQuery : CompoundBsonNode, FieldDsl { } // endregion + // endregion + // region $mod + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age.mod(2, 1) + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun Field.mod(divisor: Long, remainder: Long) { + this { mod(divisor, remainder) } + } + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age.mod(2, 1) + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun kotlin.reflect.KProperty1.mod(divisor: Long, remainder: Long) { + return this.field.mod(divisor, remainder) + } + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age.mod(2, 1) + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun Field.mod(divisor: Int, remainder: Int) { + this.mod(divisor.toLong(), remainder.toLong()) + } + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age.mod(2, 1) + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun kotlin.reflect.KProperty1.mod(divisor: Int, remainder: Int) { + return this.field.mod(divisor, remainder) + } + // endregion // region $in diff --git a/dsl/src/commonMain/kotlin/query/FilterQueryPredicate.kt b/dsl/src/commonMain/kotlin/query/FilterQueryPredicate.kt index fe7892ad..78807330 100644 --- a/dsl/src/commonMain/kotlin/query/FilterQueryPredicate.kt +++ b/dsl/src/commonMain/kotlin/query/FilterQueryPredicate.kt @@ -640,6 +640,37 @@ interface FilterQueryPredicate : CompoundBsonNode, FieldDsl { lte(value) } + // endregion + // region $mod + + /** + * Selects documents where the value of the field divided by [divisor] has the specified [remainder]. + * + * If the value of the field is a floating-point number, it is rounded towards zero before the operation. + * + * ### Example + * + * To find all users with an odd score: + * ```kotlin + * class User( + * val name: String, + * val age: Int?, + * ) + * + * collection.find { + * User::age { mod(2, 1) } + * } + * ``` + * + * This request returns elements where `age % 2 == 1`. + * + * ### External resources + * + * - [Official documentation](https://www.mongodb.com/docs/manual/reference/operator/query/mod/) + */ + @KtMongoDsl + fun mod(divisor: Long, remainder: Long) + // endregion // region $in diff --git a/dsl/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt b/dsl/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt index c102f8b5..114a7044 100644 --- a/dsl/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt +++ b/dsl/src/commonMain/kotlin/query/FilterQueryPredicateImpl.kt @@ -253,6 +253,30 @@ private class FilterQueryPredicateImpl( } } + // endregion + // region $mod + + @OptIn(LowLevelApi::class, DangerousMongoApi::class) + override fun mod(divisor: Long, remainder: Long) { + accept(ModPredicateBsonNode(divisor, remainder, context)) + } + + @LowLevelApi + private class ModPredicateBsonNode( + private val divisor: Long, + private val remainder: Long, + context: BsonContext, + ) : PredicateBsonNodeNode(context) { + + @LowLevelApi + override fun write(writer: BsonFieldWriter) = with(writer) { + writeArray($$"$mod") { + writeSafe(divisor) + writeSafe(remainder) + } + } + } + // endregion // region $in diff --git a/dsl/src/commonTest/kotlin/query/filter/ComparisonFilterTest.kt b/dsl/src/commonTest/kotlin/query/filter/ComparisonFilterTest.kt index 4c69167b..dfadaaf0 100644 --- a/dsl/src/commonTest/kotlin/query/filter/ComparisonFilterTest.kt +++ b/dsl/src/commonTest/kotlin/query/filter/ComparisonFilterTest.kt @@ -278,4 +278,16 @@ val ComparisonFilterTest by multiContextSuite { """.trimIndent() } } + + test($$"$mod") { + filter { + (User::pets / Pet::age).mod(2, 0) + } shouldBeBson $$""" + { + "pets.age": { + "$mod": [2, 0] + } + } + """.trimIndent() + } } -- 2.51.2