From 5e2c5d2a50c2d10a4811a0ea06e6ddc941583df4 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sun, 13 Dec 2020 23:44:46 +0100 Subject: [PATCH] Extends query builder --- .../docspell/joex/analysis/RegexNerFile.scala | 2 +- .../main/scala/docspell/store/qb/DSL.scala | 4 +- .../scala/docspell/store/qb/FromExpr.scala | 47 +++++++++++++++---- .../main/scala/docspell/store/qb/Join.scala | 10 ---- .../main/scala/docspell/store/qb/Select.scala | 5 ++ .../store/qb/impl/FromExprBuilder.scala | 30 +++++++----- .../store/qb/impl/SelectBuilder.scala | 3 ++ .../scala/docspell/store/queries/QJob.scala | 2 +- 8 files changed, 69 insertions(+), 34 deletions(-) delete mode 100644 modules/store/src/main/scala/docspell/store/qb/Join.scala diff --git a/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala b/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala index 75c21673..24e7f6ae 100644 --- a/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala +++ b/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala @@ -150,7 +150,7 @@ object RegexNerFile { ) val t = Column[Timestamp]("t", TableDef("")) - run(select(max(t)), fromSubSelect(sql).as("x")) + run(select(max(t)), from(sql, "x")) .query[Option[Timestamp]] .option .map(_.flatten) 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 4a1aa116..45905d84 100644 --- a/modules/store/src/main/scala/docspell/store/qb/DSL.scala +++ b/modules/store/src/main/scala/docspell/store/qb/DSL.scala @@ -59,8 +59,8 @@ trait DSL extends DoobieMeta { def from(table: TableDef): FromExpr.From = FromExpr.From(table) - def fromSubSelect(sel: Select): FromExpr.SubSelect = - FromExpr.SubSelect(sel, "x") + def from(sel: Select, alias: String): FromExpr.From = + FromExpr.From(sel, alias) def count(c: Column[_]): DBFunction = DBFunction.Count(c) diff --git a/modules/store/src/main/scala/docspell/store/qb/FromExpr.scala b/modules/store/src/main/scala/docspell/store/qb/FromExpr.scala index 8a8d65b1..ac32d791 100644 --- a/modules/store/src/main/scala/docspell/store/qb/FromExpr.scala +++ b/modules/store/src/main/scala/docspell/store/qb/FromExpr.scala @@ -4,24 +4,55 @@ sealed trait FromExpr object FromExpr { - case class From(table: TableDef) extends FromExpr { - def innerJoin(other: TableDef, on: Condition): Joined = + case class From(table: Relation) extends FromExpr { + def innerJoin(other: Relation, on: Condition): Joined = Joined(this, Vector(Join.InnerJoin(other, on))) - def leftJoin(other: TableDef, on: Condition): Joined = + def innerJoin(other: TableDef, on: Condition): Joined = + innerJoin(Relation.Table(other), on) + + def leftJoin(other: Relation, on: Condition): Joined = Joined(this, Vector(Join.LeftJoin(other, on))) + + def leftJoin(other: TableDef, on: Condition): Joined = + leftJoin(Relation.Table(other), on) + } + + object From { + def apply(td: TableDef): From = + From(Relation.Table(td)) + + def apply(select: Select, alias: String): From = + From(Relation.SubSelect(select, alias)) } case class Joined(from: From, joins: Vector[Join]) extends FromExpr { - def innerJoin(other: TableDef, on: Condition): Joined = + def innerJoin(other: Relation, on: Condition): Joined = Joined(from, joins :+ Join.InnerJoin(other, on)) - def leftJoin(other: TableDef, on: Condition): Joined = + def innerJoin(other: TableDef, on: Condition): Joined = + innerJoin(Relation.Table(other), on) + + def leftJoin(other: Relation, on: Condition): Joined = Joined(from, joins :+ Join.LeftJoin(other, on)) + + def leftJoin(other: TableDef, on: Condition): Joined = + leftJoin(Relation.Table(other), on) } - case class SubSelect(sel: Select, name: String) extends FromExpr { - def as(name: String): SubSelect = - copy(name = name) + sealed trait Relation + object Relation { + final case class Table(table: TableDef) extends Relation + final case class SubSelect(select: Select, alias: String) extends Relation { + def as(a: String): SubSelect = + copy(alias = a) + } } + + sealed trait Join + object Join { + final case class InnerJoin(table: Relation, cond: Condition) extends Join + final case class LeftJoin(table: Relation, cond: Condition) extends Join + } + } diff --git a/modules/store/src/main/scala/docspell/store/qb/Join.scala b/modules/store/src/main/scala/docspell/store/qb/Join.scala deleted file mode 100644 index a51a3b70..00000000 --- a/modules/store/src/main/scala/docspell/store/qb/Join.scala +++ /dev/null @@ -1,10 +0,0 @@ -package docspell.store.qb - -sealed trait Join - -object Join { - - case class InnerJoin(table: TableDef, cond: Condition) extends Join - - case class LeftJoin(table: TableDef, cond: Condition) extends Join -} diff --git a/modules/store/src/main/scala/docspell/store/qb/Select.scala b/modules/store/src/main/scala/docspell/store/qb/Select.scala index 74bd8b6c..f1135f97 100644 --- a/modules/store/src/main/scala/docspell/store/qb/Select.scala +++ b/modules/store/src/main/scala/docspell/store/qb/Select.scala @@ -86,6 +86,11 @@ object Select { copy(projection = projection.append(e)) } + case class RawSelect(fragment: Fragment) extends Select { + def appendSelect(e: SelectExpr): RawSelect = + sys.error("RawSelect doesn't support appending select expressions") + } + case class Union(q: Select, qs: Vector[Select]) extends Select { def appendSelect(e: SelectExpr): Union = copy(q = q.appendSelect(e)) diff --git a/modules/store/src/main/scala/docspell/store/qb/impl/FromExprBuilder.scala b/modules/store/src/main/scala/docspell/store/qb/impl/FromExprBuilder.scala index 926ed330..522a8941 100644 --- a/modules/store/src/main/scala/docspell/store/qb/impl/FromExprBuilder.scala +++ b/modules/store/src/main/scala/docspell/store/qb/impl/FromExprBuilder.scala @@ -9,15 +9,12 @@ object FromExprBuilder { def build(expr: FromExpr): Fragment = expr match { - case FromExpr.From(table) => - fr" FROM" ++ buildTable(table) + case FromExpr.From(relation) => + fr" FROM" ++ buildRelation(relation) case FromExpr.Joined(from, joins) => build(from) ++ joins.map(buildJoin).foldLeft(Fragment.empty)(_ ++ _) - - case FromExpr.SubSelect(sel, name) => - sql" FROM (" ++ SelectBuilder(sel) ++ fr") AS" ++ Fragment.const(name) } def buildTable(table: TableDef): Fragment = @@ -25,15 +22,24 @@ object FromExprBuilder { .map(a => Fragment.const0(a)) .getOrElse(Fragment.empty) - def buildJoin(join: Join): Fragment = - join match { - case Join.InnerJoin(table, cond) => - val c = fr" ON" ++ ConditionBuilder.build(cond) - fr" INNER JOIN" ++ buildTable(table) ++ c + def buildRelation(rel: FromExpr.Relation): Fragment = + rel match { + case FromExpr.Relation.Table(table) => + buildTable(table) - case Join.LeftJoin(table, cond) => + case FromExpr.Relation.SubSelect(sel, alias) => + sql" (" ++ SelectBuilder(sel) ++ fr") AS" ++ Fragment.const(alias) + } + + def buildJoin(join: FromExpr.Join): Fragment = + join match { + case FromExpr.Join.InnerJoin(table, cond) => val c = fr" ON" ++ ConditionBuilder.build(cond) - fr" LEFT JOIN" ++ buildTable(table) ++ c + fr" INNER JOIN" ++ buildRelation(table) ++ c + + case FromExpr.Join.LeftJoin(table, cond) => + val c = fr" ON" ++ ConditionBuilder.build(cond) + fr" LEFT JOIN" ++ buildRelation(table) ++ c } } diff --git a/modules/store/src/main/scala/docspell/store/qb/impl/SelectBuilder.scala b/modules/store/src/main/scala/docspell/store/qb/impl/SelectBuilder.scala index 40234b96..ee50ba67 100644 --- a/modules/store/src/main/scala/docspell/store/qb/impl/SelectBuilder.scala +++ b/modules/store/src/main/scala/docspell/store/qb/impl/SelectBuilder.scala @@ -21,6 +21,9 @@ object SelectBuilder { val sel = if (sq.distinctFlag) fr"SELECT DISTINCT" else fr"SELECT" sel ++ buildSimple(sq) + case Select.RawSelect(f) => + f + case Select.Union(q, qs) => qs.prepended(q).map(build).reduce(_ ++ union ++ _) diff --git a/modules/store/src/main/scala/docspell/store/queries/QJob.scala b/modules/store/src/main/scala/docspell/store/queries/QJob.scala index c85080a1..1d7b4c3e 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QJob.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QJob.scala @@ -110,7 +110,7 @@ object QJob { val gcol = Column[String]("g", TableDef("")) val groups = - Select(select(gcol), fromSubSelect(union(sql1, sql2)).as("t0"), gcol.isNull.negate) + Select(select(gcol), from(union(sql1, sql2), "t0"), gcol.isNull.negate) // either 0, one or two results, but may be empty if RJob table is empty groups.build.query[Ident].to[List].map(_.headOption)