Last active
May 8, 2021 14:01
-
-
Save hackeris/cebdb09dc02fffc0a40f6bfea4930ca3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package rainm.play | |
import scala.language.implicitConversions | |
object StrongQueryPlay { | |
trait Table { | |
def name: String | |
} | |
case class Col[V, +T <: Table](name: String)(implicit table: T) { | |
def ==(value: V): BinPred[T] = BinPred(this, BinOp.Equal, value.asInstanceOf[AnyVal]) | |
def !=(value: V): BinPred[T] = BinPred(this, BinOp.NotEqual, value.asInstanceOf[AnyVal]) | |
} | |
object BinOp extends Enumeration { | |
val Equal, NotEqual, GreaterThan, LessThan, GreaterOrEqual, LessOrEqual, In, NotIn, Between = Value | |
} | |
class Pred[+T <: Table] { | |
def and[U >: T <: Table](another: Pred[U]): AndPred[U] = (this, another) match { | |
case (AndPred(children1), AndPred(children2)) => | |
AndPred(children1 ++ children2) | |
case (AndPred(children), _) => | |
AndPred(children :+ another) | |
case (_, AndPred(children)) => | |
AndPred(children :+ another) | |
case (_, _) => | |
AndPred(Seq(this, another)) | |
} | |
def or[U >: T <: Table](another: Pred[U]): OrPred[U] = (this, another) match { | |
case (OrPred(children1), OrPred(children2)) => | |
OrPred(children1 ++ children2) | |
case (OrPred(children), _) => | |
OrPred(children :+ another) | |
case (_, OrPred(children)) => | |
OrPred(children :+ another) | |
case (_, _) => | |
OrPred(Seq(this, another)) | |
} | |
} | |
case class AndPred[+T <: Table](children: Seq[Pred[T]]) extends Pred[T] | |
case class OrPred[+T <: Table](children: Seq[Pred[T]]) extends Pred[T] | |
case class BinPred[+T <: Table](col: Col[_, T], op: BinOp.Value, value: AnyVal) extends Pred[T] | |
case class Select[+T <: Table](fields: Seq[Col[_, T]]) { | |
def from[U >: T <: Table](table: U): From[U] = From(this, table) | |
} | |
case class From[+T <: Table](select: Select[T], table: T) { | |
def where[U >: T <: Table](condition: Pred[U]): QueryStatement[U] = QueryStatement(Where(this, condition), null, null, null) | |
} | |
case class Where[+T <: Table](from: From[T], condition: Pred[T]) | |
case class QueryStatement[+T <: Table](where: Where[T], ordering: QueryOrdering[T], grouping: Seq[Col[_, T]], limit: QueryLimit) { | |
val table: T = where.from.table | |
val projection: Seq[Col[_, T]] = where.from.select.fields | |
val condition: Pred[T] = where.condition | |
def groupBy[U >: T <: Table](columns: Col[_, U]*): QueryStatement[U] = QueryStatement(where, ordering, Seq(columns: _*), limit) | |
def orderBy[U >: T <: Table](ordering: QueryOrdering[U]): QueryStatement[U] = QueryStatement(where, ordering, grouping, limit) | |
def limit(offset: Int, limit: Int): QueryStatement[T] = QueryStatement(where, ordering, grouping, QueryLimit(offset, limit)) | |
} | |
case class QueryLimit(left: Int, right: Int) | |
object OrderDirection extends Enumeration { | |
val Asc, Desc = Value | |
} | |
case class QueryOrdering[+T <: Table](order: Seq[Col[_, T]], direction: OrderDirection.Value) | |
def select[T <: Table](projections: Col[_, T]*): Select[T] = { | |
Select(Seq(projections: _*)) | |
} | |
case class BinOpBuilder[V, T <: Table](col: Col[V, T]) { | |
def >=(value: V): Pred[T] = BinPred(col, BinOp.GreaterOrEqual, value.asInstanceOf[AnyVal]) | |
def <=(value: V): Pred[T] = BinPred(col, BinOp.LessOrEqual, value.asInstanceOf[AnyVal]) | |
def >(value: V): Pred[T] = BinPred(col, BinOp.GreaterThan, value.asInstanceOf[AnyVal]) | |
def <(value: V): Pred[T] = BinPred(col, BinOp.LessThan, value.asInstanceOf[AnyVal]) | |
def between(left: V, right: V): Pred[T] = BinPred(col, BinOp.Between, (left, right).asInstanceOf[AnyVal]) | |
def in(values: V*): Pred[T] = BinPred(col, BinOp.In, Seq(values).asInstanceOf[AnyVal]) | |
def notIn(values: V*): Pred[T] = BinPred(col, BinOp.NotIn, Seq(values).asInstanceOf[AnyVal]) | |
} | |
def asc[T <: Table](columns: Col[_, T]*): QueryOrdering[T] = QueryOrdering(Seq(columns: _*), OrderDirection.Asc) | |
def desc[T <: Table](columns: Col[_, T]*): QueryOrdering[T] = QueryOrdering(Seq(columns: _*), OrderDirection.Desc) | |
implicit def toColumn[T <: Table](name: String)(implicit table: T): Col[_, T] = Col[AnyVal, T](name)(table) | |
implicit def toOpBuilder[T <: Table, V](col: Col[V, T]): BinOpBuilder[V, T] = BinOpBuilder(col) | |
case class SampleTable(name: String) extends Table { | |
override def equals(that: Any): Boolean = { | |
super.equals(that) | |
} | |
} | |
object SampleTable { | |
implicit val sample_table: SampleTable = SampleTable("sample_table") | |
val id: Col[Int, SampleTable] = Col[Int, SampleTable]("id") | |
val name: Col[String, SampleTable] = Col[String, SampleTable]("name") | |
val value: Col[Int, SampleTable] = Col[Int, SampleTable]("value") | |
val label: Col[String, SampleTable] = Col[String, SampleTable]("label") | |
} | |
def main(args: Array[String]): Unit = { | |
import SampleTable._ | |
val q = select(name, id) | |
.from(sample_table) | |
.where((name >= "5") and (id == 8)) | |
.groupBy(label) | |
.orderBy(desc(value)) | |
.limit(1, 2) | |
val q2 = select(name, id) from sample_table where ( | |
(name >= "5") and (id == 8) | |
) groupBy(label, id) orderBy desc(value) limit(1, 2) | |
val tableName = tableNameOf(q2) | |
println(q) | |
println(q2) | |
println(tableName) | |
} | |
def tableNameOf[T <: Table](q: QueryStatement[T]): String = { | |
q.table.name | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment