From a0588a9078f363a99603a912c17823fb0a2cbfef Mon Sep 17 00:00:00 2001 From: Jason Liszka Date: Thu, 7 Jun 2012 01:56:09 -0400 Subject: [PATCH] simplified phantom types --- .../com/foursquare/rogue/PhantomTypes.scala | 55 +-- .../scala/com/foursquare/rogue/Query.scala | 389 ++++++------------ .../com/foursquare/rogue/QueryExecutor.scala | 55 +-- .../com/foursquare/rogue/QueryHelpers.scala | 12 +- .../com/foursquare/rogue/QueryOptimizer.scala | 2 +- .../scala/com/foursquare/rogue/Rogue.scala | 19 +- .../scala/com/foursquare/rogue/package.scala | 10 +- .../foursquare/rogue/ExecutableQuery.scala | 59 ++- .../com/foursquare/rogue/LiftRogue.scala | 34 +- .../foursquare/rogue/QueryExecutorTest.scala | 2 +- .../com/foursquare/rogue/QueryTest.scala | 7 +- 11 files changed, 240 insertions(+), 404 deletions(-) diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/PhantomTypes.scala b/rogue-core/src/main/scala/com/foursquare/rogue/PhantomTypes.scala index a117f3d..a31b62c 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/PhantomTypes.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/PhantomTypes.scala @@ -6,36 +6,43 @@ package com.foursquare.rogue // *** Phantom types // *************************************************************************** -abstract sealed class MaybeOrdered -abstract sealed class Ordered extends MaybeOrdered -abstract sealed class Unordered extends MaybeOrdered +sealed trait Ordered +sealed trait Unordered +sealed trait Ord extends Ordered with Unordered -abstract sealed class MaybeSelected -abstract sealed class Selected extends MaybeSelected -abstract sealed class SelectedOne extends Selected -abstract sealed class Unselected extends MaybeSelected +sealed trait Selected +sealed trait SelectedOne extends Selected +sealed trait Unselected +sealed trait Sel extends Selected with SelectedOne with Unselected -abstract sealed class MaybeLimited -abstract sealed class Limited extends MaybeLimited -abstract sealed class Unlimited extends MaybeLimited +sealed trait Limited +sealed trait Unlimited +sealed trait Lim extends Limited with Unlimited -abstract sealed class MaybeSkipped -abstract sealed class Skipped extends MaybeSkipped -abstract sealed class Unskipped extends MaybeSkipped +sealed trait Skipped +sealed trait Unskipped +sealed trait Sk extends Skipped with Unskipped + +sealed trait HasOrClause +sealed trait HasNoOrClause +sealed trait Or extends HasOrClause with HasNoOrClause + +class AddOrder[-In, +Out] +class AddSelect[-In, +Out, +One] +class AddLimit[-In, +Out] +class AddSkip[-In, +Out] +class AddOrClause[-In, +Out] -abstract sealed class MaybeHasOrClause -abstract sealed class HasOrClause extends MaybeHasOrClause -abstract sealed class HasNoOrClause extends MaybeHasOrClause sealed trait MaybeIndexed sealed trait Indexable extends MaybeIndexed sealed trait IndexScannable extends MaybeIndexed -abstract sealed class NoIndexInfo extends Indexable with IndexScannable -abstract sealed class Index extends Indexable with IndexScannable -abstract sealed class PartialIndexScan extends IndexScannable -abstract sealed class IndexScan extends IndexScannable -abstract sealed class DocumentScan extends MaybeIndexed +sealed trait NoIndexInfo extends Indexable with IndexScannable +sealed trait Index extends Indexable with IndexScannable +sealed trait PartialIndexScan extends IndexScannable +sealed trait IndexScan extends IndexScannable +sealed trait DocumentScan extends MaybeIndexed case object NoIndexInfo extends NoIndexInfo case object Index extends Index @@ -43,6 +50,6 @@ case object PartialIndexScan extends PartialIndexScan case object IndexScan extends IndexScan case object DocumentScan extends DocumentScan -abstract sealed class MaybeUsedIndex -abstract sealed class UsedIndex extends MaybeUsedIndex -abstract sealed class HasntUsedIndex extends MaybeUsedIndex +sealed trait MaybeUsedIndex +sealed trait UsedIndex extends MaybeUsedIndex +sealed trait HasntUsedIndex extends MaybeUsedIndex diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/Query.scala b/rogue-core/src/main/scala/com/foursquare/rogue/Query.scala index 5935a1e..8908983 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/Query.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/Query.scala @@ -47,15 +47,7 @@ import scala.collection.immutable.ListMap * an or-clause. The type system will guarantee that the query and its or-connected query * have the same types. */ -case class BaseQuery[ - M, - R, - Ord <: MaybeOrdered, - Sel <: MaybeSelected, - Lim <: MaybeLimited, - Sk <: MaybeSkipped, - Or <: MaybeHasOrClause -]( +case class BaseQuery[M, R, +State]( meta: M, collectionName: String, lim: Option[Int], @@ -71,7 +63,7 @@ case class BaseQuery[ private def addClause[F](clause: M => QueryClause[F], expectedIndexBehavior: MaybeIndexed): - AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] = { + AbstractQuery[M, R, State] = { val cl = clause(meta) val newClause = cl.withExpectedIndexBehavior(expectedIndexBehavior) this.copy(condition = condition.copy(clauses = newClause :: condition.clauses)) @@ -122,7 +114,7 @@ case class BaseQuery[ def scanOpt[V, F](opt: Option[V])(clause: (M, V) => QueryClause[F]) = addClauseOpt(opt)(clause, expectedIndexBehavior = DocumentScan) - def raw(f: BasicDBObjectBuilder => Unit): AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] = { + def raw(f: BasicDBObjectBuilder => Unit): AbstractQuery[M, R, State] = { val newClause = new RawQueryClause(f) this.copy(condition = condition.copy(clauses = newClause :: condition.clauses)) } @@ -137,10 +129,10 @@ case class BaseQuery[ * you can see that the "MaybeHasOrClause" type parameter is changed, and is now specifically * bound to "HasOrClause", rather than to a type variable.

*/ - def or(subqueries: (AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] => AbstractQuery[M, R, Ord, Sel, Lim, Sk, _])*) - (implicit ev: Or =:= HasNoOrClause): AbstractQuery[M, R, Ord, Sel, Lim, Sk, HasOrClause] = { + def or[S2](subqueries: (AbstractQuery[M, R, Ordered with Selected with Limited with Skipped with HasNoOrClause] => AbstractQuery[M, R, _])*) + (implicit ev: AddOrClause[State, S2]): AbstractQuery[M, R, S2] = { val queryBuilder = - this.copy[M, R, Ord, Sel, Lim, Sk, Or]( + this.copy[M, R, Ordered with Selected with Limited with Skipped with HasNoOrClause]( lim = None, sk = None, maxScan = None, @@ -162,30 +154,30 @@ case class BaseQuery[ * type signature of the returned query is updated so that the "MaybeOrdered" type parameter is * now Ordered. */ - def orderAsc[V](field: M => AbstractQueryField[V, _, M]) - (implicit ev: Ord =:= Unordered): BaseQuery[M, R, Ordered, Sel, Lim, Sk, Or] = + def orderAsc[V, S2](field: M => AbstractQueryField[V, _, M]) + (implicit ev: AddOrder[State, S2]): BaseQuery[M, R, S2] = this.copy(order = Some(MongoOrder(List((field(meta).field.name, true))))) - def orderDesc[V](field: M => AbstractQueryField[V, _, M]) - (implicit ev: Ord =:= Unordered): BaseQuery[M, R, Ordered, Sel, Lim, Sk, Or] = + def orderDesc[V, S2](field: M => AbstractQueryField[V, _, M]) + (implicit ev: AddOrder[State, S2]): BaseQuery[M, R, S2] = this.copy(order = Some(MongoOrder(List((field(meta).field.name, false))))) def andAsc[V](field: M => AbstractQueryField[V, _, M]) - (implicit ev: Ord =:= Ordered): BaseQuery[M, R, Ordered, Sel, Lim, Sk, Or] = + (implicit ev: State <:< Ordered): BaseQuery[M, R, State] = this.copy(order = Some(MongoOrder((field(meta).field.name, true) :: order.get.terms))) def andDesc[V](field: M => AbstractQueryField[V, _, M]) - (implicit ev: Ord =:= Ordered): BaseQuery[M, R, Ordered, Sel, Lim, Sk, Or] = + (implicit ev: State <:< Ordered): BaseQuery[M, R, State] = this.copy(order = Some(MongoOrder((field(meta).field.name, false) :: order.get.terms))) /** * Natural ordering. * TODO: doesn't make sense in conjunction with ordering on any other fields. enforce w/ phantom types? */ - def orderNaturalAsc[V](implicit ev: Ord =:= Unordered): BaseQuery[M, R, Ordered, Sel, Lim, Sk, Or] = + def orderNaturalAsc[V, S2](implicit ev: AddOrder[State, S2]): BaseQuery[M, R, S2] = this.copy(order = Some(MongoOrder(List(("$natural", true))))) - def orderNaturalDesc[V](implicit ev: Ord =:= Unordered): BaseQuery[M, R, Ordered, Sel, Lim, Sk, Or] = + def orderNaturalDesc[V, S2](implicit ev: AddOrder[State, S2]): BaseQuery[M, R, S2] = this.copy(order = Some(MongoOrder(List(("$natural", false))))) /** @@ -197,10 +189,10 @@ case class BaseQuery[ * type signature of the returned query is updated so that the "MaybeLimited" type parameter is * now Limited.

*/ - def limit(n: Int)(implicit ev: Lim =:= Unlimited): BaseQuery[M, R, Ord, Sel, Limited, Sk, Or] = + def limit[S2](n: Int)(implicit ev: AddLimit[State, S2]): BaseQuery[M, R, S2] = this.copy(lim = Some(n)) - def limitOpt(n: Option[Int])(implicit ev: Lim =:= Unlimited): BaseQuery[M, R, Ord, Sel, Limited, Sk, Or] = + def limitOpt[S2](n: Option[Int])(implicit ev: AddLimit[State, S2]): BaseQuery[M, R, S2] = this.copy(lim = n) /** @@ -212,15 +204,14 @@ case class BaseQuery[ * type signature of the returned query is updated so that the {@link MaybeSkipped} type parameter is * now {@link Skipped}.

*/ - def skip(n: Int)(implicit ev: Sk =:= Unskipped): BaseQuery[M, R, Ord, Sel, Lim, Skipped, Or] = + def skip[S2](n: Int)(implicit ev: AddSkip[State, S2]): BaseQuery[M, R, S2] = this.copy(sk = Some(n)) - def skipOpt(n: Option[Int])(implicit ev: Sk =:= Unskipped): BaseQuery[M, R, Ord, Sel, Lim, Skipped, Or] = + def skipOpt[S2](n: Option[Int])(implicit ev: AddSkip[State, S2]): BaseQuery[M, R, S2] = this.copy(sk = n) - def noop()(implicit ev1: Sel =:= Unselected, - ev2: Lim =:= Unlimited, - ev3: Sk =:= Unskipped): BaseModifyQuery[M] = BaseModifyQuery(this, MongoModify(Nil)) // TODO: Does this work? + def noop()(implicit ev: State <:< Unselected with Unlimited with Unskipped): BaseModifyQuery[M] = + BaseModifyQuery(this, MongoModify(Nil)) // TODO: Does this work? override def toString: String = MongoBuilder.buildQueryString("find", collectionName, this) @@ -231,9 +222,9 @@ case class BaseQuery[ def signature(): String = MongoBuilder.buildSignature(collectionName, this) - def maxScan(max: Int): AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] = this.copy(maxScan = Some(max)) + def maxScan(max: Int): AbstractQuery[M, R, State] = this.copy(maxScan = Some(max)) - def comment(c: String): AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] = this.copy(comment = Some(c)) + def comment(c: String): AbstractQuery[M, R, State] = this.copy(comment = Some(c)) /** * Set a flag to indicate whether this query should hit primaries or secondaries. @@ -246,9 +237,9 @@ case class BaseQuery[ * For more info, see * http://www.mongodb.org/display/DOCS/slaveOk */ - def setReadPreference(r: ReadPreference): AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] = this.copy(readPreference = Some(r)) + def setReadPreference(r: ReadPreference): AbstractQuery[M, R, State] = this.copy(readPreference = Some(r)) - def hint(index: MongoIndex[M]): AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or] = this.copy(hint = Some(index.asListMap)) + def hint(index: MongoIndex[M]): AbstractQuery[M, R, State] = this.copy(hint = Some(index.asListMap)) /** * Adds a select clause to the query. The use of this method constrains the type @@ -261,290 +252,178 @@ case class BaseQuery[ * you can see that the "MaybeSelected" type parameter is changed, and is now specifically * bound to "Selected", rather than to a type variable.

*/ - def select[F1](f: M => SelectField[F1, M]) - (implicit ev: Sel =:= Unselected): BaseQuery[M, F1, Ord, Selected, Lim, Sk, Or] = { - selectCase(f, (f: F1) => f) + + /* + + // Code to generate the select() and selectCase() methods: + + def select(n: Int) = { + val typeParams = (1 to n).map(i => "F%d".format(i)).mkString(", ") + val arguments = (1 to n).map(i => "f%d: M => SelectField[F%d, M]".format(i, i)).mkString(", ") + val outParams = if (n == 1) "_, S2" else "S2, _" + val resultType = if (n == 1) typeParams else "(%s)".format(typeParams) + val vars = (1 to n).map(i => "f%d".format(i)).mkString(", ") + val fnArgs = (1 to n).map(i => "f%d: F%d".format(i, i)).mkString(", ") + val fnResult = if (n == 1) vars else "(%s)".format(vars) + val code = """ + def select[%s, S2](%s) + (implicit ev: AddSelect[State, %s]): BaseQuery[M, %s, S2] = { + selectCase(%s, (%s) => %s) + }""" + code.format(typeParams, arguments, outParams, resultType, vars, fnArgs, fnResult) + } + + def selectCase(n: Int) = { + val typeParams = (1 to n).map(i => "F%d".format(i)).mkString(", ") + val arguments = (1 to n).map(i => "f%d: M => SelectField[F%d, M]".format(i, i)).mkString(", ") + val outParams = if (n == 1) "_, S2" else "S2, _" + val resultType = if (n == 1) typeParams else "(%s)".format(typeParams) + val instCalls = (1 to n).map(i => "f%d(inst)".format(i)).mkString(", ") + val createArgs = (1 to n).map(i => "xs(%d).asInstanceOf[F%d]".format(i-1, i)).mkString(", ") + val code = """ + def selectCase[%s, CC, S2](%s, + create: %s => CC)(implicit ev: AddSelect[State, %s]): BaseQuery[M, CC, S2] = { + val inst = meta + val fields = List(%s) + val transformer = (xs: List[_]) => create(%s) + this.copy(select = Some(MongoSelect(fields, transformer))) + }""" + code.format(typeParams, arguments, resultType, outParams, instCalls, createArgs) + } + + def generate(n: Int) = { + (1 to n).foreach(i => println(select(i))) + (1 to n).foreach(i => println(selectCase(i))) + } + */ + + def select[F1, S2](f1: M => SelectField[F1, M]) + (implicit ev: AddSelect[State, _, S2]): BaseQuery[M, F1, S2] = { + selectCase(f1, (f1: F1) => f1) } - def select[F1, F2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M]) - (implicit ev: Sel =:= Unselected): BaseQuery[M, (F1, F2), Ord, Selected, Lim, Sk, Or] = { + def select[F1, F2, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2), S2] = { selectCase(f1, f2, (f1: F1, f2: F2) => (f1, f2)) } - def select[F1, F2, F3](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M]) - (implicit ev: Sel =:= Unselected): BaseQuery[M, (F1, F2, F3), Ord, Selected, Lim, Sk, Or] = { + def select[F1, F2, F3, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3), S2] = { selectCase(f1, f2, f3, (f1: F1, f2: F2, f3: F3) => (f1, f2, f3)) } - def select[F1, F2, F3, F4](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M]) - (implicit ev: Sel =:= Unselected): BaseQuery[M, (F1, F2, F3, F4), Ord, Selected, Lim, Sk, Or] = { + def select[F1, F2, F3, F4, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4), S2] = { selectCase(f1, f2, f3, f4, (f1: F1, f2: F2, f3: F3, f4: F4) => (f1, f2, f3, f4)) } - def select[F1, F2, F3, F4, F5](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M]) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, (F1, F2, F3, F4, F5), Ord, Selected, Lim, Sk, Or] = { + def select[F1, F2, F3, F4, F5, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4, F5), S2] = { selectCase(f1, f2, f3, f4, f5, (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5) => (f1, f2, f3, f4, f5)) } - def select[F1, F2, F3, F4, F5, F6](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M]) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, (F1, F2, F3, F4, F5, F6), Ord, Selected, Lim, Sk, Or] = { + def select[F1, F2, F3, F4, F5, F6, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4, F5, F6), S2] = { selectCase(f1, f2, f3, f4, f5, f6, (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6) => (f1, f2, f3, f4, f5, f6)) } - def select[F1, F2, F3, F4, F5, F6, F7](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M]) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7), Ord, Selected, Lim, Sk, Or] = { - selectCase(f1, f2, f3, f4, f5, f6, f7, - (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7) => (f1, f2, f3, f4, f5, f6, f7)) + def select[F1, F2, F3, F4, F5, F6, F7, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7), S2] = { + selectCase(f1, f2, f3, f4, f5, f6, f7, (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7) => (f1, f2, f3, f4, f5, f6, f7)) } - def select[F1, F2, F3, F4, F5, F6, F7, F8](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - f8: M => SelectField[F8, M]) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7, F8), Ord, Selected, Lim, Sk, Or] = { - selectCase(f1, f2, f3, f4, f5, f6, f7, f8, - (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7, f8: F8) => (f1, f2, f3, f4, f5, f6, f7, f8)) + def select[F1, F2, F3, F4, F5, F6, F7, F8, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], f8: M => SelectField[F8, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7, F8), S2] = { + selectCase(f1, f2, f3, f4, f5, f6, f7, f8, (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7, f8: F8) => (f1, f2, f3, f4, f5, f6, f7, f8)) } - def select[F1, F2, F3, F4, F5, F6, F7, F8, F9](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - f8: M => SelectField[F8, M], - f9: M => SelectField[F9, M]) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7, F8, F9), Ord, Selected, Lim, Sk, Or] = { - selectCase(f1, f2, f3, f4, f5, f6, f7, f8, f9, - (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7, f8: F8, f9: F9) => - (f1, f2, f3, f4, f5, f6, f7, f8, f9)) + def select[F1, F2, F3, F4, F5, F6, F7, F8, F9, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], f8: M => SelectField[F8, M], f9: M => SelectField[F9, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7, F8, F9), S2] = { + selectCase(f1, f2, f3, f4, f5, f6, f7, f8, f9, (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7, f8: F8, f9: F9) => (f1, f2, f3, f4, f5, f6, f7, f8, f9)) } - def select[F1, F2, F3, F4, F5, F6, F7, F8, F9, F10](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - f8: M => SelectField[F8, M], - f9: M => SelectField[F9, M], - f10: M => SelectField[F10, M]) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10), Ord, Selected, Lim, Sk, Or] = { - selectCase(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, - (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7, f8: F8, f9: F9, f10: F10) => - (f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)) + def select[F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], f8: M => SelectField[F8, M], f9: M => SelectField[F9, M], f10: M => SelectField[F10, M]) + (implicit ev: AddSelect[State, S2, _]): BaseQuery[M, (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10), S2] = { + selectCase(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, (f1: F1, f2: F2, f3: F3, f4: F4, f5: F5, f6: F6, f7: F7, f8: F8, f9: F9, f10: F10) => (f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)) } - def selectCase[F1, CC](f: M => SelectField[F1, M], - create: F1 => CC)(implicit ev: Sel =:= Unselected): - BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, CC, S2](f1: M => SelectField[F1, M], + create: F1 => CC)(implicit ev: AddSelect[State, _, S2]): BaseQuery[M, CC, S2] = { val inst = meta - val fields = List(f(inst)) - val transformer = (xs: List[_]) => create(xs.head.asInstanceOf[F1]) + val fields = List(f1(inst)) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - create: (F1, F2) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], + create: (F1, F2) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst)) val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - create: (F1, F2, F3) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], + create: (F1, F2, F3) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - create: (F1, F2, F3, F4) => CC) - (implicit ev: Sel =:= Unselected): - BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], + create: (F1, F2, F3, F4) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst), f4(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, F5, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - create: (F1, F2, F3, F4, F5) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, F5, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], + create: (F1, F2, F3, F4, F5) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4], - xs(4).asInstanceOf[F5]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4], xs(4).asInstanceOf[F5]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, F5, F6, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - create: (F1, F2, F3, F4, F5, F6) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, F5, F6, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], + create: (F1, F2, F3, F4, F5, F6) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst), f6(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4], - xs(4).asInstanceOf[F5], - xs(5).asInstanceOf[F6]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4], xs(4).asInstanceOf[F5], xs(5).asInstanceOf[F6]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, F5, F6, F7, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - create: (F1, F2, F3, F4, F5, F6, F7) => CC) - ( implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, F5, F6, F7, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], + create: (F1, F2, F3, F4, F5, F6, F7) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst), f6(inst), f7(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4], - xs(4).asInstanceOf[F5], - xs(5).asInstanceOf[F6], - xs(6).asInstanceOf[F7]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4], xs(4).asInstanceOf[F5], xs(5).asInstanceOf[F6], xs(6).asInstanceOf[F7]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, F5, F6, F7, F8, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - f8: M => SelectField[F8, M], - create: (F1, F2, F3, F4, F5, F6, F7, F8) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, F5, F6, F7, F8, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], f8: M => SelectField[F8, M], + create: (F1, F2, F3, F4, F5, F6, F7, F8) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst), f6(inst), f7(inst), f8(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4], - xs(4).asInstanceOf[F5], - xs(5).asInstanceOf[F6], - xs(6).asInstanceOf[F7], - xs(7).asInstanceOf[F8]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4], xs(4).asInstanceOf[F5], xs(5).asInstanceOf[F6], xs(6).asInstanceOf[F7], xs(7).asInstanceOf[F8]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, F5, F6, F7, F8, F9, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - f8: M => SelectField[F8, M], - f9: M => SelectField[F9, M], - create: (F1, F2, F3, F4, F5, F6, F7, F8, F9) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, F5, F6, F7, F8, F9, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], f8: M => SelectField[F8, M], f9: M => SelectField[F9, M], + create: (F1, F2, F3, F4, F5, F6, F7, F8, F9) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst), f6(inst), f7(inst), f8(inst), f9(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4], - xs(4).asInstanceOf[F5], - xs(5).asInstanceOf[F6], - xs(6).asInstanceOf[F7], - xs(7).asInstanceOf[F8], - xs(8).asInstanceOf[F9]) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4], xs(4).asInstanceOf[F5], xs(5).asInstanceOf[F6], xs(6).asInstanceOf[F7], xs(7).asInstanceOf[F8], xs(8).asInstanceOf[F9]) this.copy(select = Some(MongoSelect(fields, transformer))) } - def selectCase[F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, CC](f1: M => SelectField[F1, M], - f2: M => SelectField[F2, M], - f3: M => SelectField[F3, M], - f4: M => SelectField[F4, M], - f5: M => SelectField[F5, M], - f6: M => SelectField[F6, M], - f7: M => SelectField[F7, M], - f8: M => SelectField[F8, M], - f9: M => SelectField[F9, M], - f10: M => SelectField[F10, M], - create: (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10) => CC) - (implicit ev: Sel =:= Unselected): BaseQuery[M, CC, Ord, Selected, Lim, Sk, Or] = { + def selectCase[F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, CC, S2](f1: M => SelectField[F1, M], f2: M => SelectField[F2, M], f3: M => SelectField[F3, M], f4: M => SelectField[F4, M], f5: M => SelectField[F5, M], f6: M => SelectField[F6, M], f7: M => SelectField[F7, M], f8: M => SelectField[F8, M], f9: M => SelectField[F9, M], f10: M => SelectField[F10, M], + create: (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10) => CC)(implicit ev: AddSelect[State, S2, _]): BaseQuery[M, CC, S2] = { val inst = meta - val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst), - f6(inst), f7(inst), f8(inst), f9(inst), f10(inst)) - val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], - xs(1).asInstanceOf[F2], - xs(2).asInstanceOf[F3], - xs(3).asInstanceOf[F4], - xs(4).asInstanceOf[F5], - xs(5).asInstanceOf[F6], - xs(6).asInstanceOf[F7], - xs(7).asInstanceOf[F8], - xs(8).asInstanceOf[F9], - xs(9).asInstanceOf[F10]) + val fields = List(f1(inst), f2(inst), f3(inst), f4(inst), f5(inst), f6(inst), f7(inst), f8(inst), f9(inst), f10(inst)) + val transformer = (xs: List[_]) => create(xs(0).asInstanceOf[F1], xs(1).asInstanceOf[F2], xs(2).asInstanceOf[F3], xs(3).asInstanceOf[F4], xs(4).asInstanceOf[F5], xs(5).asInstanceOf[F6], xs(6).asInstanceOf[F7], xs(7).asInstanceOf[F8], xs(8).asInstanceOf[F9], xs(9).asInstanceOf[F10]) this.copy(select = Some(MongoSelect(fields, transformer))) } } @@ -554,14 +433,7 @@ case class BaseQuery[ // ******************************************************* case class BaseModifyQuery[M]( - query: BaseQuery[ - M, - _, - _ <: MaybeOrdered, - _ <: MaybeSelected, - _ <: MaybeLimited, - _ <: MaybeSkipped, - _ <: MaybeHasOrClause], + query: BaseQuery[M, _, _], mod: MongoModify ) { @@ -595,14 +467,7 @@ case class BaseModifyQuery[M]( // ******************************************************* case class BaseFindAndModifyQuery[M, R]( - query: BaseQuery[ - M, - R, - _ <: MaybeOrdered, - _ <: MaybeSelected, - _ <: MaybeLimited, - _ <: MaybeSkipped, - _ <: MaybeHasOrClause], + query: BaseQuery[M, R, _], mod: MongoModify ) { diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/QueryExecutor.scala b/rogue-core/src/main/scala/com/foursquare/rogue/QueryExecutor.scala index 6259f29..8f6693b 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/QueryExecutor.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/QueryExecutor.scala @@ -11,7 +11,7 @@ trait RogueSerializer[R] { def fromDBObject(dbo: DBObject): R } -trait QueryExecutor[MB] { +trait QueryExecutor[MB] extends Rogue { def adapter: MongoJavaDriverAdapter[MB] def optimizer: QueryOptimizer = new QueryOptimizer @@ -23,11 +23,7 @@ trait QueryExecutor[MB] { select: Option[MongoSelect[R]] ): RogueSerializer[R] - def count[M <: MB, Lim <: MaybeLimited, Sk <: MaybeSkipped]( - query: AbstractQuery[M, _, _, _, Lim, Sk, _] - )( - implicit ev1: Lim =:= Unlimited, ev2: Sk =:= Unskipped - ): Long = { + def count[M <: MB, State](query: AbstractQuery[M, _, State]): Long = { if (optimizer.isEmptyQuery(query)) { 0L } else { @@ -35,22 +31,8 @@ trait QueryExecutor[MB] { } } - def countDistinct[ - M <: MB, - Sel <: MaybeSelected, - Lim <: MaybeLimited, - Sk <: MaybeSkipped, - V - ]( - query: AbstractQuery[M, _, _, Sel, Lim, Sk, _] - )( - field: M => Field[V, M] - )( - implicit - // ev1: Sel =:= SelectedOne, - ev2: Lim =:= Unlimited, - ev3: Sk =:= Unskipped - ): Long = { + def countDistinct[M <: MB, State, V](query: AbstractQuery[M, _, State]) + (field: M => Field[V, M]): Long = { if (optimizer.isEmptyQuery(query)) { 0L } else { @@ -59,7 +41,7 @@ trait QueryExecutor[MB] { } def fetch[M <: MB, R]( - query: AbstractQuery[M, R, _, _, _, _, _], + query: AbstractQuery[M, R, _], readPreference: ReadPreference = defaultReadPreference ): List[R] = { if (optimizer.isEmptyQuery(query)) { @@ -72,15 +54,13 @@ trait QueryExecutor[MB] { } } - def fetchOne[M <: MB, R, Lim <: MaybeLimited]( - query: AbstractQuery[M, R, _, _, Lim, _, _], + def fetchOne[M <: MB, R]( + query: AbstractQuery[M, R, Unlimited], readPreference: ReadPreference = defaultReadPreference - )( - implicit ev1: Lim =:= Unlimited ): Option[R] = fetch(query.limit(1), readPreference).headOption def foreach[M <: MB, R]( - query: AbstractQuery[M, R, _, _, _, _, _], + query: AbstractQuery[M, R, _], readPreference: ReadPreference = defaultReadPreference )( f: R => Unit @@ -107,7 +87,7 @@ trait QueryExecutor[MB] { } def fetchBatch[M <: MB, R, T]( - query: AbstractQuery[M, R, _, _, _, _, _], + query: AbstractQuery[M, R, _], batchSize: Int, readPreference: ReadPreference = defaultReadPreference )( @@ -130,14 +110,9 @@ trait QueryExecutor[MB] { } } - def bulkDelete_!![M <: MB, Sel <: MaybeSelected, Lim <: MaybeLimited, Sk <: MaybeSkipped]( - query: AbstractQuery[M, _, _, Sel, Lim, Sk, _], + def bulkDelete_!![M <: MB]( + query: AbstractQuery[M, _, Unselected with Unlimited with Unskipped], writeConcern: WriteConcern = defaultWriteConcern - )( - implicit - ev1: Sel <:< Unselected, - ev2: Lim =:= Unlimited, - ev3: Sk =:= Unskipped ): Unit = { if (optimizer.isEmptyQuery(query)) { () @@ -206,7 +181,7 @@ trait QueryExecutor[MB] { } def findAndDeleteOne[M <: MB, R]( - query: AbstractQuery[M, R, _ <: MaybeOrdered, _ <: MaybeSelected, _ <: MaybeLimited, _ <: MaybeSkipped, _ <: MaybeHasOrClause], + query: AbstractQuery[M, R, _], writeConcern: WriteConcern = defaultWriteConcern ): Option[R] = { if (optimizer.isEmptyQuery(query)) { @@ -218,12 +193,12 @@ trait QueryExecutor[MB] { } } - def explain[M <: MB](query: AbstractQuery[M, _, _, _, _, _, _]): String = { + def explain[M <: MB](query: AbstractQuery[M, _, _]): String = { adapter.explain(query) } def iterate[S, M <: MB, R]( - query: AbstractQuery[M, R, _, _, _, _, _], + query: AbstractQuery[M, R, _], state: S )( handler: (S, Rogue.Iter.Event[R]) => Rogue.Iter.Command[S] @@ -237,7 +212,7 @@ trait QueryExecutor[MB] { } def iterateBatch[S, M <: MB, R]( - query: AbstractQuery[M, R, _, _, _, _, _], + query: AbstractQuery[M, R, _], batchSize: Int, state: S )( diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/QueryHelpers.scala b/rogue-core/src/main/scala/com/foursquare/rogue/QueryHelpers.scala index 4587c72..75a66f7 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/QueryHelpers.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/QueryHelpers.scala @@ -50,7 +50,7 @@ object QueryHelpers { trait QueryValidator { def validateList[T](xs: Traversable[T]): Unit def validateRadius(d: Degrees): Degrees - def validateQuery[M](query: BaseQuery[M, _, _, _, _, _, _]): Unit + def validateQuery[M](query: BaseQuery[M, _, _]): Unit def validateModify[M](modify: BaseModifyQuery[M]): Unit def validateFindAndModify[M, R](modify: BaseFindAndModifyQuery[M, R]): Unit } @@ -58,7 +58,7 @@ object QueryHelpers { class DefaultQueryValidator extends QueryValidator { override def validateList[T](xs: Traversable[T]) {} override def validateRadius(d: Degrees) = d - override def validateQuery[M](query: BaseQuery[M, _, _, _, _, _, _]) {} + override def validateQuery[M](query: BaseQuery[M, _, _]) {} override def validateModify[M](modify: BaseModifyQuery[M]) {} override def validateFindAndModify[M, R](modify: BaseFindAndModifyQuery[M, R]) {} } @@ -68,13 +68,13 @@ object QueryHelpers { var validator: QueryValidator = NoopQueryValidator trait QueryTransformer { - def transformQuery[M](query: BaseQuery[M, _, _, _, _, _, _]): BaseQuery[M, _, _, _, _, _, _] + def transformQuery[M](query: BaseQuery[M, _, _]): BaseQuery[M, _, _] def transformModify[M](modify: BaseModifyQuery[M]): BaseModifyQuery[M] def transformFindAndModify[M, R](modify: BaseFindAndModifyQuery[M, R]): BaseFindAndModifyQuery[M, R] } class DefaultQueryTransformer extends QueryTransformer { - override def transformQuery[M](query: BaseQuery[M, _, _, _, _, _, _]): BaseQuery[M, _, _, _, _, _, _] = { query } + override def transformQuery[M](query: BaseQuery[M, _, _]): BaseQuery[M, _, _] = { query } override def transformModify[M](modify: BaseModifyQuery[M]): BaseModifyQuery[M] = { modify } override def transformFindAndModify[M, R](modify: BaseFindAndModifyQuery[M, R]): BaseFindAndModifyQuery[M, R] = { modify } } @@ -140,10 +140,10 @@ object QueryHelpers { JObjectParser.parse(Extraction.decompose(x).asInstanceOf[JObject]) } - def orConditionFromQueries(subqueries: List[AbstractQuery[_, _, _, _, _, _, _]]) = { + def orConditionFromQueries(subqueries: List[AbstractQuery[_, _, _]]) = { MongoHelpers.OrCondition(subqueries.flatMap(subquery => { subquery match { - case q: BaseQuery[_, _, _, _, _, _, _] => Some(q.condition) + case q: BaseQuery[_, _, _] => Some(q.condition) case _ => None } })) diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/QueryOptimizer.scala b/rogue-core/src/main/scala/com/foursquare/rogue/QueryOptimizer.scala index 6c76d21..8de36e1 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/QueryOptimizer.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/QueryOptimizer.scala @@ -9,7 +9,7 @@ class QueryOptimizer { case _ => false } - def isEmptyQuery(query: BaseQuery[_, _, _, _, _, _, _]): Boolean = { + def isEmptyQuery(query: BaseQuery[_, _, _]): Boolean = { query.condition.clauses.exists(isEmptyClause) } diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/Rogue.scala b/rogue-core/src/main/scala/com/foursquare/rogue/Rogue.scala index ec10ef4..099170e 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/Rogue.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/Rogue.scala @@ -19,15 +19,28 @@ import org.bson.types.ObjectId *@see AbstractQuery for an example of the use of implicit conversions. */ trait Rogue { + + implicit def addOrder[Rest >: Sel with Lim with Sk with Or]: AddOrder[Rest with Unordered, Rest with Ordered] = null + implicit def addSelect[Rest >: Ord with Lim with Sk with Or]: AddSelect[Rest with Unselected, Rest with Selected, Rest with SelectedOne] = null + implicit def addLimit[Rest >: Ord with Sel with Sk with Or]: AddLimit[Rest with Unlimited, Rest with Limited] = null + implicit def addSkip[Rest >: Ord with Sel with Lim with Or]: AddSkip[Rest with Unskipped, Rest with Skipped] = null + implicit def addOr[Rest >: Ord with Sel with Lim with Sk]: AddOrClause[Rest with HasNoOrClause, Rest with HasOrClause] = null + + implicit def upcastQuery[M, R, S, T](q: AbstractQuery[M, R, S])(implicit ev: S <:< T): AbstractQuery[M, R, T] = + q.asInstanceOf[AbstractQuery[M, R, T]] + + type InitialState = Unordered with Unselected with Unlimited with Unskipped with HasNoOrClause + type OrderedState = Ordered with Unselected with Unlimited with Unskipped with HasNoOrClause + type Query[T] = - BaseQuery[T, T, Unordered, Unselected, Unlimited, Unskipped, HasNoOrClause] + BaseQuery[T, T, InitialState] type OrderedQuery[T] = - BaseQuery[T, T, Ordered, Unselected, Unlimited, Unskipped, HasNoOrClause] + BaseQuery[T, T, OrderedState] // type PaginatedQuery[T <: MongoRecord[T]] = BasePaginatedQuery[T, T] type ModifyQuery[T] = BaseModifyQuery[T] - type GenericQuery[M, R] = BaseQuery[M, R, _, _, _, _, _] + type GenericQuery[M, R] = BaseQuery[M, R, _] type GenericBaseQuery[M, R] = GenericQuery[M, R] object Asc extends IndexModifier(1) diff --git a/rogue-core/src/main/scala/com/foursquare/rogue/package.scala b/rogue-core/src/main/scala/com/foursquare/rogue/package.scala index 7a17092..da1cd27 100644 --- a/rogue-core/src/main/scala/com/foursquare/rogue/package.scala +++ b/rogue-core/src/main/scala/com/foursquare/rogue/package.scala @@ -3,15 +3,7 @@ package com.foursquare package object rogue { - type AbstractQuery[ - M, - R, - Ord <: MaybeOrdered, - Sel <: MaybeSelected, - Lim <: MaybeLimited, - Sk <: MaybeSkipped, - Or <: MaybeHasOrClause - ] = BaseQuery[M, R, Ord, Sel, Lim, Sk, Or] + type AbstractQuery[M, R, +State] = BaseQuery[M, R, State] type ModifyQuery[T] = BaseModifyQuery[T] type AbstractModifyQuery[M] = BaseModifyQuery[M] diff --git a/rogue-lift/src/main/scala/com/foursquare/rogue/ExecutableQuery.scala b/rogue-lift/src/main/scala/com/foursquare/rogue/ExecutableQuery.scala index 48a728f..6654460 100644 --- a/rogue-lift/src/main/scala/com/foursquare/rogue/ExecutableQuery.scala +++ b/rogue-lift/src/main/scala/com/foursquare/rogue/ExecutableQuery.scala @@ -4,19 +4,11 @@ package com.foursquare.rogue import com.foursquare.field.Field import com.foursquare.rogue.MongoHelpers.MongoSelect +import com.foursquare.rogue.Rogue._ import com.mongodb.WriteConcern -case class ExecutableQuery[ - MB, - M <: MB, - R, - Ord <: MaybeOrdered, - Sel <: MaybeSelected, - Lim <: MaybeLimited, - Sk <: MaybeSkipped, - Or <: MaybeHasOrClause -]( - query: AbstractQuery[M, R, Ord, Sel, Lim, Sk, Or], +case class ExecutableQuery[MB, M <: MB, R, State]( + query: AbstractQuery[M, R, State], db: QueryExecutor[MB] ) { @@ -24,22 +16,23 @@ case class ExecutableQuery[ * Gets the size of the query result. This should only be called on queries that do not * have limits or skips. */ - def count()(implicit ev1: Lim =:= Unlimited, ev2: Sk =:= Unskipped): Long = + def count(): Long = db.count(query) /** * Returns the number of distinct values returned by a query. The query must not have * limit or skip clauses. */ - def countDistinct[V](field: M => Field[V, _]) - (implicit ev1: Lim =:= Unlimited, ev2: Sk =:= Unskipped): Long = + def countDistinct[V](field: M => Field[V, _]): Long = db.countDistinct(query)(field.asInstanceOf[M => Field[V, M]]) /** * Checks if there are any records that match this query. */ - def exists()(implicit ev1: Lim =:= Unlimited, ev2: Sk =:= Unskipped): Boolean = - db.fetch(query.copy(select = Some(MongoSelect[Null](Nil, _ => null))).limit(1)).size > 0 + def exists()(implicit ev: State <:< Unlimited with Unskipped): Boolean = { + val q = query.copy(select = Some(MongoSelect[Null](Nil, _ => null))) + db.fetch(q.limit(1)).size > 0 + } /** * Executes a function on each record value returned by a query. @@ -61,8 +54,8 @@ case class ExecutableQuery[ * query must not have a limit clause. * @param limit the maximum number of records to return. */ - def fetch(limit: Int)(implicit ev: Lim =:= Unlimited): List[R] = - db.fetch(query.limit(limit)) + def fetch(limit: Int)(implicit ev: State <:< Unlimited): List[R] = + db.fetch(upcastQuery(query).limit(limit)) /** * fetch a batch of results, and execute a function on each element of the list. @@ -72,22 +65,23 @@ case class ExecutableQuery[ def fetchBatch[T](batchSize: Int)(f: List[R] => List[T]): List[T] = db.fetchBatch(query, batchSize)(f).toList + /** * Fetches the first record that matches the query. The query must not contain a "limited" clause. * @return an option record containing either the first result that matches the * query, or None if there are no records that match. */ - def get()(implicit ev: Lim =:= Unlimited): Option[R] = - db.fetchOne(query) + def get()(implicit ev: State <:< Unlimited): Option[R] = + db.fetchOne(upcastQuery(query)) /** * Fetches the records that match the query in paginated form. The query must not contain * a "limit" clause. * @param countPerPage the number of records to be contained in each page of the result. */ - def paginate(countPerPage: Int)(implicit ev1: Lim =:= Unlimited, ev2: Sk =:= Unskipped) = { - val q = query.asInstanceOf[AbstractQuery[M, R, Ord, Sel, Unlimited, Unskipped, Or]] - new BasePaginatedQuery[MB, M, R](q, db, countPerPage) + def paginate(countPerPage: Int) + (implicit ev: State <:< Unlimited with Unskipped) = { + new BasePaginatedQuery[MB, M, R](upcastQuery(query), db, countPerPage) } /** @@ -95,20 +89,17 @@ case class ExecutableQuery[ * "limit", or "select" clauses. Sends the delete operation to mongo, and returns - does * not wait for the delete to be finished. */ - def bulkDelete_!!!()(implicit ev1: Sel <:< Unselected, - ev2: Lim =:= Unlimited, - ev3: Sk =:= Unskipped): Unit = - db.bulkDelete_!!(query) + def bulkDelete_!!!()(implicit ev: State <:< Unselected with Unlimited with Unskipped): Unit = + db.bulkDelete_!!(upcastQuery(query)) /** * Delete all of the records that match the query. The query must not contain any "skip", * "limit", or "select" clauses. Sends the delete operation to mongo, and waits for the * delete operation to complete before returning to the caller. */ - def bulkDelete_!!(concern: WriteConcern)(implicit ev1: Sel <:< Unselected, - ev2: Lim =:= Unlimited, - ev3: Sk =:= Unskipped): Unit = - db.bulkDelete_!!(query, concern) + def bulkDelete_!!(concern: WriteConcern) + (implicit ev: State <:< Unselected with Unlimited with Unskipped): Unit = + db.bulkDelete_!!(upcastQuery(query), concern) /** * Finds the first record that matches the query (if any), fetches it, and then deletes it. @@ -164,7 +155,7 @@ case class ExecutableFindAndModifyQuery[MB, M <: MB, R]( } class BasePaginatedQuery[MB, M <: MB, R]( - q: AbstractQuery[M, R, _, _, Unlimited, Unskipped, _], + q: AbstractQuery[M, R, Unlimited with Unskipped], db: QueryExecutor[MB], val countPerPage: Int, val pageNum: Int = 1 @@ -177,7 +168,9 @@ class BasePaginatedQuery[MB, M <: MB, R]( lazy val countAll: Long = db.count(q) - def fetch(): List[R] = db.fetch(q.skip(countPerPage * (pageNum - 1)).limit(countPerPage)) + def fetch[S2, S3](): List[R] = { + db.fetch(q.skip(countPerPage * (pageNum - 1)).limit(countPerPage)) + } def numPages = math.ceil(countAll.toDouble / countPerPage.toDouble).toInt max 1 } diff --git a/rogue-lift/src/main/scala/com/foursquare/rogue/LiftRogue.scala b/rogue-lift/src/main/scala/com/foursquare/rogue/LiftRogue.scala index d650ccf..0688e13 100644 --- a/rogue-lift/src/main/scala/com/foursquare/rogue/LiftRogue.scala +++ b/rogue-lift/src/main/scala/com/foursquare/rogue/LiftRogue.scala @@ -19,13 +19,13 @@ import net.liftweb.record.field.EnumField trait LiftRogue extends Rogue { def OrQuery[M <: MongoRecord[M], R] - (subqueries: AbstractQuery[M, R, Unordered, Unselected, Unlimited, Unskipped, _]*) - : AbstractQuery[M, R, Unordered, Unselected, Unlimited, Unskipped, HasOrClause] = { + (subqueries: AbstractQuery[M, R, _]*) + : AbstractQuery[M, R, Unordered with Unselected with Unlimited with Unskipped with HasOrClause] = { subqueries.toList match { case Nil => throw new RogueException("No subqueries supplied to OrQuery", null) case q :: qs => { val orCondition = QueryHelpers.orConditionFromQueries(q :: qs) - BaseQuery[M, R, Unordered, Unselected, Unlimited, Unskipped, HasOrClause]( + BaseQuery[M, R, Unordered with Unselected with Unlimited with Unskipped with HasOrClause]( q.meta, q.collectionName, None, None, None, None, None, AndCondition(Nil, Some(orCondition)), None, None, None) } @@ -36,8 +36,8 @@ trait LiftRogue extends Rogue { * a QueryBuilder. This allows users to write queries as "QueryType where ...". */ implicit def metaRecordToQueryBuilder[M <: MongoRecord[M]] - (rec: M with MongoMetaRecord[M]): BaseQuery[M, M, Unordered, Unselected, Unlimited, Unskipped, HasNoOrClause] = - BaseQuery[M, M, Unordered, Unselected, Unlimited, Unskipped, HasNoOrClause]( + (rec: M with MongoMetaRecord[M]): BaseQuery[M, M, InitialState] = + BaseQuery[M, M, InitialState]( rec, rec.collectionName, None, None, None, None, None, AndCondition(Nil, None), None, None, None) implicit def metaRecordToModifyQuery[M <: MongoRecord[M]](rec: M with MongoMetaRecord[M]): AbstractModifyQuery[M] = @@ -51,9 +51,9 @@ trait LiftRogue extends Rogue { */ implicit def queryBuilderToModifyQuery[ M <: MongoRecord[M], - Or <: MaybeHasOrClause + State <: Unordered with Unselected with Unlimited with Unskipped ]( - query: AbstractQuery[M, M, Unordered, Unselected, Unlimited, Unskipped, Or] + query: AbstractQuery[M, M, State] ): AbstractModifyQuery[M] = { BaseModifyQuery[M](query, MongoModify(Nil)) } @@ -61,11 +61,9 @@ trait LiftRogue extends Rogue { implicit def queryBuilderToFindAndModifyQuery[ M <: MongoRecord[M], R, - Ord <: MaybeOrdered, - Sel <: MaybeSelected, - Or <: MaybeHasOrClause + State <: Unlimited with Unskipped ]( - query: AbstractQuery[M, R, Ord, Sel, Unlimited, Unskipped, Or] + query: AbstractQuery[M, R, State] ): AbstractFindAndModifyQuery[M, R] = { BaseFindAndModifyQuery[M, R](query, MongoModify(Nil)) } @@ -73,16 +71,12 @@ trait LiftRogue extends Rogue { implicit def queryToLiftQuery[ M <: MongoRecord[_], R, - Ord <: MaybeOrdered, - Sel <: MaybeSelected, - Lim <: MaybeLimited, - Sk <: MaybeSkipped, - Or <: MaybeHasOrClause + State ]( - query: BaseQuery[M, R, Ord, Sel, Lim, Sk, Or] - ): ExecutableQuery[MongoRecord[_] with MongoMetaRecord[_], M with MongoMetaRecord[_], R, Ord, Sel, Lim, Sk, Or] = { + query: BaseQuery[M, R, State] + ): ExecutableQuery[MongoRecord[_] with MongoMetaRecord[_], M with MongoMetaRecord[_], R, State] = { ExecutableQuery( - query.asInstanceOf[BaseQuery[M with MongoMetaRecord[_], R, Ord, Sel, Lim, Sk, Or]], + query.asInstanceOf[BaseQuery[M with MongoMetaRecord[_], R, State]], LiftQueryExecutor ) } @@ -107,7 +101,7 @@ trait LiftRogue extends Rogue { implicit def metaRecordToLiftQuery[M <: MongoRecord[M]]( rec: M with MongoMetaRecord[M] - ): ExecutableQuery[MongoRecord[_] with MongoMetaRecord[_], M with MongoMetaRecord[_], M, Unordered, Unselected, Unlimited, Unskipped, HasNoOrClause] = { + ): ExecutableQuery[MongoRecord[_] with MongoMetaRecord[_], M with MongoMetaRecord[_], M, InitialState] = { val queryBuilder = metaRecordToQueryBuilder(rec) val liftQuery = queryToLiftQuery(queryBuilder) liftQuery diff --git a/rogue-lift/src/test/scala/com/foursquare/rogue/QueryExecutorTest.scala b/rogue-lift/src/test/scala/com/foursquare/rogue/QueryExecutorTest.scala index 527301e..bf3db3a 100644 --- a/rogue-lift/src/test/scala/com/foursquare/rogue/QueryExecutorTest.scala +++ b/rogue-lift/src/test/scala/com/foursquare/rogue/QueryExecutorTest.scala @@ -17,7 +17,7 @@ class LegacyQueryExecutorTest extends SpecsMatchers { @Test def testExeptionInRunCommandIsDecorated { - val query = BaseQuery[Dummy.type, Dummy, Unordered, Unselected, Unlimited, Unskipped, HasNoOrClause]( + val query = BaseQuery[Dummy.type, Dummy, Rogue.InitialState]( Dummy, "Dummy", None, None, None, None, None, AndCondition(Nil, None), None, None, None) (LiftAdapter.runCommand("hello", query){ throw new RuntimeException("bang") diff --git a/rogue-lift/src/test/scala/com/foursquare/rogue/QueryTest.scala b/rogue-lift/src/test/scala/com/foursquare/rogue/QueryTest.scala index d10b6f4..ba0549c 100644 --- a/rogue-lift/src/test/scala/com/foursquare/rogue/QueryTest.scala +++ b/rogue-lift/src/test/scala/com/foursquare/rogue/QueryTest.scala @@ -525,7 +525,7 @@ class QueryTest extends SpecsMatchers { @Test def testSetReadPreference: Unit = { - type Q = BaseQuery[Venue, Venue, _, _, _, _, _] + type Q = BaseQuery[Venue, Venue, _] Venue.where(_.mayor eqs 2).asInstanceOf[Q].readPreference must_== None Venue.where(_.mayor eqs 2).setReadPreference(ReadPreference.SECONDARY).asInstanceOf[Q].readPreference must_== Some(ReadPreference.SECONDARY) @@ -602,16 +602,13 @@ class QueryTest extends SpecsMatchers { // // Phantom type stuff // + check("""Venue orderAsc(_.legacyid) orderAsc(_.closed)""") check("""Venue andAsc(_.legacyid)""") check("""Venue limit(1) limit(5)""") check("""Venue limit(1) fetch(5)""") check("""Venue limit(1) get()""") check("""Venue skip(3) skip(3)""") - check("""Venue limit(10) count()""") - check("""Venue skip(10) count()""") - check("""Venue limit(10) countDistinct(_.legacyid)""") - check("""Venue skip(10) countDistinct(_.legacyid)""") check("""Venue select(_.legacyid) select(_.closed)""") // select case class