Skip to content

Commit

Permalink
Merge pull request #615 from LeeJejune/feat/round
Browse files Browse the repository at this point in the history
Support Arithmetic function ROUND
  • Loading branch information
shouwn authored Jan 31, 2024
2 parents 475f6b5 + 48d2fa6 commit b673f19
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 2 deletions.
6 changes: 5 additions & 1 deletion docs/en/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ locate("Book", path(Book::title))
```

### Arithmetic functions
* round

```kotlin
round(path(Book::price), Expressions.value(Int::class))
```

Use the following functions to build arithmetic functions:

Expand All @@ -242,7 +247,6 @@ sqrt(path(Book::price))
| LN | not yet |
| MOD | not yet |
| POWER | not yet |
| ROUND | not yet |
| SIGN | not yet |
| SIZE | not yet |
| INDEX | not yet |
Expand Down
6 changes: 5 additions & 1 deletion docs/ko/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ locate("Book", path(Book::title))
```

### Arithmetic functions
* round

```kotlin
round(path(Book::price), Expressions.value(Int::class))
```

산술 함수를 만들기 위해서는 다음 함수들을 사용할 수 있습니다.

Expand All @@ -238,7 +243,6 @@ sqrt(path(Book::price))
| LN | not yet |
| MOD | not yet |
| POWER | not yet |
| ROUND | not yet |
| SIGN | not yet |
| SIZE | not yet |
| INDEX | not yet |
Expand Down
16 changes: 16 additions & 0 deletions dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,22 @@ open class Jpql : JpqlDsl {
fun <T : Number> sqrt(value: Expressionable<T>): Expression<Double> {
return Expressions.sqrt(value.toExpression())
}

/**
* Creates an expression that represents the rounding of the value to a specified scale.
*/
@SinceJdsl("3.4.0")
fun <T : Any, V : Number> round(expr: KProperty1<T, @Exact V>, scale: Expression<Int>): Expression<V> {
return Expressions.round(Paths.path(expr), scale.toExpression())
}

/**
* Creates an expression that represents the rounding of the value to a specified scale.
*/
@SinceJdsl("3.4.0")
fun <T : Number> round(value: Expressionable<T>, scale: Expressionable<Int>): Expression<T> {
return Expressions.round(value.toExpression(), scale.toExpression())
}

/**
* Creates an expression that represents the count of non-null values.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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 RoundDslTest : WithAssertions {
private val expression1 = Paths.path(Book::salePrice)
private val intExpression1: Expression<Int> = Expressions.value(3)
private val doubleExpression1: Expression<Double> = Expressions.value(3.0)
private val int1 = 3
private val double1 = 3.0

@Test
fun `round() with a BigDecimalExpression`() {
// when
val expression = queryPart {
round(expression1, intExpression1)
}.toExpression()

val actual: Expression<BigDecimal> = expression // for type check

// then
val expected = Expressions.round(
value = expression1,
scale = intExpression1,
)

assertThat(actual).isEqualTo(expected)
}

@Test
fun `round() with a BigDecimalProperty`() {
// when
val expression = queryPart {
round(Book::price, Expressions.value(int1))
}.toExpression()

val actual: Expression<BigDecimal> = expression // for type check

// then
val expected = Expressions.round(
value = Paths.path(Book::price),
scale = Expressions.value(int1),
)

assertThat(actual).isEqualTo(expected)
}

@Test
fun `round() with a DoubleExpression`() {
// when
val expression = queryPart {
round(doubleExpression1, intExpression1)
}.toExpression()

val actual: Expression<Double> = expression // for type check

// then
val expected = Expressions.round(
value = doubleExpression1,
scale = intExpression1,
)

assertThat(actual).isEqualTo(expected)
}

@Test
fun `round() with a DoubleProperty`() {
// when
val expression = queryPart {
round(Expressions.value(double1), Expressions.value(int1))
}.toExpression()

val actual: Expression<Double> = expression // for type check

// then
val expected = Expressions.round(
value = Expressions.value(double1),
scale = Expressions.value(int1),
)

assertThat(actual).isEqualTo(expected)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlTrimLeading
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlTrimTrailing
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlUpper
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlValue
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
import com.linecorp.kotlinjdsl.querymodel.jpql.path.Path
import com.linecorp.kotlinjdsl.querymodel.jpql.predicate.Predicate
import com.linecorp.kotlinjdsl.querymodel.jpql.select.SelectQuery
Expand Down Expand Up @@ -273,6 +274,14 @@ object Expressions {
return JpqlAvg(distinct, expr)
}

/**
* Creates an expression that represents the rounding of the specified value to a specified scale.
*/
@SinceJdsl("3.4.0")
fun <T : Number> round(value: Expression<T>, scale: Expression<Int>): Expression<T> {
return JpqlRound(value, scale)
}

/**
* Creates an expression that represents the sum of values.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl

import com.linecorp.kotlinjdsl.Internal
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression

/**
* Expression that calculates the rounding of a numeric [value] to a specified [scale].
*/
@Internal
data class JpqlRound<T: Number> internal constructor(
val value: Expression<T>,
val scale: Expression<Int>,
) : Expression<T> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlNullIf
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlParam
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPathType
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPlus
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSqrt
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSubquery
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSubstring
Expand Down Expand Up @@ -374,6 +375,34 @@ class ExpressionsTest : WithAssertions {
assertThat(actual).isEqualTo(expected)
}

@Test
fun `round() with BigDecimalExpression`() {
// when
val actual = Expressions.round(bigDecimalExpression1, intExpression1)

// then
val expected = JpqlRound(
bigDecimalExpression1,
intExpression1
)

assertThat(actual).isEqualTo(expected)
}

@Test
fun `round() with doubleExpression`() {
// when
val actual = Expressions.round(doubleExpression1, intExpression1)

// then
val expected = JpqlRound(
doubleExpression1,
intExpression1
)

assertThat(actual).isEqualTo(expected)
}

@ParameterizedTest
@ValueSource(booleans = [true, false])
fun count(distinct: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathTreatSerializ
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathTypeSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPlusSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPredicateParenthesesSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlRoundSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlSelectQuerySerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlSortSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlSqrtSerializer
Expand Down Expand Up @@ -343,6 +344,7 @@ private class DefaultModule : JpqlRenderModule {
JpqlPathTypeSerializer(),
JpqlPlusSerializer(),
JpqlPredicateParenthesesSerializer(),
JpqlRoundSerializer(),
JpqlSelectQuerySerializer(),
JpqlSortSerializer(),
JpqlSqrtSerializer(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.linecorp.kotlinjdsl.render.jpql.serializer.impl

import com.linecorp.kotlinjdsl.Internal
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
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 JpqlRoundSerializer : JpqlSerializer<JpqlRound<*>>{
override fun handledType(): KClass<JpqlRound<*>> {
return JpqlRound::class
}

override fun serialize(part: JpqlRound<*>, writer: JpqlWriter, context: RenderContext) {
val delegate = context.getValue(JpqlRenderSerializer)

writer.write("ROUND")

writer.writeParentheses {
delegate.serialize(part.value, writer, context)

writer.write(", ")
delegate.serialize(part.scale, writer, context)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.linecorp.kotlinjdsl.render.jpql.serializer.impl

import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
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.Assertions.assertThat
import org.junit.jupiter.api.Test

@JpqlSerializerTest
class JpqlRoundSerializerTest {
private val sut = JpqlRoundSerializer()

@MockK
private lateinit var writer: JpqlWriter

@MockK
private lateinit var serializer: JpqlRenderSerializer

private val expression1 = Paths.path(Book::price)
private val expression2 = Expressions.value(3)

@Test
fun handledType() {
// when
val actual = sut.handledType()

// then
assertThat(actual).isEqualTo(JpqlRound::class)
}
@Test
fun serialize() {
// given
val part = Expressions.round(
value = expression1,
scale = expression2
)
val context = TestRenderContext(serializer)

// when
sut.serialize(part as JpqlRound<*>, writer, context)

// then
verifySequence {
writer.write("ROUND")
writer.writeParentheses(any())
serializer.serialize(expression1, writer, context)
writer.write(", ")
serializer.serialize(expression2, writer, context)
}
}
}

0 comments on commit b673f19

Please sign in to comment.