From 8622b29bf142aaf24041cdc17583f189d73ecbe7 Mon Sep 17 00:00:00 2001 From: sinkyoungdeok Date: Wed, 31 Jan 2024 00:33:13 +0900 Subject: [PATCH 1/2] docs: add floor function description --- docs/en/jpql-with-kotlin-jdsl/expressions.md | 3 +++ docs/ko/jpql-with-kotlin-jdsl/expressions.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/en/jpql-with-kotlin-jdsl/expressions.md b/docs/en/jpql-with-kotlin-jdsl/expressions.md index a3f0f2995..af8620116 100644 --- a/docs/en/jpql-with-kotlin-jdsl/expressions.md +++ b/docs/en/jpql-with-kotlin-jdsl/expressions.md @@ -225,11 +225,14 @@ Use the following functions to build arithmetic functions: * ABS (abs) * CEILING (ceiling) +* FLOOR (floor) ```kotlin abs(path(Book::price)) ceiling(path(Book::price)) + +floor(path(Book::price)) ``` | Function | DSL function | diff --git a/docs/ko/jpql-with-kotlin-jdsl/expressions.md b/docs/ko/jpql-with-kotlin-jdsl/expressions.md index 8726fef12..ab828352f 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/expressions.md +++ b/docs/ko/jpql-with-kotlin-jdsl/expressions.md @@ -222,11 +222,14 @@ locate("Book", path(Book::title)) * ABS (abs) * CEILING (ceiling) +* FLOOR (floor) ```kotlin abs(path(Book::price)) ceiling(path(Book::price)) + +floor(path(Book::price)) ``` | Function | DSL function | From 0a746b452bbcd30c9e41c5bbb9f6ef3ece8e56db Mon Sep 17 00:00:00 2001 From: sinkyoungdeok Date: Wed, 31 Jan 2024 00:33:30 +0900 Subject: [PATCH 2/2] feat: implement floor function expression --- .../com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt | 16 ++++++ .../dsl/jpql/expression/FloorDslTest.kt | 48 ++++++++++++++++ .../querymodel/jpql/expression/Expressions.kt | 9 +++ .../jpql/expression/impl/JpqlFloor.kt | 12 ++++ .../jpql/expression/ExpressionsTest.kt | 14 +++++ .../render/jpql/JpqlRenderContext.kt | 2 + .../serializer/impl/JpqlFloorSerializer.kt | 26 +++++++++ .../impl/JpqlFloorSerializerTest.kt | 55 +++++++++++++++++++ 8 files changed, 182 insertions(+) create mode 100644 dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FloorDslTest.kt create mode 100644 query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlFloor.kt create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializer.kt create mode 100644 render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializerTest.kt diff --git a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt index c6bd4b53e..f0fe0889e 100644 --- a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt +++ b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt @@ -568,6 +568,22 @@ open class Jpql : JpqlDsl { return Expressions.ceiling(value.toExpression()) } + /** + * Creates an expression that is enclosed in floor + */ + @SinceJdsl("3.4.0") + fun floor(expr: KProperty1): Expression { + return Expressions.floor(Paths.path(expr)) + } + + /** + * Creates an expression that is enclosed in floor + */ + @SinceJdsl("3.4.0") + fun floor(value: Expressionable): Expression { + return Expressions.floor(value.toExpression()) + } + /** * Creates an expression that represents the count of non-null values. * diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FloorDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FloorDslTest.kt new file mode 100644 index 000000000..4205b5c8a --- /dev/null +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FloorDslTest.kt @@ -0,0 +1,48 @@ +package com.linecorp.kotlinjdsl.dsl.jpql.expression + +import com.linecorp.kotlinjdsl.dsl.jpql.entity.book.Book +import com.linecorp.kotlinjdsl.dsl.jpql.queryPart +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions +import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths +import org.assertj.core.api.WithAssertions +import org.junit.jupiter.api.Test +import java.math.BigDecimal + +class FloorDslTest : WithAssertions { + private val expression1 = Paths.path(Book::salePrice) + + @Test + fun `floor() with a property`() { + // when + val expression = queryPart { + floor(Book::price) + }.toExpression() + + val actual: Expression = expression // for type check + + // then + val expected = Expressions.floor( + value = Paths.path(Book::price), + ) + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `floor() with a expression`() { + // when + val expression = queryPart { + floor(expression1) + }.toExpression() + + val actual: Expression = expression // for type check + + // then + val expected = Expressions.floor( + value = expression1, + ) + + assertThat(actual).isEqualTo(expected) + } +} diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt index fb06e5926..fe8a4487e 100644 --- a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt +++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt @@ -16,6 +16,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlDivide import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlEntityType import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlExpression import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlExpressionParentheses +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlFloor import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlFunctionExpression import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLength import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLiteral @@ -204,6 +205,14 @@ object Expressions { return JpqlCeiling(value) } + /** + * Creates an expression that is enclosed in floor + */ + @SinceJdsl("3.4.0") + fun floor(value: Expression): Expression { + return JpqlFloor(value) + } + /** * Creates an expression that represents the times of values. * diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlFloor.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlFloor.kt new file mode 100644 index 000000000..88364ccb8 --- /dev/null +++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlFloor.kt @@ -0,0 +1,12 @@ +package com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl + +import com.linecorp.kotlinjdsl.Internal +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression + +/** + * Expression that applies the floor function to [value]. + */ +@Internal +data class JpqlFloor internal constructor( + val value: Expression, +) : Expression diff --git a/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt b/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt index 9e1155a49..1c8402282 100644 --- a/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt +++ b/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt @@ -18,6 +18,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlDivide import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlEntityType import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlExpression import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlExpressionParentheses +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlFloor import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlFunctionExpression import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLength import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLiteral @@ -915,4 +916,17 @@ class ExpressionsTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun floor() { + // when + val actual = Expressions.floor(doubleExpression1) + + // then + val expected = JpqlFloor( + doubleExpression1, + ) + + assertThat(actual).isEqualTo(expected) + } } diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt index d17578ed3..4fd1c6960 100644 --- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt @@ -39,6 +39,7 @@ import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlEqualSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlExistsSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlExpressionParenthesesSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlExpressionSerializer +import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlFloorSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlFunctionExpressionSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlFunctionPredicateSerializer import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlGreaterThanAllSerializer @@ -286,6 +287,7 @@ private class DefaultModule : JpqlRenderModule { JpqlExistsSerializer(), JpqlExpressionParenthesesSerializer(), JpqlExpressionSerializer(), + JpqlFloorSerializer(), JpqlFunctionExpressionSerializer(), JpqlFunctionPredicateSerializer(), JpqlGreaterThanAllSerializer(), diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializer.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializer.kt new file mode 100644 index 000000000..04dd9157e --- /dev/null +++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializer.kt @@ -0,0 +1,26 @@ +package com.linecorp.kotlinjdsl.render.jpql.serializer.impl + +import com.linecorp.kotlinjdsl.Internal +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlFloor +import com.linecorp.kotlinjdsl.render.RenderContext +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer +import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter +import kotlin.reflect.KClass + +@Internal +class JpqlFloorSerializer : JpqlSerializer> { + override fun handledType(): KClass> { + return JpqlFloor::class + } + + override fun serialize(part: JpqlFloor<*>, writer: JpqlWriter, context: RenderContext) { + val delegate = context.getValue(JpqlRenderSerializer) + + writer.write("FLOOR") + + writer.writeParentheses { + delegate.serialize(part.value, writer, context) + } + } +} diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializerTest.kt new file mode 100644 index 000000000..528548a4a --- /dev/null +++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlFloorSerializerTest.kt @@ -0,0 +1,55 @@ +package com.linecorp.kotlinjdsl.render.jpql.serializer.impl + +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions +import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlFloor +import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths +import com.linecorp.kotlinjdsl.render.TestRenderContext +import com.linecorp.kotlinjdsl.render.jpql.entity.book.Book +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer +import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializerTest +import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter +import io.mockk.impl.annotations.MockK +import io.mockk.verifySequence +import org.assertj.core.api.WithAssertions +import org.junit.jupiter.api.Test + +@JpqlSerializerTest +class JpqlFloorSerializerTest : WithAssertions { + private val sut = JpqlFloorSerializer() + + @MockK + private lateinit var writer: JpqlWriter + + @MockK + private lateinit var serializer: JpqlRenderSerializer + + private val expression1 = Paths.path(Book::price) + + @Test + fun handledType() { + // when + val actual = sut.handledType() + + // then + assertThat(actual).isEqualTo(JpqlFloor::class) + } + + @Test + fun serialize() { + // given + val part = Expressions.floor( + value = expression1, + ) + val context = TestRenderContext(serializer) + + // when + sut.serialize(part as JpqlFloor<*>, writer, context) + + // then + verifySequence { + writer.write("FLOOR") + writer.writeParentheses(any()) + serializer.serialize(expression1, writer, context) + } + } +}