Introduce unit condition

This commit is contained in:
Eike Kettner
2020-12-14 23:07:06 +01:00
parent 80406cabc2
commit 2dff686fa0
14 changed files with 225 additions and 39 deletions

View File

@ -7,6 +7,9 @@ import doobie._
sealed trait Condition
object Condition {
case object UnitCondition extends Condition
val unit: Condition = UnitCondition
case class CompareVal[A](column: Column[A], op: Operator, value: A)(implicit
val P: Put[A]
@ -26,36 +29,58 @@ object Condition {
case class IsNull(col: Column[_]) extends Condition
case class And(c: Condition, cs: Vector[Condition]) extends Condition {
case class And(inner: NonEmptyList[Condition]) extends Condition {
def append(other: Condition): And =
other match {
case And(oc, ocs) =>
And(c, cs ++ (oc +: ocs))
case And(otherInner) =>
And(inner.concatNel(otherInner))
case _ =>
And(c, cs :+ other)
And(inner.append(other))
}
}
object And {
def apply(c: Condition, cs: Condition*): And =
And(c, cs.toVector)
And(NonEmptyList(c, cs.toList))
object Inner extends InnerCondition {
def unapply(node: Condition): Option[NonEmptyList[Condition]] =
node match {
case n: And =>
Option(n.inner)
case _ =>
None
}
}
}
case class Or(c: Condition, cs: Vector[Condition]) extends Condition {
case class Or(inner: NonEmptyList[Condition]) extends Condition {
def append(other: Condition): Or =
other match {
case Or(oc, ocs) =>
Or(c, cs ++ (oc +: ocs))
case Or(otherInner) =>
Or(inner.concatNel(otherInner))
case _ =>
Or(c, cs :+ other)
Or(inner.append(other))
}
}
object Or {
def apply(c: Condition, cs: Condition*): Or =
Or(c, cs.toVector)
Or(NonEmptyList(c, cs.toList))
object Inner extends InnerCondition {
def unapply(node: Condition): Option[NonEmptyList[Condition]] =
node match {
case n: Or =>
Option(n.inner)
case _ =>
None
}
}
}
case class Not(c: Condition) extends Condition
object Not {}
trait InnerCondition {
def unapply(node: Condition): Option[NonEmptyList[Condition]]
}
}

View File

@ -97,15 +97,15 @@ trait DSL extends DoobieMeta {
case a: Condition.And =>
cs.foldLeft(a)(_.append(_))
case _ =>
Condition.And(c, cs.toVector)
Condition.And(c, cs: _*)
}
def or(c: Condition, cs: Condition*): Condition =
c match {
case Condition.Or(head, tail) =>
Condition.Or(head, tail ++ (c +: cs.toVector))
case o: Condition.Or =>
cs.foldLeft(o)(_.append(_))
case _ =>
Condition.Or(c, cs.toVector)
Condition.Or(c, cs: _*)
}
def not(c: Condition): Condition =

View File

@ -46,35 +46,35 @@ sealed trait Select {
object Select {
def apply(projection: Nel[SelectExpr], from: FromExpr) =
SimpleSelect(false, projection, from, None, None)
SimpleSelect(false, projection, from, Condition.unit, None)
def apply(projection: SelectExpr, from: FromExpr) =
SimpleSelect(false, Nel.of(projection), from, None, None)
SimpleSelect(false, Nel.of(projection), from, Condition.unit, None)
def apply(
projection: Nel[SelectExpr],
from: FromExpr,
where: Condition
) = SimpleSelect(false, projection, from, Some(where), None)
) = SimpleSelect(false, projection, from, where, None)
def apply(
projection: SelectExpr,
from: FromExpr,
where: Condition
) = SimpleSelect(false, Nel.of(projection), from, Some(where), None)
) = SimpleSelect(false, Nel.of(projection), from, where, None)
def apply(
projection: Nel[SelectExpr],
from: FromExpr,
where: Condition,
groupBy: GroupBy
) = SimpleSelect(false, projection, from, Some(where), Some(groupBy))
) = SimpleSelect(false, projection, from, where, Some(groupBy))
case class SimpleSelect(
distinctFlag: Boolean,
projection: Nel[SelectExpr],
from: FromExpr,
where: Option[Condition],
where: Condition,
groupBy: Option[GroupBy]
) extends Select {
def group(gb: GroupBy): SimpleSelect =
@ -84,9 +84,10 @@ object Select {
copy(distinctFlag = true)
def where(c: Option[Condition]): SimpleSelect =
copy(where = c)
where(c.getOrElse(Condition.unit))
def where(c: Condition): SimpleSelect =
copy(where = Some(c))
copy(where = c)
def appendSelect(e: SelectExpr): SimpleSelect =
copy(projection = projection.append(e))
@ -95,7 +96,7 @@ object Select {
copy(from = f(from))
def changeWhere(f: Condition => Condition): SimpleSelect =
copy(where = where.map(f))
copy(where = f(where))
def orderBy(ob: OrderBy, obs: OrderBy*): Select =
Ordered(this, ob, obs.toVector)

View File

@ -1,5 +1,7 @@
package docspell.store.qb.impl
import cats.data.NonEmptyList
import docspell.store.qb._
import _root_.doobie.implicits._
@ -12,8 +14,55 @@ object ConditionBuilder {
val parenOpen = Fragment.const0("(")
val parenClose = Fragment.const0(")")
def build(expr: Condition): Fragment =
expr match {
final def reduce(c: Condition): Condition =
c match {
case Condition.And(inner) =>
NonEmptyList.fromList(flatten(inner.toList, Condition.And.Inner)) match {
case Some(rinner) =>
if (rinner.tail.isEmpty) reduce(rinner.head)
else Condition.And(rinner.reverse.map(reduce))
case None =>
Condition.unit
}
case Condition.Or(inner) =>
NonEmptyList.fromList(flatten(inner.toList, Condition.Or.Inner)) match {
case Some(rinner) =>
if (rinner.tail.isEmpty) reduce(rinner.head)
else Condition.Or(rinner.reverse.map(reduce))
case None =>
Condition.unit
}
case Condition.Not(Condition.UnitCondition) =>
Condition.unit
case Condition.Not(Condition.Not(inner)) =>
reduce(inner)
case _ =>
c
}
private def flatten(
els: List[Condition],
nodePattern: Condition.InnerCondition,
result: List[Condition] = Nil
): List[Condition] =
els match {
case Nil =>
result
case nodePattern(more) :: tail =>
val spliced = flatten(more.toList, nodePattern, result)
flatten(tail, nodePattern, spliced)
case Condition.UnitCondition :: tail =>
flatten(tail, nodePattern, result)
case h :: tail =>
flatten(tail, nodePattern, h :: result)
}
final def build(expr: Condition): Fragment =
reduce(expr) match {
case c @ Condition.CompareVal(col, op, value) =>
val opFrag = operator(op)
val valFrag = buildValue(value)(c.P)
@ -58,14 +107,14 @@ object ConditionBuilder {
case Condition.IsNull(col) =>
SelectExprBuilder.column(col) ++ fr" is null"
case Condition.And(c, cs) =>
val inner = cs.prepended(c).map(build).reduce(_ ++ and ++ _)
if (cs.isEmpty) inner
case Condition.And(ands) =>
val inner = ands.map(build).reduceLeft(_ ++ and ++ _)
if (ands.tail.isEmpty) inner
else parenOpen ++ inner ++ parenClose
case Condition.Or(c, cs) =>
val inner = cs.prepended(c).map(build).reduce(_ ++ or ++ _)
if (cs.isEmpty) inner
case Condition.Or(ors) =>
val inner = ors.map(build).reduceLeft(_ ++ or ++ _)
if (ors.tail.isEmpty) inner
else parenOpen ++ inner ++ parenClose
case Condition.Not(Condition.IsNull(col)) =>
@ -73,6 +122,9 @@ object ConditionBuilder {
case Condition.Not(c) =>
fr"NOT" ++ build(c)
case Condition.UnitCondition =>
Fragment.empty
}
def operator(op: Operator): Fragment =

View File

@ -47,7 +47,7 @@ object SelectBuilder {
def buildSimple(sq: Select.SimpleSelect): Fragment = {
val f0 = sq.projection.map(selectExpr).reduceLeft(_ ++ comma ++ _)
val f1 = fromExpr(sq.from)
val f2 = sq.where.map(cond).getOrElse(Fragment.empty)
val f2 = cond(sq.where)
val f3 = sq.groupBy.map(groupBy).getOrElse(Fragment.empty)
f0 ++ f1 ++ f2 ++ f3
}
@ -70,7 +70,12 @@ object SelectBuilder {
FromExprBuilder.build(fr)
def cond(c: Condition): Fragment =
fr" WHERE" ++ ConditionBuilder.build(c)
c match {
case Condition.UnitCondition =>
Fragment.empty
case _ =>
fr" WHERE" ++ ConditionBuilder.build(c)
}
def groupBy(gb: GroupBy): Fragment = {
val f0 = gb.names.prepended(gb.name).map(selectExpr).reduce(_ ++ comma ++ _)

View File

@ -1,9 +1,10 @@
package docspell.store.queries
import bitpeace.FileMeta
import docspell.common._
import docspell.store.records._
import bitpeace.FileMeta
case class ItemData(
item: RItem,
corrOrg: Option[ROrganization],

View File

@ -13,8 +13,8 @@ import docspell.store.qb.DSL._
import docspell.store.qb._
import docspell.store.records._
import doobie.{Query => _, _}
import doobie.implicits._
import doobie.{Query => _, _}
import org.log4s._
object QItem {

View File

@ -1,14 +1,14 @@
package docspell.store.queries
import cats.effect._
import cats.data.OptionT
import cats.effect._
import cats.implicits._
import docspell.common._
import docspell.store.records._
import doobie.{Query => _, _}
import doobie.implicits._
import doobie.{Query => _, _}
object QMoveAttachment {
def moveAttachmentBefore(