Skip to content

Instantly share code, notes, and snippets.

@bwmcadams
Created November 6, 2011 21:46
Show Gist options
  • Save bwmcadams/1343568 to your computer and use it in GitHub Desktop.
Save bwmcadams/1343568 to your computer and use it in GitHub Desktop.
/* I'm trying to refactor $and and $or (which are essentially the same code returning a different outer query).
The problem is that currently only "String, Any" pairs work, and I need to be able to combine both those *AND* "Query Expression Objects".
Here's the $or spec right now
*/
"Casbah's DSL $or Operator" should {
"Accept multiple values" in {
val or = $or("foo" -> "bar", "x" -> "y")
or must haveListEntry("$or", Seq(MongoDBObject("foo" -> "bar"), MongoDBObject("x" -> "y")))
}
"Work with nested operators" in {
val or = $or( "foo" $lt 5 $gt 1, "x" $gte 10 $lte 152 )
or must haveSuperclass[BasicBSONList]
or must beEqualTo(MongoDBObject("$or" -> MongoDBList(MongoDBObject("foo" -> "bar", "x" -> "y"))))
}
}
/**
the second block doesn't compile or work and I need to figure out an idiom that works.
I've fiddled with a few things but Type Classes don't work due to needing a wildcard / <: on the second arg to the tuple pairs.
*/
/**
* Trait to provide the $or method as a bareword operator.
*
* $or ("Foo" -> "bar")
*
* Targets an RValue of (String, Any)* to be converted to a DBObject
*
* TODO - Test that rvalue ends up being an array e.g.:
*
* scala> $or ("foo" -> "bar", "X" -> 5)
* res1: com.mongodb.casbah.commons.Imports.DBObject = { "$or" : [ { "foo" : "bar" , "X" : 5}]}
*
*
* @author Brendan W. McAdams <brendan@10gen.com>
* @since 2.0
* @see http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24or
*/
trait OrOp extends BarewordQueryOperator {
def $or(fields: (String, Any)*) = {
val bldr = MongoDBList.newBuilder
for ((k, v) <- fields) bldr += MongoDBObject(k -> v)
MongoDBObject("$or" -> bldr.result)
}
}
/**
* Trait to provide the $or method as a bareword operator.
*
* $or ("Foo" -> "bar")
*
* Targets an RValue of (String, Any)* to be converted to a DBObject
*
* TODO - Test that rvalue ends up being an array e.g.:
*
* scala> $or ("foo" -> "bar", "X" -> 5)
* res1: com.mongodb.casbah.commons.Imports.DBObject = { "$or" : [ { "foo" : "bar" , "X" : 5}]}
*
*
* @author Brendan W. McAdams <brendan@10gen.com>
* @since 2.0
* @see http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24or
*/
trait OrOp extends BarewordQueryOperator {
def $or(fields: (String, Any)*) = {
val bldr = MongoDBList.newBuilder
for ((k, v) <- fields) bldr += MongoDBObject(k -> v)
MongoDBObject("$or" -> bldr.result)
}
}
trait OrOp extends BarewordQueryOperator {
def $or(fields: (String, Any)*) = {
val bldr = MongoDBList.newBuilder
for ((k, v) <- fields) bldr += MongoDBObject(k -> v)
MongoDBObject("$or" -> bldr.result)
}
def $or[A : ValidBarewordExpressionArgType](fields: A*) =
MongoDBObject("$or" -> implicitly[ValidBarewordExpressionArgType[A]].listify(fields))
}
trait ValidTypes {
trait KVPair extends ValidBarewordExpressionArgType[(String, Any)] {
def listify(args: Seq[(String, Any)]): Seq[DBObject] = {
val bldr = MongoDBList.newBuilder
args.foreach(kv => bldr += MongoDBObject(kv))
bldr.result.asInstanceOf[Seq[DBObject]]
}
}
// Valid Bareword Query Expression entries
trait CoreOperatorResultObj extends ValidBarewordExpressionArgType[DBObject with QueryExpressionObject] {
def listify(args: Seq[DBObject with QueryExpressionObject]): Seq[DBObject] = {
val bldr = MongoDBList.newBuilder
args.foreach(bldr.+=)
bldr.result.asInstanceOf[Seq[DBObject]]
}
}
}
trait ValidBarewordExpressionArgTypeHolder {
import com.mongodb.casbah.query.ValidTypes.{CoreOperatorResultObj, KVPair}
implicit object KVPairOk extends CoreOperatorResultObj
implicit object CoreOperatorResultObjOk extends CoreOperatorResultObj
}
/**
Compile Error:
[error] /Users/bwmcadams/code/mongodb/casbah/casbah-query/src/test/scala/BarewordOperatorsSpec.scala:79: could not find implicit value for evidence parameter of type com.mongodb.casbah.query.ValidBarewordExpressionArgType[(java.lang.String, java.lang.String)]
[error] val or = $or("foo" -> "bar", "x" -> "y")
[error] ^
*/
@djspiewak
Copy link

You can solve the problem listed in the comments of the first file by using a typeclass to represent the things that are composable via $or and friends. Provide one instance of the typeclass for (String, Any), another instance for "query expression objects".

Update: Just read your trailing comment. Thinking...

@bwmcadams
Copy link
Author

I tried the type class approach, but a TypeClass of (String, Any) won't allow say "a" -> "b" as it says it is looking for a typeclass of (String, String)

@djspiewak
Copy link

Try defining the typeclass in terms of (String, A) for some type parameter A.

@bwmcadams
Copy link
Author

@djspiewak - That won't work, as you can't give the implicit instance a wildcard arg... unless there's some syntax I'm missing?

See my updated "3rd" file.

@djspiewak
Copy link

Forked and updated with working syntax. You can parameterize an implicit value by using def rather than val or object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment