Skip to content

Commit

Permalink
Merge pull request #61 from ergon/bugfix/dope-260-fix-order-by
Browse files Browse the repository at this point in the history
DOPE-260: added OrderExpression and implemented fix for order by
  • Loading branch information
jansigi authored Nov 19, 2024
2 parents 5380a0b + 23c5bb4 commit c710fcd
Show file tree
Hide file tree
Showing 8 changed files with 567 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import ch.ergon.dope.resolvable.clause.model.GroupByClause
import ch.ergon.dope.resolvable.clause.model.InnerJoinClause
import ch.ergon.dope.resolvable.clause.model.LeftJoinClause
import ch.ergon.dope.resolvable.clause.model.OrderByType
import ch.ergon.dope.resolvable.clause.model.OrderExpression
import ch.ergon.dope.resolvable.clause.model.RightJoinClause
import ch.ergon.dope.resolvable.clause.model.SelectLimitClause
import ch.ergon.dope.resolvable.clause.model.SelectOffsetClause
import ch.ergon.dope.resolvable.clause.model.SelectOrderByClause
import ch.ergon.dope.resolvable.clause.model.SelectOrderByTypeClause
import ch.ergon.dope.resolvable.clause.model.SelectWhereClause
import ch.ergon.dope.resolvable.clause.model.StandardJoinClause
import ch.ergon.dope.resolvable.clause.model.UnnestClause
Expand All @@ -27,7 +27,6 @@ import ch.ergon.dope.resolvable.fromable.Joinable
import ch.ergon.dope.validtype.ArrayType
import ch.ergon.dope.validtype.BooleanType
import ch.ergon.dope.validtype.NumberType
import ch.ergon.dope.validtype.StringType
import ch.ergon.dope.validtype.ValidType

interface ISelectOffsetClause<T : ValidType> : Clause {
Expand All @@ -46,9 +45,10 @@ interface ISelectOrderByClause<T : ValidType> : ISelectLimitClause<T> {
}

interface ISelectGroupByClause<T : ValidType> : ISelectOrderByClause<T> {
fun orderBy(stringField: Field<StringType>) = SelectOrderByClause(stringField, this)
fun orderBy(stringField: Field<StringType>, orderByType: OrderByType) =
SelectOrderByTypeClause(stringField, orderByType, this)
fun orderBy(expression: TypeExpression<out ValidType>, orderByType: OrderByType? = null) = SelectOrderByClause(
OrderExpression(expression, orderByType),
parentClause = this,
)
}

interface ISelectWhereClause<T : ValidType> : ISelectGroupByClause<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,58 @@ package ch.ergon.dope.resolvable.clause.model

import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.resolvable.Resolvable
import ch.ergon.dope.resolvable.clause.ISelectGroupByClause
import ch.ergon.dope.resolvable.clause.ISelectOrderByClause
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.formatToQueryStringWithSymbol
import ch.ergon.dope.validtype.StringType
import ch.ergon.dope.validtype.ValidType

enum class OrderByType(val type: String) {
enum class OrderByType(val queryString: String) {
ASC("ASC"),
DESC("DESC"),
}

private const val ORDER_BY = "ORDER BY"

open class SelectOrderByClause<T : ValidType>(private val stringField: Field<StringType>, private val parentClause: ISelectGroupByClause<T>) :
ISelectOrderByClause<T> {

class SelectOrderByClause<T : ValidType>(
private val orderExpression: OrderExpression,
private vararg val additionalOrderExpressions: OrderExpression,
private val parentClause: ISelectGroupByClause<T>,
) : ISelectOrderByClause<T> {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val parentDopeQuery = parentClause.toDopeQuery(manager)
val stringDopeQuery = stringField.toDopeQuery(manager)
val orderExpressionDopeQuery = orderExpression.toDopeQuery(manager)
val additionalOrderExpressions = additionalOrderExpressions.map { it.toDopeQuery(manager) }
return DopeQuery(
queryString = formatToQueryStringWithSymbol(parentDopeQuery.queryString, ORDER_BY, stringDopeQuery.queryString),
parameters = parentDopeQuery.parameters.merge(stringDopeQuery.parameters),
queryString = formatToQueryStringWithSymbol(
parentDopeQuery.queryString,
ORDER_BY,
orderExpressionDopeQuery.queryString,
*additionalOrderExpressions.map { it.queryString }.toTypedArray(),
),
parameters = parentDopeQuery.parameters.merge(
orderExpressionDopeQuery.parameters,
*additionalOrderExpressions.map { it.parameters }.toTypedArray(),
),
)
}
}

class SelectOrderByTypeClause<T : ValidType>(
private val stringField: Field<StringType>,
private val orderByType: OrderByType,
private val parentClause: ISelectGroupByClause<T>,
) : SelectOrderByClause<T>(stringField, parentClause) {
fun thenOrderBy(typeExpression: TypeExpression<out ValidType>, orderByType: OrderByType? = null) =
SelectOrderByClause(
this.orderExpression,
*additionalOrderExpressions,
OrderExpression(typeExpression, orderByType),
parentClause = this.parentClause,
)
}

class OrderExpression(private val expression: TypeExpression<out ValidType>, private val orderByType: OrderByType? = null) : Resolvable {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val parentDopeQuery = parentClause.toDopeQuery(manager)
val stringDopeQuery = stringField.toDopeQuery(manager)
val typeDopeQuery = expression.toDopeQuery(manager)
return DopeQuery(
queryString = formatToQueryStringWithSymbol(
parentDopeQuery.queryString,
ORDER_BY,
stringDopeQuery.queryString + " $orderByType",
),
parameters = parentDopeQuery.parameters.merge(stringDopeQuery.parameters),
queryString = listOfNotNull(typeDopeQuery.queryString, orderByType?.queryString).joinToString(separator = " "),
parameters = typeDopeQuery.parameters,
)
}
}
23 changes: 23 additions & 0 deletions core/src/test/kotlin/ch/ergon/dope/buildTest/OrderByTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package ch.ergon.dope.buildTest

import ch.ergon.dope.QueryBuilder
import ch.ergon.dope.helper.someBucket
import ch.ergon.dope.helper.someNumberField
import ch.ergon.dope.helper.someStringField
import ch.ergon.dope.resolvable.clause.model.OrderByType
import ch.ergon.dope.resolvable.expression.unaliased.type.function.stringfunction.lower
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -62,4 +64,25 @@ class OrderByTest {

assertEquals(expected, actual)
}

@Test
fun `should support multiple Order By clause`() {
val expected = "SELECT * FROM `someBucket` ORDER BY `stringField` DESC, `numberField`, LOWER(\"SOMETHING\") ASC"

val actual: String = create
.selectAsterisk()
.from(
someBucket(),
).orderBy(
someStringField(),
OrderByType.DESC,
).thenOrderBy(
someNumberField(),
).thenOrderBy(
lower("SOMETHING"),
OrderByType.ASC,
).build().queryString

assertEquals(expected, actual)
}
}
8 changes: 8 additions & 0 deletions core/src/test/kotlin/ch/ergon/dope/helper/Builder.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package ch.ergon.dope.helper

import ch.ergon.dope.resolvable.clause.model.OrderByType
import ch.ergon.dope.resolvable.clause.model.OrderByType.ASC
import ch.ergon.dope.resolvable.clause.model.OrderExpression
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.UnaliasedExpression
import ch.ergon.dope.resolvable.expression.unaliased.aggregator.CountAsteriskExpression
Expand Down Expand Up @@ -64,3 +67,8 @@ fun someStringSearchNumberResult(
searchExpression: UnaliasedExpression<StringType> = someString().toDopeType(),
resultExpression: UnaliasedExpression<NumberType> = someNumber().toDopeType(),
) = SearchResult(searchExpression, resultExpression)

fun someOrderExpression(typeExpression: TypeExpression<StringType> = someStringField(), orderByType: OrderByType = ASC) = OrderExpression(
typeExpression,
orderByType,
)
Loading

0 comments on commit c710fcd

Please sign in to comment.