Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement power operator #651

Merged
merged 11 commits into from
Mar 12, 2024
8 changes: 4 additions & 4 deletions docs/en/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ locate("Book", path(Book::title))
* FLOOR (floor)
* INDEX (index)
* LN (ln)
* MOD (mod)
* POWER (power)
* SIGN (sign)
* SQRT (sqrt)
* ROUND (round)
Expand All @@ -247,6 +249,8 @@ ln(path(Book::price))

mod(path(Employee::age), 3)

power(path(Employee::age), 2)

sign(path(Book::price))

sqrt(path(Book::price))
Expand All @@ -256,10 +260,6 @@ round(path(Book::price), 2)
size(path(Book::authors))
```

| Function | DSL function |
|----------|--------------|
| POWER | not yet |

### Datetime functions

* CURRENT\_DATE (currentDate)
Expand Down
8 changes: 4 additions & 4 deletions docs/ko/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ locate("Book", path(Book::title))
* FLOOR (floor)
* INDEX (index)
* LN (ln)
* MOD (mod)
* POWER (power)
* SIGN (sign)
* SQRT (sqrt)
* ROUND (round)
Expand All @@ -243,6 +245,8 @@ ln(path(Book::price))

mod(path(Employee::age), 3)

power(path(Employee::age), 2)

sign(path(Book::price))

sqrt(path(Book::price))
Expand All @@ -252,10 +256,6 @@ round(path(Book::price), 2)
size(path(Book::authors))
```

| Function | DSL function |
|----------|--------------|
| POWER | not yet |

### Datetime functions

* CURRENT\_DATE (currentDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,38 @@ open class Jpql : JpqlDsl {
return Expressions.ln(value.toExpression())
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about writing something like this?

Suggested change
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
* Create an expression that represents the power of [base] and [exponent].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you. The sentence below looks easier to read.

*/
@SinceJdsl("3.4.0")
fun <T : Any, V : Number> power(base: KProperty1<T, @Exact V>, exponent: Number): Expression<Double> {
return Expressions.power(Paths.path(base), Expressions.value(exponent))
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Any, V : Number> power(base: KProperty1<T, @Exact V>, exponent: Expressionable<V>): Expression<Double> {
return Expressions.power(Paths.path(base), exponent.toExpression())
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Number> power(base: Expressionable<T>, exponent: T): Expression<Double> {
return Expressions.power(base.toExpression(), Expressions.value(exponent))
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Number> power(base: Expressionable<T>, exponent: Expressionable<T>): Expression<Double> {
return Expressions.power(base.toExpression(), exponent.toExpression())
}

/**
* Creates an expression that represents the sign of value.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.linecorp.kotlinjdsl.dsl.jpql.expression

import com.linecorp.kotlinjdsl.dsl.jpql.entity.employee.Employee
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

class PowerDslTest : WithAssertions {
private val intExpression1 = Paths.path(Employee::age)
private val intExpression2 = Expressions.value(2)

private val int1 = 2

@Test
fun `power() with a property`() {
// when
val expression1 = queryPart {
power(Employee::age, int1)
}.toExpression()

val expression2 = queryPart {
power(Employee::age, intExpression2)
}.toExpression()

val actual1: Expression<Number> = expression1 // for type check
val actual2: Expression<Number> = expression2 // for type check

// then
val expected1 = Expressions.power(
base = Paths.path(Employee::age),
exponent = Expressions.value(int1),
)

val expected2 = Expressions.power(
base = Paths.path(Employee::age),
exponent = intExpression2,
)

assertThat(actual1).isEqualTo(expected1)
assertThat(actual2).isEqualTo(expected2)
}

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

val expression2 = queryPart {
power(intExpression1, intExpression2)
}.toExpression()

val actual1: Expression<Number> = expression1 // for type check
val actual2: Expression<Number> = expression2 // for type check

// then
val expected1 = Expressions.power(
base = intExpression1,
exponent = Expressions.value(int1),
)

val expected2 = Expressions.power(
base = intExpression1,
exponent = intExpression2,
)

assertThat(actual1).isEqualTo(expected1)
assertThat(actual2).isEqualTo(expected2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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.JpqlPower
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSign
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSize
Expand Down Expand Up @@ -277,6 +278,14 @@ object Expressions {
return JpqlLn(value)
}

/**
* Creates expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Number> power(base: Expression<T>, exponent: Expression<T>): Expression<Double> {
return JpqlPower(base, exponent)
}

/**
* Creates an expression that represents the sign of a numeric value.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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 powering of a numeric [base] to a specified [exponent].
*/
@Internal
data class JpqlPower<T : Number> internal constructor(
val base: Expression<T>,
val exponent: Expression<T>,
) : Expression<Double>
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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.JpqlPower
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSign
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSize
Expand Down Expand Up @@ -428,6 +429,20 @@ class ExpressionsTest : WithAssertions {
assertThat(actual).isEqualTo(expected)
}

@Test
fun power() {
// when
val actual = Expressions.power(doubleExpression1, intExpression1)

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

assertThat(actual).isEqualTo(expected)
}

@Test
fun sign() {
// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathPropertySeria
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathTreatSerializer
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.JpqlPowerSerializer
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
Expand Down Expand Up @@ -367,6 +368,7 @@ private class DefaultModule : JpqlRenderModule {
JpqlPathTreatSerializer(),
JpqlPathTypeSerializer(),
JpqlPlusSerializer(),
JpqlPowerSerializer(),
JpqlPredicateParenthesesSerializer(),
JpqlRoundSerializer(),
JpqlSelectQuerySerializer(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.linecorp.kotlinjdsl.render.jpql.serializer.impl

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

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

writer.write("POWER")

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

writer.write(",")
writer.write(" ")

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

import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPower
import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths
import com.linecorp.kotlinjdsl.render.TestRenderContext
import com.linecorp.kotlinjdsl.render.jpql.entity.employee.Employee
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 JpqlPowerSerializerTest {
private val sut = JpqlPowerSerializer()

@MockK
private lateinit var writer: JpqlWriter

@MockK
private lateinit var serializer: JpqlRenderSerializer

private val expression1 = Paths.path(Employee::age)
private val expression2 = Expressions.value(2)

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

// then
assertThat(actual).isEqualTo(JpqlPower::class)
}

@Test
fun serialize() {
// given
val part = Expressions.power(
base = expression1,
exponent = expression2,
)
val context = TestRenderContext(serializer)

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

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