diff --git a/modules/store/src/main/scala/docspell/store/qb/Condition.scala b/modules/store/src/main/scala/docspell/store/qb/Condition.scala index 0b0c0692..9a329033 100644 --- a/modules/store/src/main/scala/docspell/store/qb/Condition.scala +++ b/modules/store/src/main/scala/docspell/store/qb/Condition.scala @@ -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 = diff --git a/modules/store/src/main/scala/docspell/store/qb/DSL.scala b/modules/store/src/main/scala/docspell/store/qb/DSL.scala index 63a0afc4..d2e78be1 100644 --- a/modules/store/src/main/scala/docspell/store/qb/DSL.scala +++ b/modules/store/src/main/scala/docspell/store/qb/DSL.scala @@ -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 { diff --git a/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala b/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala index a99516ff..6a721270 100644 --- a/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala +++ b/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala @@ -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 = diff --git a/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala b/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala index f110bb92..c9e50575 100644 --- a/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala +++ b/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala @@ -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