Skip to content

Instantly share code, notes, and snippets.

@hackeris
Last active May 8, 2021 14:01
Show Gist options
  • Save hackeris/cebdb09dc02fffc0a40f6bfea4930ca3 to your computer and use it in GitHub Desktop.
Save hackeris/cebdb09dc02fffc0a40f6bfea4930ca3 to your computer and use it in GitHub Desktop.
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