mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-10-31 09:30:12 +00:00 
			
		
		
		
	Extends query builder
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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)) | ||||
|   | ||||
| @@ -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 | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 ++ _) | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user