Skip to content

Commit

Permalink
fix #80 - quotation: allow custom ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
fwbrasil committed Jan 2, 2016
1 parent 892eb37 commit 2fe7556
Show file tree
Hide file tree
Showing 49 changed files with 403 additions and 348 deletions.
20 changes: 17 additions & 3 deletions README.md
Expand Up @@ -256,12 +256,26 @@ db.run(q)

**sortBy**
```scala
val q = quote {
val q1 = quote {
query[Person].sortBy(p => p.age)
}

db.run(q)
// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.age
db.run(q1)
// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.age ASC NULLS FIRST

val q2 = quote {
query[Person].sortBy(p => p.age)(Ord.descNullsLast)
}

db.run(q2)
// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.age DESC NULLS LAST

val q3 = quote {
query[Person].sortBy(p => (p.name, p.age))(Ord(Ord.asc, Ord.desc))
}

db.run(q3)
// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.name ASC, p.age DESC
```

**drop/take**
Expand Down
23 changes: 23 additions & 0 deletions quill-core/src/main/scala/io/getquill/Ord.scala
@@ -0,0 +1,23 @@
package io.getquill

trait Ord[T]

trait OrdOps {

def asc[T](implicit ord: Ordering[T]): Ord[T]
def desc[T](implicit ord: Ordering[T]): Ord[T]
def ascNullsFirst[T](implicit ord: Ordering[T]): Ord[T]
def descNullsFirst[T](implicit ord: Ordering[T]): Ord[T]
def ascNullsLast[T](implicit ord: Ordering[T]): Ord[T]
def descNullsLast[T](implicit ord: Ordering[T]): Ord[T]

def apply[T1, T2](o1: Ord[T1], o2: Ord[T2]): Ord[(T1, T2)]
def apply[T1, T2, T3](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3]): Ord[(T1, T2, T3)]
def apply[T1, T2, T3, T4](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4]): Ord[(T1, T2, T3, T4)]
def apply[T1, T2, T3, T4, T5](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5]): Ord[(T1, T2, T3, T4, T5)]
def apply[T1, T2, T3, T4, T5, T6](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6]): Ord[(T1, T2, T3, T4, T5, T6)]
def apply[T1, T2, T3, T4, T5, T6, T7](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7]): Ord[(T1, T2, T3, T4, T5, T6, T7)]
def apply[T1, T2, T3, T4, T5, T6, T7, T8](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7], o8: Ord[T8]): Ord[(T1, T2, T3, T4, T5, T6, T7, T8)]
def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7], o8: Ord[T8], o9: Ord[T9]): Ord[(T1, T2, T3, T4, T5, T6, T7, T8, T9)]
def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7], o8: Ord[T8], o9: Ord[T9], o10: Ord[T10]): Ord[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)]
}
12 changes: 1 addition & 11 deletions quill-core/src/main/scala/io/getquill/Query.scala
Expand Up @@ -6,7 +6,7 @@ sealed trait Query[+T] {
def flatMap[R](f: T => Query[R]): Query[R]
def withFilter(f: T => Boolean): Query[T]
def filter(f: T => Boolean): Query[T]
def sortBy[R](f: T => R)(implicit ord: Ordering[R]): SortedQuery[T]
def sortBy[R](f: T => R)(implicit ord: Ord[R]): Query[T]

def take(n: Int): Query[T]
def drop(n: Int): Query[T]
Expand Down Expand Up @@ -35,16 +35,6 @@ sealed trait OuterJoinQuery[A, B, R] extends Query[R] {
def on(f: (A, B) => Boolean): Query[R]
}

sealed trait SortedQuery[+T] extends Query[T] {

def reverse: SortedQuery[T]

def map[R](f: T => R): SortedQuery[R]
def flatMap[R](f: T => Query[R]): SortedQuery[R]
def withFilter(f: T => Boolean): SortedQuery[T]
def filter(f: T => Boolean): SortedQuery[T]
}

sealed trait EntityQuery[T]
extends Query[T]
with Insertable[T]
Expand Down
15 changes: 12 additions & 3 deletions quill-core/src/main/scala/io/getquill/ast/Ast.scala
Expand Up @@ -27,14 +27,23 @@ case class Map(query: Ast, alias: Ident, body: Ast) extends Query

case class FlatMap(query: Ast, alias: Ident, body: Ast) extends Query

case class SortBy(query: Ast, alias: Ident, criterias: Ast) extends Query
case class SortBy(query: Ast, alias: Ident, criterias: Ast, ordering: Ordering) extends Query

sealed trait Ordering
case class TupleOrdering(elems: List[Ordering]) extends Ordering

sealed trait PropertyOrdering extends Ordering
case object Asc extends PropertyOrdering
case object Desc extends PropertyOrdering
case object AscNullsFirst extends PropertyOrdering
case object DescNullsFirst extends PropertyOrdering
case object AscNullsLast extends PropertyOrdering
case object DescNullsLast extends PropertyOrdering

case class GroupBy(query: Ast, alias: Ident, body: Ast) extends Query

case class Aggregation(operator: AggregationOperator, ast: Ast) extends Query

case class Reverse(query: Ast) extends Query

case class Take(query: Ast, n: Ast) extends Query

case class Drop(query: Ast, n: Ast) extends Query
Expand Down
20 changes: 15 additions & 5 deletions quill-core/src/main/scala/io/getquill/ast/AstShow.scala
Expand Up @@ -57,18 +57,15 @@ object AstShow {
case FlatMap(source, alias, body) =>
s"${source.show}.flatMap(${alias.show} => ${body.show})"

case SortBy(source, alias, body) =>
s"${source.show}.sortBy(${alias.show} => ${body.show})"
case SortBy(source, alias, body, ordering) =>
s"${source.show}.sortBy(${alias.show} => ${body.show})(${ordering.show})"

case GroupBy(source, alias, body) =>
s"${source.show}.groupBy(${alias.show} => ${body.show})"

case Aggregation(op, ast) =>
s"${scopedShow(ast)}.${op.show}"

case Reverse(source) =>
s"${source.show}.reverse"

case Take(source, n) =>
s"${source.show}.take($n)"

Expand All @@ -86,6 +83,19 @@ object AstShow {
}
}

implicit val orderingShow: Show[Ordering] = new Show[Ordering] {
def show(q: Ordering) =
q match {
case TupleOrdering(elems) => s"Ord(${elems.show})"
case Asc => s"Ord.asc"
case Desc => s"Ord.desc"
case AscNullsFirst => s"Ord.ascNullsFirst"
case DescNullsFirst => s"Ord.descNullsFirst"
case AscNullsLast => s"Ord.ascNullsLast"
case DescNullsLast => s"Ord.descNullsLast"
}
}

implicit val optionOperationShow: Show[OptionOperation] = new Show[OptionOperation] {
def show(q: OptionOperation) = {
val method = q.t match {
Expand Down
Expand Up @@ -54,20 +54,17 @@ trait StatefulTransformer[T] {
val (at, att) = apply(a)
val (ct, ctt) = att.apply(c)
(FlatMap(at, b, ct), ctt)
case SortBy(a, b, c) =>
case SortBy(a, b, c, d) =>
val (at, att) = apply(a)
val (ct, ctt) = att.apply(c)
(SortBy(at, b, ct), ctt)
(SortBy(at, b, ct, d), ctt)
case GroupBy(a, b, c) =>
val (at, att) = apply(a)
val (ct, ctt) = att.apply(c)
(GroupBy(at, b, ct), ctt)
case Aggregation(o, a) =>
val (at, att) = apply(a)
(Aggregation(o, at), att)
case Reverse(a) =>
val (at, att) = apply(a)
(Reverse(at), att)
case Take(a, b) =>
val (at, att) = apply(a)
val (bt, btt) = att.apply(b)
Expand Down
Expand Up @@ -25,10 +25,9 @@ trait StatelessTransformer {
case Filter(a, b, c) => Filter(apply(a), b, apply(c))
case Map(a, b, c) => Map(apply(a), b, apply(c))
case FlatMap(a, b, c) => FlatMap(apply(a), b, apply(c))
case SortBy(a, b, c) => SortBy(apply(a), b, apply(c))
case SortBy(a, b, c, d) => SortBy(apply(a), b, apply(c), d)
case GroupBy(a, b, c) => GroupBy(apply(a), b, apply(c))
case Aggregation(o, a) => Aggregation(o, apply(a))
case Reverse(a) => Reverse(apply(a))
case Take(a, b) => Take(apply(a), apply(b))
case Drop(a, b) => Drop(apply(a), apply(b))
case Union(a, b) => Union(apply(a), apply(b))
Expand Down
Expand Up @@ -31,13 +31,8 @@ object AdHocReduction {

// a.flatMap(b => c).sortBy(d => e) =>
// a.flatMap(b => c.sortBy(d => e))
case SortBy(FlatMap(a, b, c), d, e) =>
Some(FlatMap(a, b, SortBy(c, d, e)))

// a.flatMap(b => c).reverse =>
// a.flatMap(b => c.reverse)
case Reverse(FlatMap(a, b, c)) =>
Some(FlatMap(a, b, Reverse(c)))
case SortBy(FlatMap(a, b, c), d, e, f) =>
Some(FlatMap(a, b, SortBy(c, d, e, f)))

// a.flatMap(b => c.union(d))
// a.flatMap(b => c).union(a.flatMap(b => d))
Expand Down
Expand Up @@ -10,7 +10,7 @@ object ApplyIntermediateMap {
case Map(Map(a: GroupBy, b, c), d, e) => None
case FlatMap(Map(a: GroupBy, b, c), d, e) => None
case Filter(Map(a: GroupBy, b, c), d, e) => None
case SortBy(Map(a: GroupBy, b, c), d, e) => None
case SortBy(Map(a: GroupBy, b, c), d, e, f) => None
case Map(a: GroupBy, b, c) if (b == c) => None

// a.map(b => b) =>
Expand Down Expand Up @@ -38,9 +38,9 @@ object ApplyIntermediateMap {

// a.map(b => c).sortBy(d => e) =>
// a.sortBy(b => e[d := c]).map(b => c)
case SortBy(Map(a, b, c), d, e) =>
case SortBy(Map(a, b, c), d, e, f) =>
val er = BetaReduction(e, d -> c)
Some(Map(SortBy(a, b, er), b, c))
Some(Map(SortBy(a, b, er, f), b, c))

case other => None
}
Expand Down
Expand Up @@ -11,13 +11,12 @@ object AttachToEntity {
case Map(a: Entity, b, c) => Map(f(a, b), b, c)
case FlatMap(a: Entity, b, c) => FlatMap(f(a, b), b, c)
case Filter(a: Entity, b, c) => Filter(f(a, b), b, c)
case SortBy(a: Entity, b, c) => SortBy(f(a, b), b, c)
case SortBy(a: Entity, b, c, d) => SortBy(f(a, b), b, c, d)

case Map(a: Query, b, c) => Map(apply(f, Some(b))(a), b, c)
case FlatMap(a: Query, b, c) => FlatMap(apply(f, Some(b))(a), b, c)
case Filter(a: Query, b, c) => Filter(apply(f, Some(b))(a), b, c)
case SortBy(a: Query, b, c) => SortBy(apply(f, Some(b))(a), b, c)
case Reverse(a: Query) => Reverse(apply(f, alias)(a))
case SortBy(a: Query, b, c, d) => SortBy(apply(f, Some(b))(a), b, c, d)
case Take(a: Query, b) => Take(apply(f, alias)(a), b)
case Drop(a: Query, b) => Drop(apply(f, alias)(a), b)
case GroupBy(a: Query, b, c) => GroupBy(apply(f, Some(b))(a), b, c)
Expand Down
Expand Up @@ -29,13 +29,13 @@ case class BetaReduction(map: collection.Map[Ident, Ast])
Map(apply(a), b, BetaReduction(map - b)(c))
case FlatMap(a, b, c) =>
FlatMap(apply(a), b, BetaReduction(map - b)(c))
case SortBy(a, b, c) =>
SortBy(apply(a), b, BetaReduction(map - b)(c))
case SortBy(a, b, c, d) =>
SortBy(apply(a), b, BetaReduction(map - b)(c), d)
case GroupBy(a, b, c) =>
GroupBy(apply(a), b, BetaReduction(map - b)(c))
case OuterJoin(t, a, b, iA, iB, on) =>
OuterJoin(t, apply(a), apply(b), iA, iB, BetaReduction(map - iA - iB)(on))
case _: Reverse | _: Take | _: Entity | _: Drop | _: Union | _: UnionAll | _: Aggregation =>
case _: Take | _: Entity | _: Drop | _: Union | _: UnionAll | _: Aggregation =>
super.apply(query)
}
}
Expand Down
Expand Up @@ -10,10 +10,9 @@ object NormalizeNestedStructures {
case Map(a, b, c) => apply(a, c)(Map(_, b, _))
case FlatMap(a, b, c) => apply(a, c)(FlatMap(_, b, _))
case Filter(a, b, c) => apply(a, c)(Filter(_, b, _))
case SortBy(a, b, c) => apply(a, c)(SortBy(_, b, _))
case SortBy(a, b, c, d) => apply(a, c)(SortBy(_, b, _, d))
case GroupBy(a, b, c) => apply(a, c)(GroupBy(_, b, _))
case Aggregation(a, b) => apply(b)(Aggregation(a, _))
case Reverse(a) => apply(a)(Reverse)
case Take(a, b) => apply(a, b)(Take)
case Drop(a, b) => apply(a, b)(Drop)
case Union(a, b) => apply(a, b)(Union)
Expand Down
15 changes: 2 additions & 13 deletions quill-core/src/main/scala/io/getquill/norm/OrderTerms.scala
Expand Up @@ -7,23 +7,12 @@ object OrderTerms {
def unapply(q: Query) =
q match {

case Reverse(Map(a: GroupBy, b, c)) => None
case Take(Map(a: GroupBy, b, c), d) => None

// a.reverse.filter(b => c) =>
// a.filter(b => c).reverse
case Filter(Reverse(a), b, c) =>
Some(Reverse(Filter(a, b, c)))

// a.map(b => c).reverse =>
// a.reverse.map(b => c)
case Reverse(Map(a, b, c)) =>
Some(Map(Reverse(a), b, c))

// a.sortBy(b => c).filter(d => e) =>
// a.filter(d => e).sortBy(b => c)
case Filter(SortBy(a, b, c), d, e) =>
Some(SortBy(Filter(a, d, e), b, c))
case Filter(SortBy(a, b, c, d), e, f) =>
Some(SortBy(Filter(a, e, f), b, c, d))

// a.map(b => c).take(d) =>
// a.take(d).map(b => c)
Expand Down
Expand Up @@ -7,11 +7,6 @@ object SymbolicReduction {
def unapply(q: Query) =
q match {

// a.reverse.reverse =>
// a
case Reverse(Reverse(a: Query)) =>
Some(a)

// a.filter(b => c).flatMap(d => e.$) =>
// a.flatMap(d => e.filter(_ => c[b := d]).$)
case FlatMap(Filter(a, b, c), d, e: Query) =>
Expand Down
Expand Up @@ -18,8 +18,8 @@ private case class AvoidAliasConflict(state: Set[Ident])
case Filter(q: Entity, x, p) =>
apply(x, p)(Filter(q, _, _))

case SortBy(q: Entity, x, p) =>
apply(x, p)(SortBy(q, _, _))
case SortBy(q: Entity, x, p, o) =>
apply(x, p)(SortBy(q, _, _, o))

case OuterJoin(t, a, b, iA, iB, o) =>
val freshA = freshIdent(iA)
Expand Down
Expand Up @@ -17,15 +17,12 @@ case class Dealias(state: Option[Ident]) extends StatefulTransformer[Option[Iden
dealias(a, b, c)(Map)
case Filter(a, b, c) =>
dealias(a, b, c)(Filter)
case SortBy(a, b, c) =>
dealias(a, b, c)(SortBy)
case SortBy(a, b, c, d) =>
dealias(a, b, c)(SortBy(_, _, _, d))
case GroupBy(a, b, c) =>
dealias(a, b, c)(GroupBy)
case q: Aggregation =>
(q, Dealias(None))
case Reverse(a) =>
val (an, ant) = apply(a)
(Reverse(an), ant)
case Take(a, b) =>
val (an, ant) = apply(a)
(Take(an, b), ant)
Expand Down
4 changes: 4 additions & 0 deletions quill-core/src/main/scala/io/getquill/package.scala
Expand Up @@ -21,4 +21,8 @@ package object getquill {
def mappedEncoding[I, O](f: I => O) = source.MappedEncoding(f)

type Quoted[T] = quotation.Quoted[T]

def Ord: OrdOps = NonQuotedException()

implicit def orderingToOrd[T](implicit o: Ordering[T]): Ord[T] = NonQuotedException()
}
Expand Up @@ -22,17 +22,17 @@ case class FreeVariables(state: State)

override def apply(query: Query): (Query, StatefulTransformer[State]) =
query match {
case q @ Filter(a, b, c) => (q, free(a, b, c))
case q @ Map(a, b, c) => (q, free(a, b, c))
case q @ FlatMap(a, b, c) => (q, free(a, b, c))
case q @ SortBy(a, b, c) => (q, free(a, b, c))
case q @ GroupBy(a, b, c) => (q, free(a, b, c))
case q @ Filter(a, b, c) => (q, free(a, b, c))
case q @ Map(a, b, c) => (q, free(a, b, c))
case q @ FlatMap(a, b, c) => (q, free(a, b, c))
case q @ SortBy(a, b, c, d) => (q, free(a, b, c))
case q @ GroupBy(a, b, c) => (q, free(a, b, c))
case q @ OuterJoin(t, a, b, iA, iB, on) =>
val (_, freeA) = apply(a)
val (_, freeB) = apply(a)
val (_, freeOn) = FreeVariables(State(state.seen + iA + iB, Set.empty))(on)
(q, FreeVariables(State(state.seen, state.free ++ freeA.state.free ++ freeB.state.free ++ freeOn.state.free)))
case _: Entity | _: Reverse | _: Take | _: Drop | _: Union | _: UnionAll | _: Aggregation =>
case _: Entity | _: Take | _: Drop | _: Union | _: UnionAll | _: Aggregation =>
super.apply(query)
}

Expand Down
13 changes: 11 additions & 2 deletions quill-core/src/main/scala/io/getquill/quotation/Liftables.scala
Expand Up @@ -72,10 +72,9 @@ trait Liftables {
case Filter(a, b, c) => q"$pack.Filter($a, $b, $c)"
case Map(a, b, c) => q"$pack.Map($a, $b, $c)"
case FlatMap(a, b, c) => q"$pack.FlatMap($a, $b, $c)"
case SortBy(a, b, c) => q"$pack.SortBy($a, $b, $c)"
case SortBy(a, b, c, d) => q"$pack.SortBy($a, $b, $c, $d)"
case GroupBy(a, b, c) => q"$pack.GroupBy($a, $b, $c)"
case Aggregation(a, b) => q"$pack.Aggregation($a, $b)"
case Reverse(a) => q"$pack.Reverse($a)"
case Take(a, b) => q"$pack.Take($a, $b)"
case Drop(a, b) => q"$pack.Drop($a, $b)"
case Union(a, b) => q"$pack.Union($a, $b)"
Expand All @@ -87,6 +86,16 @@ trait Liftables {
case PropertyAlias(a, b) => q"$pack.PropertyAlias($a, $b)"
}

implicit val orderingLiftable: Liftable[Ordering] = Liftable[Ordering] {
case TupleOrdering(elems) => q"$pack.TupleOrdering($elems)"
case Asc => q"$pack.Asc"
case Desc => q"$pack.Desc"
case AscNullsFirst => q"$pack.AscNullsFirst"
case DescNullsFirst => q"$pack.DescNullsFirst"
case AscNullsLast => q"$pack.AscNullsLast"
case DescNullsLast => q"$pack.DescNullsLast"
}

implicit val outerJoinTypeLiftable: Liftable[OuterJoinType] = Liftable[OuterJoinType] {
case LeftJoin => q"$pack.LeftJoin"
case RightJoin => q"$pack.RightJoin"
Expand Down

0 comments on commit 2fe7556

Please sign in to comment.