Skip to content

feat: implement cast, right, left, replace expressions #894

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

Merged
merged 5 commits into from
Jul 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/en/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ Kotlin JDSL provides a series of functions to support built-in functions in JPA.
* UPPER (upper)
* LENGTH (length)
* LOCATE (locate)
* CAST (cast) - *Added in JPA 3.2*
* LEFT (left) - *Added in JPA 3.2*
* RIGHT (right) - *Added in JPA 3.2*
* REPLACE (replace) - *Added in JPA 3.2*

```kotlin
concat(path(Book::title), literal(":"), path(Book::imageUrl))
Expand All @@ -215,6 +219,25 @@ upper(path(Book::title))
length(path(Book::title))

locate("Book", path(Book::title))

cast(path(Book::price)).asString()
cast(path(Book::authorId)).asInt()
cast(path(Book::authorId)).asLong()
cast(path(Book::authorId)).asDouble()
cast(path(Book::authorId)).asFloat()

left(path(Book::title), 3)
left(path(Book::title), literal(3))

right(path(Book::title), 3)
right(path(Book::title), literal(3))

replace(path(Book::title), "old", "new")
replace(path(Book::title), stringLiteral("old"), "new")
replace(path(Book::title), path(Book::name), "new")
replace(path(Book::title), "old", stringLiteral("new"))
replace(path(Book::title), "old", path(Book::name))
replace(path(Book::title), literal("old"), literal("new"))
```

### Arithmetic functions
Expand Down
23 changes: 23 additions & 0 deletions docs/ko/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ Kotlin JDSL은 JPA에서 제공하는 여러 함수들을 지원하기 위한
* UPPER (upper)
* LENGTH (length)
* LOCATE (locate)
* CAST (cast) - *JPA 3.2에 추가됨*
* LEFT (left) - *JPA 3.2에 추가됨*
* RIGHT (right) - *JPA 3.2에 추가됨*
* REPLACE (replace) - *JPA 3.2에 추가됨*

```kotlin
concat(path(Book::title), literal(":"), path(Book::imageUrl))
Expand All @@ -213,6 +217,25 @@ upper(path(Book::title))
length(path(Book::title))

locate("Book", path(Book::title))

cast(path(Book::price)).asString()
cast(path(Book::authorId)).asInt()
cast(path(Book::authorId)).asLong()
cast(path(Book::authorId)).asDouble()
cast(path(Book::authorId)).asFloat()

left(path(Book::title), 3)
left(path(Book::title), literal(3))

right(path(Book::title), 3)
right(path(Book::title), literal(3))

replace(path(Book::title), "old", "new")
replace(path(Book::title), stringLiteral("old"), "new")
replace(path(Book::title), path(Book::name), "new")
replace(path(Book::title), "old", stringLiteral("new"))
replace(path(Book::title), "old", path(Book::name))
replace(path(Book::title), literal("old"), literal("new"))
```

### Arithmetic functions
Expand Down
100 changes: 100 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 @@ -5,9 +5,13 @@ import com.linecorp.kotlinjdsl.dsl.jpql.delete.DeleteQueryWhereStep
import com.linecorp.kotlinjdsl.dsl.jpql.delete.impl.DeleteQueryDsl
import com.linecorp.kotlinjdsl.dsl.jpql.expression.CaseThenFirstStep
import com.linecorp.kotlinjdsl.dsl.jpql.expression.CaseValueWhenFirstStep
import com.linecorp.kotlinjdsl.dsl.jpql.expression.CastStep
import com.linecorp.kotlinjdsl.dsl.jpql.expression.CastStepToString
import com.linecorp.kotlinjdsl.dsl.jpql.expression.TrimFromStep
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.CaseThenFirstStepDsl
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.CaseValueWhenFirstStepDsl
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.JpqlCastStep
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.JpqlCastStepToString
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.TrimBothFromStepDsl
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.TrimFromStepDsl
import com.linecorp.kotlinjdsl.dsl.jpql.expression.impl.TrimLeadingFromStepDsl
Expand Down Expand Up @@ -1737,6 +1741,102 @@ open class Jpql : JpqlDsl {
)
}

/**
* Creates a step to cast a string expression to another type.
*/
@SinceJdsl("3.6.0")
fun cast(value: Expressionable<String>): CastStep {
return JpqlCastStep(value.toExpression())
}

/**
* Creates a step to cast a scalar expression to a string.
*/
@SinceJdsl("3.6.0")
fun <T : Any> cast(value: Expressionable<T>): CastStepToString {
return JpqlCastStepToString(value.toExpression())
}

/**
* Creates an expression that returns the leftmost count characters from a string.
*/
@SinceJdsl("3.6.0")
fun left(value: Expressionable<String>, len: Expressionable<Int>): Expression<String> {
return Expressions.left(value.toExpression(), len.toExpression())
}

/**
* Creates an expression that returns the leftmost count characters from a string.
*/
@SinceJdsl("3.6.0")
fun left(value: Expressionable<String>, len: Int): Expression<String> {
return left(value.toExpression(), intLiteral(len))
}

/**
* Creates an expression that returns the rightmost count characters from a string.
*/
@SinceJdsl("3.6.0")
fun right(value: Expressionable<String>, len: Expressionable<Int>): Expression<String> {
return Expressions.right(value.toExpression(), len.toExpression())
}

/**
* Creates an expression that returns the rightmost count characters from a string.
*/
@SinceJdsl("3.6.0")
fun right(value: Expressionable<String>, len: Int): Expression<String> {
return right(value.toExpression(), intLiteral(len))
}

/**
* Creates an expression that replaces all occurrences of a search string with a replacement string.
*/
@SinceJdsl("3.6.0")
fun replace(
value: Expressionable<String>,
substring: Expressionable<String>,
replacement: Expressionable<String>,
): Expression<String> {
return Expressions.replace(value.toExpression(), substring.toExpression(), replacement.toExpression())
}

/**
* Creates an expression that replaces all occurrences of a search string with a replacement string.
*/
@SinceJdsl("3.6.0")
fun replace(
value: Expressionable<String>,
substring: String,
replacement: String,
): Expression<String> {
return replace(value.toExpression(), stringLiteral(substring), stringLiteral(replacement))
}

/**
* Creates an expression that replaces all occurrences of a search string with a replacement string.
*/
@SinceJdsl("3.6.0")
fun replace(
value: Expressionable<String>,
substring: Expressionable<String>,
replacement: String,
): Expression<String> {
return replace(value.toExpression(), substring, stringLiteral(replacement))
}

/**
* Creates an expression that replaces all occurrences of a search string with a replacement string.
*/
@SinceJdsl("3.6.0")
fun replace(
value: Expressionable<String>,
substring: String,
replacement: Expressionable<String>,
): Expression<String> {
return replace(value.toExpression(), substring, replacement)
}

/**
* Creates an expression that represents predefined database functions and user-defined database functions.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.linecorp.kotlinjdsl.dsl.jpql.expression

import com.linecorp.kotlinjdsl.SinceJdsl
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressionable

/**
* A step to specify the target type for a cast from a string expression.
* This corresponds to the BNF: CAST(string_expression AS {type})
*/
@SinceJdsl("3.6.0")
interface CastStep {
/**
* Casts the expression to an INTEGER.
*/
@SinceJdsl("3.6.0")
fun asInteger(): Expressionable<Int>

/**
* Casts the expression to a LONG.
*/
@SinceJdsl("3.6.0")
fun asLong(): Expressionable<Long>

/**
* Casts the expression to a FLOAT.
*/
@SinceJdsl("3.6.0")
fun asFloat(): Expressionable<Float>

/**
* Casts the expression to a DOUBLE.
*/
@SinceJdsl("3.6.0")
fun asDouble(): Expressionable<Double>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.linecorp.kotlinjdsl.dsl.jpql.expression

import com.linecorp.kotlinjdsl.SinceJdsl
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressionable

/**
* A step to cast a scalar expression to a STRING.
* This corresponds to the BNF: CAST(scalar_expression AS STRING)
*/
@SinceJdsl("3.6.0")
interface CastStepToString {
/**
* Casts the expression to a STRING.
*/
@SinceJdsl("3.6.0")
fun asString(): Expressionable<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.linecorp.kotlinjdsl.dsl.jpql.expression.impl

import com.linecorp.kotlinjdsl.dsl.jpql.expression.CastStep
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressionable
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlCast

@PublishedApi
internal data class JpqlCastStep(
private val expression: Expression<String>,
) : CastStep {
override fun asInteger(): Expressionable<Int> {
return JpqlCast(expression, Int::class)
}

override fun asLong(): Expressionable<Long> {
return JpqlCast(expression, Long::class)
}

override fun asFloat(): Expressionable<Float> {
return JpqlCast(expression, Float::class)
}

override fun asDouble(): Expressionable<Double> {
return JpqlCast(expression, Double::class)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.linecorp.kotlinjdsl.dsl.jpql.expression.impl

import com.linecorp.kotlinjdsl.dsl.jpql.expression.CastStepToString
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressionable
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlCast

@PublishedApi
internal data class JpqlCastStepToString<T : Any>(
private val expression: Expression<T>,
) : CastStepToString {
override fun asString(): Expressionable<String> {
return JpqlCast(expression, String::class)
}
}
Loading
Loading