Permalink
Browse files

Merge branch 'compile-time-indexes' of https://github.com/nsanch/rogue

…into nsanch-compile-time-indexes

Conflicts:
	src/main/scala/com/foursquare/rogue/Query.scala
	src/main/scala/com/foursquare/rogue/QueryClause.scala
	src/main/scala/com/foursquare/rogue/QueryField.scala
	src/main/scala/com/foursquare/rogue/Rogue.scala
	src/test/scala/com/foursquare/rogue/QueryTest.scala
  • Loading branch information...
jliszka committed Jan 5, 2012
2 parents a5e69d6 + 7a4524b commit 7ecfb9a8473d5fbd79c0bec1aad3c35121058bf8
@@ -45,7 +45,7 @@ object MongoIndexChecker extends Loggable with MongoQueryTypes {
}
def normalizeCondition(condition: MongoHelpers.AndCondition): List[List[QueryClause[_]]] = {
- flattenCondition(condition).map(_.filter(_.expectedIndexBehavior != IndexBehavior.DocumentScan))
+ flattenCondition(condition).map(_.filter(_.expectedIndexBehavior != DocumentScan))
}
/**
@@ -88,20 +88,20 @@ object MongoIndexChecker extends Loggable with MongoQueryTypes {
*/
def validateIndexExpectations(query: GenericBaseQuery[_, _], indexes: List[MongoIndex[_]]): Boolean = {
val baseConditions = normalizeCondition(query.condition);
- val conditions = baseConditions.map(_.filter(_.expectedIndexBehavior != IndexBehavior.DocumentScan))
+ val conditions = baseConditions.map(_.filter(_.expectedIndexBehavior != DocumentScan))
conditions.forall(clauses => {
clauses.forall(clause => {
// DocumentScan expectations have been filtered out at this point.
// We just have to worry about expectations being more optimistic than actual.
val badExpectations = List(
- IndexBehavior.Index -> List(IndexBehavior.PartialIndexScan, IndexBehavior.IndexScan,IndexBehavior.DocumentScan),
- IndexBehavior.IndexScan -> List(IndexBehavior.DocumentScan)
+ Index -> List(PartialIndexScan, IndexScan, DocumentScan),
+ IndexScan -> List(DocumentScan)
)
badExpectations.forall{ case (expectation, badActual) => {
if (clause.expectedIndexBehavior == expectation &&
badActual.exists(_ == clause.actualIndexBehavior)) {
- signalError(
+ signalError(
"Query is expecting %s on %s but actual behavior is %s. query = %s" format
(clause.expectedIndexBehavior, clause.fieldName, clause.actualIndexBehavior, query.toString))
} else true
@@ -144,7 +144,7 @@ object MongoIndexChecker extends Loggable with MongoQueryTypes {
private def matchesUniqueIndex(clauses: List[QueryClause[_]]) = {
// Special case for overspecified queries matching on the _id field.
// TODO: Do the same for any overspecified query exactly matching a unique index.
- clauses.exists(clause => clause.fieldName == "_id" && clause.actualIndexBehavior == IndexBehavior.Index)
+ clauses.exists(clause => clause.fieldName == "_id" && clause.actualIndexBehavior == Index)
}
private def matchesIndex(index: List[String],
@@ -181,11 +181,11 @@ object MongoIndexChecker extends Loggable with MongoQueryTypes {
matchingClauses match {
case matchingClause :: _ => {
// If a previous field caused a scan, this field must scan too.
- val expectationOk = !scanning || matchingClause.expectedIndexBehavior == IndexBehavior.IndexScan
+ val expectationOk = !scanning || matchingClause.expectedIndexBehavior == IndexScan
// If this field causes a scan, later fields must scan too.
val nowScanning = scanning ||
- matchingClause.actualIndexBehavior == IndexBehavior.IndexScan ||
- matchingClause.actualIndexBehavior == IndexBehavior.PartialIndexScan
+ matchingClause.actualIndexBehavior == IndexScan ||
+ matchingClause.actualIndexBehavior == PartialIndexScan
expectationOk && matchesCompoundIndex(rest, remainingClauses, scanning = nowScanning)
}
case Nil => {
@@ -40,7 +40,7 @@ object MongoHelpers {
// and can be chained like { a : { $gt : 2, $lt: 6 }}.
// So if there is any equality clause, apply it (only) to the builder;
// otherwise, chain the clauses.
- cs.filter(_.isInstanceOf[EqClause[_]]).headOption match {
+ cs.filter(_.isInstanceOf[EqClause[_, _]]).headOption match {
case Some(eqClause) => eqClause.extend(builder, signature)
case None => {
builder.push(name)

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -4,104 +4,119 @@ package com.foursquare.rogue
import com.mongodb.DBObject
import com.mongodb.BasicDBObjectBuilder
+import net.liftweb.record.Field
-/**
- * An enumeration that defines the possible types of indexing that are supported by rogue
- * query clauses.
- */
-object IndexBehavior extends Enumeration {
- type IndexBehavior = Value
- val Index = Value("Index")
- val PartialIndexScan = Value("Partial index scan")
- val IndexScan = Value("Index scan")
- val DocumentScan = Value("Document scan")
-}
-
-/*
- * For each query clause, we define a class which computes the actual index behavior supported
- * by the underlying object type, and the index behavior expected by the query.
- */
-
-class QueryClause[V](val fieldName: String, conditions: (CondOps.Value, V)*) {
+class QueryClause[V](val fieldName: String, val actualIndexBehavior: MaybeIndexed, conditions: (CondOps.Value, V)*) {
def extend(q: BasicDBObjectBuilder, signature: Boolean): Unit = {
conditions foreach { case (op, v) => q.add(op.toString, if (signature) 0 else v) }
}
-
- lazy val actualIndexBehavior = conditions.head._1 match {
- case CondOps.All | CondOps.In => IndexBehavior.Index
- case CondOps.Gt | CondOps.GtEq | CondOps.Lt | CondOps.LtEq | CondOps.Ne | CondOps.Near =>
- IndexBehavior.PartialIndexScan
- case CondOps.Mod | CondOps.Type => IndexBehavior.IndexScan
- case CondOps.Exists | CondOps.Nin | CondOps.Size => IndexBehavior.DocumentScan
- }
-
- val expectedIndexBehavior = IndexBehavior.Index
-
- def withExpectedIndexBehavior(b: IndexBehavior.Value) = new QueryClause(fieldName, conditions: _*) {
+ val expectedIndexBehavior: MaybeIndexed = Index
+ def withExpectedIndexBehavior(b: MaybeIndexed) = new QueryClause(fieldName, actualIndexBehavior, conditions: _*) {
override val expectedIndexBehavior = b
}
}
-class RawQueryClause(f: BasicDBObjectBuilder => Unit) extends QueryClause("raw") {
+class IndexableQueryClause[V, Ind <: MaybeIndexed](fname: String, actualIB: Ind, conds: (CondOps.Value, V)*)
+ extends QueryClause[V](fname, actualIB, conds: _*)
+
+class AllQueryClause[V](fieldName: String, vs: java.util.List[V])
+ extends IndexableQueryClause[java.util.List[V], Index](fieldName, Index, CondOps.All -> vs)
+
+class InQueryClause[V](fieldName: String, vs: java.util.List[V])
+ extends IndexableQueryClause[java.util.List[V], Index](fieldName, Index, CondOps.In -> vs)
+
+class GtQueryClause[V](fieldName: String, v: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.Gt -> v)
+
+class GtEqQueryClause[V](fieldName: String, v: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.GtEq -> v)
+
+class LtQueryClause[V](fieldName: String, v: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.Lt -> v)
+
+class LtEqQueryClause[V](fieldName: String, v: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.LtEq -> v)
+
+class BetweenQueryClause[V](fieldName: String, lower: V, upper: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.GtEq -> lower, CondOps.LtEq -> upper)
+
+class StrictBetweenQueryClause[V](fieldName: String, lower: V, upper: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.Gt -> lower, CondOps.Lt -> upper)
+
+class NeQueryClause[V](fieldName: String, v: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.Ne -> v)
+
+class NearQueryClause[V](fieldName: String, v: V)
+ extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan, CondOps.Near -> v)
+
+class ModQueryClause[V](fieldName: String, v: java.util.List[V])
+ extends IndexableQueryClause[java.util.List[V], IndexScan](fieldName, IndexScan, CondOps.Mod -> v)
+
+class TypeQueryClause(fieldName: String, v: MongoType.Value)
+ extends IndexableQueryClause[Int, IndexScan](fieldName, IndexScan, CondOps.Type -> v.id)
+
+class ExistsQueryClause(fieldName: String, v: Boolean)
+ extends IndexableQueryClause[Boolean, DocumentScan](fieldName, DocumentScan, CondOps.Exists -> v)
+
+class NinQueryClause[V](fieldName: String, vs: java.util.List[V])
+ extends IndexableQueryClause[java.util.List[V], DocumentScan](fieldName, DocumentScan, CondOps.Nin -> vs)
+
+class SizeQueryClause(fieldName: String, v: Int)
+ extends IndexableQueryClause[Int, DocumentScan](fieldName, DocumentScan, CondOps.Size -> v)
+
+class RawQueryClause(f: BasicDBObjectBuilder => Unit) extends IndexableQueryClause("raw", DocumentScan) {
override def extend(q: BasicDBObjectBuilder, signature: Boolean): Unit = {
f(q)
}
-
- override lazy val actualIndexBehavior = IndexBehavior.DocumentScan
-
- override val expectedIndexBehavior = IndexBehavior.DocumentScan
+ override val expectedIndexBehavior = DocumentScan
}
-class EmptyQueryClause[V](fieldName: String) extends QueryClause[V](fieldName) {
+class EmptyQueryClause[V](fieldName: String) extends IndexableQueryClause[V, Index](fieldName, Index) {
override def extend(q: BasicDBObjectBuilder, signature: Boolean): Unit = {}
-
- override lazy val actualIndexBehavior = IndexBehavior.Index
-
- override def withExpectedIndexBehavior(b: IndexBehavior.Value) =
+ override def withExpectedIndexBehavior(b: MaybeIndexed) = {
new EmptyQueryClause[V](fieldName) {
override val expectedIndexBehavior = b
}
+ }
}
-class EqClause[V](fieldName: String, value: V) extends QueryClause[V](fieldName) {
+class EqClause[V, Ind <: MaybeIndexed](fieldName: String, actualIB: Ind, value: V) extends IndexableQueryClause[V, Ind](fieldName, actualIB) {
override def extend(q: BasicDBObjectBuilder, signature: Boolean): Unit = {
q.add(fieldName, if (signature) 0 else value)
}
+ override def withExpectedIndexBehavior(b: MaybeIndexed) = new EqClause[V, Ind](fieldName, actualIB, value) {
+ override val expectedIndexBehavior = b
+ }
+}
- override lazy val actualIndexBehavior = IndexBehavior.Index
-
- override def withExpectedIndexBehavior(b: IndexBehavior.Value) =
- new EqClause(fieldName, value) { override val expectedIndexBehavior = b }
+object EqClause {
+ def apply[V](fieldName: String, value: V) = {
+ new EqClause[V, Index](fieldName, Index, value)
+ }
}
-class WithinCircleClause[V](fieldName: String, lat: Double, lng: Double, radius: Double)
- extends QueryClause(fieldName) {
+class WithinCircleClause[V](fieldName: String, lat: Double, lng: Double, radius: Double) extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan) {
override def extend(q: BasicDBObjectBuilder, signature: Boolean): Unit = {
val value = if (signature) 0 else QueryHelpers.list(List(QueryHelpers.list(List(lat, lng)), radius))
q.push("$within").add("$center", value).pop
}
-
- override lazy val actualIndexBehavior = IndexBehavior.PartialIndexScan
-
- override def withExpectedIndexBehavior(b: IndexBehavior.Value) =
+ override def withExpectedIndexBehavior(b: MaybeIndexed) = {
new WithinCircleClause[V](fieldName, lat, lng, radius) {
override val expectedIndexBehavior = b
}
+ }
}
-class WithinBoxClause[V](fieldName: String, lat1: Double, lng1: Double, lat2: Double, lng2: Double)
- extends QueryClause(fieldName) {
+class WithinBoxClause[V](fieldName: String, lat1: Double, lng1: Double, lat2: Double, lng2: Double) extends IndexableQueryClause[V, PartialIndexScan](fieldName, PartialIndexScan) {
override def extend(q: BasicDBObjectBuilder, signature: Boolean): Unit = {
val value = if (signature) 0 else {
QueryHelpers.list(List(QueryHelpers.list(lat1, lng1), QueryHelpers.list(lat2, lng2)))
}
q.push("$within").add("$box", value).pop
}
-
- override lazy val actualIndexBehavior = IndexBehavior.PartialIndexScan
-
- override def withExpectedIndexBehavior(b: IndexBehavior.Value) =
- new WithinBoxClause[V](fieldName, lat1, lng1, lat2, lng2) { override val expectedIndexBehavior = b }
+ override def withExpectedIndexBehavior(b: MaybeIndexed) = new WithinBoxClause[V](fieldName, lat1, lng1, lat2, lng2) {
+ override val expectedIndexBehavior = b
+ }
}
class ModifyClause[V](val operator: ModOps.Value, fields: (String, V)*) {
Oops, something went wrong.

0 comments on commit 7ecfb9a

Please sign in to comment.