Extend query builder allowing more conditions

Before only a column or a dbfunction could be used in a condition. It
is now allowed for all `SelectExpr`.
This commit is contained in:
Eike Kettner 2021-03-08 20:46:42 +01:00
parent b514b85f39
commit e681ffa96f
4 changed files with 62 additions and 30 deletions

View File

@ -15,7 +15,7 @@ object Condition {
val P: Put[A]
) extends Condition
case class CompareFVal[A](dbf: DBFunction, op: Operator, value: A)(implicit
case class CompareFVal[A](sel: SelectExpr, op: Operator, value: A)(implicit
val P: Put[A]
) extends Condition
@ -23,11 +23,11 @@ object Condition {
extends Condition
case class InSubSelect[A](col: Column[A], subSelect: Select) extends Condition
case class InValues[A](col: Column[A], values: NonEmptyList[A], lower: Boolean)(implicit
val P: Put[A]
case class InValues[A](sel: SelectExpr, values: NonEmptyList[A], lower: Boolean)(
implicit val P: Put[A]
) extends Condition
case class IsNull(col: Column[_]) extends Condition
case class IsNull(sel: SelectExpr) extends Condition
case class And(inner: NonEmptyList[Condition]) extends Condition {
def append(other: Condition): And =

View File

@ -207,22 +207,22 @@ trait DSL extends DoobieMeta {
in(subsel).negate
def in(values: Nel[A])(implicit P: Put[A]): Condition =
Condition.InValues(col, values, false)
Condition.InValues(col.s, values, false)
def notIn(values: Nel[A])(implicit P: Put[A]): Condition =
in(values).negate
def inLower(values: Nel[A])(implicit P: Put[A]): Condition =
Condition.InValues(col, values, true)
Condition.InValues(col.s, values, true)
def notInLower(values: Nel[A])(implicit P: Put[A]): Condition =
Condition.InValues(col, values, true).negate
Condition.InValues(col.s, values, true).negate
def isNull: Condition =
Condition.IsNull(col)
Condition.IsNull(col.s)
def isNotNull: Condition =
Condition.IsNull(col).negate
Condition.IsNull(col.s).negate
def ===(other: Column[A]): Condition =
Condition.CompareCol(col, Operator.Eq, other)
@ -267,31 +267,31 @@ trait DSL extends DoobieMeta {
SelectExpr.SelectFun(dbf, Some(otherCol.name))
def ===[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.Eq, value)
Condition.CompareFVal(dbf.s, Operator.Eq, value)
def ====(value: String): Condition =
Condition.CompareFVal(dbf, Operator.Eq, value)
Condition.CompareFVal(dbf.s, Operator.Eq, value)
def like[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.LowerLike, value)
Condition.CompareFVal(dbf.s, Operator.LowerLike, value)
def likes(value: String): Condition =
Condition.CompareFVal(dbf, Operator.LowerLike, value)
Condition.CompareFVal(dbf.s, Operator.LowerLike, value)
def <=[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.Lte, value)
Condition.CompareFVal(dbf.s, Operator.Lte, value)
def >=[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.Gte, value)
Condition.CompareFVal(dbf.s, Operator.Gte, value)
def >[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.Gt, value)
Condition.CompareFVal(dbf.s, Operator.Gt, value)
def <[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.Lt, value)
Condition.CompareFVal(dbf.s, Operator.Lt, value)
def <>[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(dbf, Operator.Neq, value)
Condition.CompareFVal(dbf.s, Operator.Neq, value)
def -[A](value: A)(implicit P: Put[A]): DBFunction =
DBFunction.Calc(
@ -300,6 +300,35 @@ trait DSL extends DoobieMeta {
SelectExpr.SelectConstant(value, None)
)
}
implicit final class SelectExprOps(sel: SelectExpr) {
def isNull: Condition =
Condition.IsNull(sel)
def isNotNull: Condition =
Condition.IsNull(sel).negate
def ===[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(sel, Operator.Eq, value)
def <=[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(sel, Operator.Lte, value)
def >=[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(sel, Operator.Gte, value)
def >[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(sel, Operator.Gt, value)
def <[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(sel, Operator.Lt, value)
def <>[A](value: A)(implicit P: Put[A]): Condition =
Condition.CompareFVal(sel, Operator.Neq, value)
def in[A](values: Nel[A])(implicit P: Put[A]): Condition =
Condition.InValues(sel, values, false)
}
}
object DSL extends DSL {

View File

@ -92,7 +92,7 @@ object ItemQueryGenerator {
val dt = dateToTimestamp(today)(value)
val col = timestampColumn(tables)(attr)
val noLikeOp = if (op == Operator.Like) Operator.Eq else op
Condition.CompareVal(col, makeOp(noLikeOp), dt)
Condition.CompareFVal(col, makeOp(noLikeOp), dt)
case Expr.SimpleExpr(op, Property.IntProperty(attr, value)) =>
val col = intColumn(tables)(attr)
@ -203,22 +203,22 @@ object ItemQueryGenerator {
today
}
private def anyColumn(tables: Tables)(attr: Attr): Column[_] =
private def anyColumn(tables: Tables)(attr: Attr): SelectExpr =
attr match {
case s: Attr.StringAttr =>
stringColumn(tables)(s)
stringColumn(tables)(s).s
case t: Attr.DateAttr =>
timestampColumn(tables)(t)
case n: Attr.IntAttr =>
intColumn(tables)(n)
intColumn(tables)(n).s
}
private def timestampColumn(tables: Tables)(attr: Attr.DateAttr) =
private def timestampColumn(tables: Tables)(attr: Attr.DateAttr): SelectExpr =
attr match {
case Attr.Date =>
tables.item.itemDate
coalesce(tables.item.itemDate.s, tables.item.created.s).s
case Attr.DueDate =>
tables.item.dueDate
tables.item.dueDate.s
}
private def stringColumn(tables: Tables)(attr: Attr.StringAttr): Column[String] =
@ -283,7 +283,7 @@ object ItemQueryGenerator {
value.toDoubleOption
.map { n =>
val numericCmp = Condition.CompareFVal(castNumeric(cfv.value.s), op, n)
val numericCmp = Condition.CompareFVal(castNumeric(cfv.value.s).s, op, n)
val fieldIsNumeric =
cf.ftype === CustomFieldType.Numeric || cf.ftype === CustomFieldType.Money
val fieldNotNumeric =

View File

@ -85,7 +85,7 @@ object ConditionBuilder {
case Operator.LowerEq =>
lower(dbf)
case _ =>
DBFunctionBuilder.build(dbf)
SelectExprBuilder.build(dbf)
}
dbfFrag ++ opFrag ++ valFrag
@ -105,13 +105,13 @@ object ConditionBuilder {
SelectExprBuilder.column(col) ++ sql" IN (" ++ sub ++ parenClose
case c @ Condition.InValues(col, values, toLower) =>
val cfrag = if (toLower) lower(col) else SelectExprBuilder.column(col)
val cfrag = if (toLower) lower(col) else SelectExprBuilder.build(col)
cfrag ++ sql" IN (" ++ values.toList
.map(a => buildValue(a)(c.P))
.reduce(_ ++ comma ++ _) ++ parenClose
case Condition.IsNull(col) =>
SelectExprBuilder.column(col) ++ fr" is null"
SelectExprBuilder.build(col) ++ fr" is null"
case Condition.And(ands) =>
val inner = ands.map(build).reduceLeft(_ ++ and ++ _)
@ -124,7 +124,7 @@ object ConditionBuilder {
else parenOpen ++ inner ++ parenClose
case Condition.Not(Condition.IsNull(col)) =>
SelectExprBuilder.column(col) ++ fr" is not null"
SelectExprBuilder.build(col) ++ fr" is not null"
case Condition.Not(c) =>
fr"NOT" ++ build(c)
@ -159,6 +159,9 @@ object ConditionBuilder {
def buildOptValue[A: Put](v: Option[A]): Fragment =
fr"$v"
def lower(sel: SelectExpr): Fragment =
Fragment.const0("LOWER(") ++ SelectExprBuilder.build(sel) ++ parenClose
def lower(col: Column[_]): Fragment =
Fragment.const0("LOWER(") ++ SelectExprBuilder.column(col) ++ parenClose