Permalink
Browse files

SCALA-68: Toying with syntax around $group ,but the current batch will

probably get ditched.
  • Loading branch information...
1 parent 525cd11 commit 126bc6f34fd03e2279b790acd7ee91bedfd235ca Brendan W. McAdams committed May 7, 2012
@@ -159,21 +159,49 @@ class ConversionsSpec extends CasbahMutableSpecification with BeforeExample {
// Note - exceptions are wrapped by Some() and won't be thrown until you .get
getConvertedDate.get must throwA[ClassCastException]
}
-/* "toString-ing a JODA Date with JODA Conversions loaded doesn't choke horribly." in {
- RegisterConversionHelpers()
- val jodaEntry: DBObject = MongoDBObject("type" -> "jdk",
- "date" -> jdkDate)
-
- /*jodaEntry.getAs[DateTime]("date") must beSome(jdkDate)
- // Casting it as something it isn't will fail
- lazy val getDate = { jodaEntry.getAs[JDKDate]("date") }
- // Note - exceptions are wrapped by Some() and won't be thrown until you .get
- getDate.get must throwA[ClassCastException] */
- RegisterJodaTimeConversionHelpers()
-
- val json = jodaEntry.toString
+ }
+
+ "Casbah and Java Driver custom type encoding" should {
+ val encoder = new com.mongodb.DefaultDBEncoder()
+ def encode(doc: DBObject): Long = {
+ val start = System.currentTimeMillis()
+ val encoded = encoder.encode(doc)
+ val end = System.currentTimeMillis()
+ end - start
+ }
+ DeregisterConversionHelpers()
+ DeregisterJodaTimeConversionHelpers()
- }*/
+ "Produce viable performance numbers to test off of " >> {
+ "Encoding DateTimes without any custom encoders registered " in {
+ var total = 0.0
+ val x = 10000
+ for (n <- 1 to x) {
+ val doc = MongoDBObject("date1" -> new JDKDate, "date2" -> new JDKDate, "foo" -> "bar", "x" -> 5.2)
+ total += encode(doc)
+ }
+
+ val avg = total / x
+
+ log.error("[Basic] Average encoding time over %s tries: %f [%s]", x, avg, total)
+ avg must beGreaterThan(0.0)
+ }
+ "Encoding Joda DateTimes with custom encoders registered " in {
+ RegisterJodaTimeConversionHelpers()
+ var total = 0.0
+ val x = 10000
+ for (n <- 1 to x) {
+ val doc = MongoDBObject("date1" -> DateTime.now, "date2" -> DateTime.now, "foo" -> "bar", "x" -> 5.2)
+ total += encode(doc)
+ }
+
+ val avg = total / x
+
+ log.error("[Custom Types] Average encoding time over %s tries: %f [%s]", x, avg, total)
+ avg must beGreaterThan(0.0)
+ }
+ }
}
+
}
// vim: set ts=2 sw=2 sts=2 et:
@@ -1,7 +1,5 @@
-package com.mongodb.casbah.query
-
/**
- * Copyright (c) 2008 - 2012 10gen, Inc. <http://10gen.com>
+ * Copyright (c) 2010 - 2012 10gen, Inc. <http://10gen.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +13,254 @@ package com.mongodb.casbah.query
* See the License for the specific language governing permissions and
* limitations under the License.
*
+ * For questions and comments about this product, please see the project page at:
+ *
+ * http://github.com/mongodb/casbah
+ *
*/
+package com.mongodb.casbah.query.dsl
+
+import com.mongodb.casbah.util.Logging
+
+import scalaj.collection.Imports._
+
+import com.mongodb.casbah.query._
+
+import scala.util.matching._
+import scala.collection.Iterable
+
+import org.bson._
+import org.bson.types.BasicBSONList
+
+object AggregationFramework {}
+
+/**
+ * Base trait for a Pipeline Operator for
+ * the Aggregation Framework.
+ * These operators are the "core" of Aggregation,
+ * representing the primary pipeline.
+ */
+trait PipelineOperator {
+
+ //protected def op(oper: String, target: Any): Map[String, Any]
+}
+
+/**
+ * Base trait for expressions in the Pipeline
+ */
+trait PipelineExpression {
+}
+
+
+// TODO - Validations of things like "ran group after sort" for certain opers
+trait PipelineOperations extends GroupOperator
+ with LimitOperator
+ with SkipOperator
+ with MatchOperator
+ with ProjectOperator
+ with SortOperator
+ with UnwindOperator
+
+trait LimitOperator extends PipelineOperator {
+ private val operator = "$limit"
+
+ // TODO - Accept Numeric? As long as we can downconvert for mongo type?
+ def $limit(target: Int) = {
+ val obj = new BasicDBObject with PipelineOperations
+ obj put (operator, target)
+ obj
+ }
+
+ def $limit(target: BigInt) = {
+ val obj = new BasicDBObject with PipelineOperations
+ obj put (operator, target)
+ obj
+ }
+
+}
+
+trait SkipOperator extends PipelineOperator {
+ private val operator = "$skip"
+
+ // TODO - Accept Numeric? As long as we can downconvert for mongo type?
+ def $skip(target: Int) = {
+ val obj = new BasicDBObject with PipelineOperations
+ obj put (operator, target)
+ obj
+ }
+ def $skip(target: BigInt) = {
+ val obj = new BasicDBObject with PipelineOperations
+ obj put (operator, target)
+ obj
+ }
+
+}
+
+trait MatchOperator extends PipelineOperator {
+ private val operator = "$match"
+
+ def $match(query: DBObject) =
+ MongoDBObject(operator -> query)
+}
+
+trait SortOperator extends PipelineOperator {
+ private val operator = "$sort"
+
+ def $sort(fields: (String, Int)*) = {
+ val bldr = MongoDBObject.newBuilder
+ for ((k, v) <- fields) bldr += k -> v
+ MongoDBObject(operator -> bldr.result)
+ }
+}
+
+trait UnwindOperator extends PipelineOperator {
+ private val operator = "$unwind"
+
+ def $unwind(target: String) = {
+ require(target.startsWith("$"), "The $unwind operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_unwind")
+ MongoDBObject("$unwind" -> target)
+ }
+}
+
+trait ProjectOperator extends PipelineOperator {
+ private val operator = "$project"
+}
+
+
+trait GroupOperatorBase extends PipelineOperator {
+
+ /** composable with suboperators */
+ protected def _group(field: String) = {
+ new {
+ protected def op(target: Any) =
+ MongoDBObject("$group" -> MongoDBObject(field -> target))
+
+ /**
+ * Returns an array of all the values found in the selected field among
+ * the documents in that group. Every unique value only appears once
+ * in the result set.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $addToSet(target: String) = {
+ require(target.startsWith("$"), "The $group.$addToSet operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$addToSet" -> target))
+ }
+
+ /**
+ * Returns the first value it sees for its group.
+ *
+ * Note Only use $first when the $group follows an $sort operation.
+ * Otherwise, the result of this operation is unpredictable.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $first(target: String) = {
+ require(target.startsWith("$"), "The $group.$first operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$first" -> target))
+ }
+
+ /**
+ * Returns the last value it sees for its group.
+ *
+ * Note Only use $last when the $group follows an $sort operation.
+ * Otherwise, the result of this operation is unpredictable.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $last(target: String) = {
+ require(target.startsWith("$"), "The $group.$last operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$last" -> target))
+ }
+
+ /**
+ * Returns the highest value among all values of the field in all documents selected by this group.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $max(target: String) = {
+ require(target.startsWith("$"), "The $group.$max operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$max" -> target))
+ }
+
+ /**
+ * Returns the lowest value among all values of the field in all documents selected by this group.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $min(target: String) = {
+ require(target.startsWith("$"), "The $group.$min operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$min" -> target))
+ }
+
+ /**
+ * Returns the average of all values of the field in all documents selected by this group.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $avg(target: String) = {
+ require(target.startsWith("$"), "The $group.$avg operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$avg" -> target))
+ }
+
+ /**
+ * Returns an array of all the values found in the selected field among
+ * the documents in that group. A value may appear more than once in the
+ * result set if more than one field in the grouped documents has that value.
+ *
+ * RValue should be $&lt;documentFieldName&gt;
+ */
+ def $push(target: String) = {
+ require(target.startsWith("$"), "The $group.$push operator only accepts a $<fieldName> argument; bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$push" -> target))
+ }
+
+ /**
+ * Returns the sum of all the values for a specified field in the
+ * grouped documents, as in the second use above.
+ *
+ * The standard usage is to indicate "1" as the value, which counts all the
+ * members in the group.
+ *
+ * Alternately, if you specify a field value as an argument, $sum will
+ * increment this field by the specified value for every document in the
+ * grouping.
+ *
+ */
+ def $sum(target: String) = {
+ require(target.startsWith("$"), "The $group.$sum operator only accepts a $<fieldName> argument (or '1'); bare field names will not function. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$sum" -> target))
+ }
+
+ /**
+ * Returns the sum of all the values for a specified field in the
+ * grouped documents, as in the second use above.
+ *
+ * The standard usage is to indicate "1" as the value, which counts all the
+ * members in the group.
+ *
+ * Alternately, if you specify a field value as an argument, $sum will
+ * increment this field by the specified value for every document in the
+ * grouping.
+ *
+ */
+ def $sum(target: Int) = {
+ require(target == 1, "The $group.$sum operator only accepts a numeric argument of '1', or a $<FieldName>. See http://docs.mongodb.org/manual/reference/aggregation/#_S_group")
+ op(MongoDBObject("$sum" -> target))
+ }
+
+ }
+ }
+}
+
+// TODO
+
+// *REQUIRES* an _id field to be defined...
+// " for _id, it can be a single field reference, a document containing several field references, or a constant of any type."
+trait GroupOperator extends GroupOperatorBase {
+ private val operator = "$group"
-class AggregationFramework {
+ def $group(field: String) = _group(field)
+}
-}
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010, 2011 10gen, Inc. <http://10gen.com>
+ * Copyright (c) 2010 - 2012 10gen, Inc. <http://10gen.com>
* Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -309,6 +309,8 @@ trait ModuloOp extends QueryOperator {
trait SizeOp extends QueryOperator {
private val oper = "$size"
+
+ // TODO - Accept Numeric? As long as we can downconvert for mongo type?
def $size(target: Int) = op(oper, target)
def $size(target: BigInt) = op(oper, target)
}
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010 10gen, Inc. <http://10gen.com>
+ * Copyright (c) 2010 - 2012 10gen, Inc. <http://10gen.com>
* Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -68,6 +68,8 @@ trait Implicits {
}
}
+ def | = new dsl.PipelineOperations {}
+
implicit def tupleToGeoCoords[A: ValidNumericType: Manifest, B: ValidNumericType: Manifest](coords: (A, B)) = dsl.GeoCoords(coords._1, coords._2)
}
@@ -61,6 +61,20 @@ class AggregationFrameworkSpec extends CasbahMutableSpecification {
_match must haveEntries("$match.score.$gt" -> 50, "$match.score.$lte" -> 90, "$match.type.$in" -> List("exam", "quiz"))
}
}
+
+ "Aggregation's Group Operator" should {
+ "Work with field operators" in {
+ "Allow $first" >> {
+ val _group = | $group ("firstAuthor") $first("$author")
+ _group must not beNull
+ }
+ }
+
+ /*"Require _id" in {
+ null must beNull
+ }
+*/
+ }
}
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010, 2011 10gen, Inc. <http://10gen.com>
+ * Copyright (c) 2010 - 2012 10gen, Inc. <http://10gen.com>
* Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");

0 comments on commit 126bc6f

Please sign in to comment.