diff --git a/reactive-mongo/src/main/scala/Akka.scala b/reactive-mongo/src/main/scala/Akka.scala index eb09ac80..feae7c31 100644 --- a/reactive-mongo/src/main/scala/Akka.scala +++ b/reactive-mongo/src/main/scala/Akka.scala @@ -10,7 +10,7 @@ import com.typesafe.config.ConfigFactory import akka.actor.{ ActorRef, ActorSystem ⇒ AkkaSystem, Props } import reactivemongo.api.commands.GetLastError -import reactivemongo.bson.{ BSONArray, BSONDocument, BSONString } +import reactivemongo.bson.{ BSONArray, BSONDocument, BSONString, BSONValue } import reactivemongo.core.actors.{ Close, CheckedWriteRequestExpectingResponse ⇒ CheckedWriteRequestExResp, @@ -128,9 +128,17 @@ private[reactivemongo] class Actor(handler: ConnectionHandler) case Request(coln, SimpleBody(ps)) ⇒ { val qreq = new Request { val collection = coln - val body = ps.collectFirst { - case ("$query", q @ BSONDocument(_)) ⇒ q - }.fold(req.body)(List(_)) + + val body = ps.foldLeft(Option.empty[BSONDocument] → ( + List.empty[(String, BSONValue)] + )) { + case ((_, opts), ("$query", q @ BSONDocument(_))) ⇒ + Some(q) → opts + + case ((q, opts), opt) ⇒ q → (opts :+ opt) + } match { + case (q, opts) ⇒ q.toList :+ BSONDocument(opts) + } } Try(handler.queryHandler(cid, qreq)) match { diff --git a/reactive-mongo/src/main/scala/QueryResponse.scala b/reactive-mongo/src/main/scala/QueryResponse.scala index 78c05244..1a53ff77 100644 --- a/reactive-mongo/src/main/scala/QueryResponse.scala +++ b/reactive-mongo/src/main/scala/QueryResponse.scala @@ -33,6 +33,14 @@ object QueryResponse { */ def count(result: Int = 0) = apply(BSONDocument("ok" → 1, "n" → result)) + /** + * Prepares a response to a successful findAndModify command. + * + * @param result FindAndModify result + */ + def findAndModify(result: BSONDocument) = + apply(BSONDocument("ok" → 1, "value" → result)) + /** * Undefined response, returned by handler no supporting * a specific query that may be handled by others. diff --git a/reactive-mongo/src/main/scala/Request.scala b/reactive-mongo/src/main/scala/Request.scala index 9f5bbad3..22894bf7 100644 --- a/reactive-mongo/src/main/scala/Request.scala +++ b/reactive-mongo/src/main/scala/Request.scala @@ -171,6 +171,30 @@ object DeleteRequest { } } +/** FindAndModify request */ +object FindAndModifyRequest { + /** + * @return Collection name, query, update and then options + */ + def unapply(findAndModify: Request): Option[(String, List[(String, BSONValue)], List[(String, BSONValue)], List[(String, BSONValue)])] = findAndModify match { + case Request(_, SimpleBody(("findAndModify", BSONString(col)) :: ps)) ⇒ { + var q = List.empty[(String, BSONValue)] + var u = List.empty[(String, BSONValue)] + val o = List.newBuilder[(String, BSONValue)] + + ps.foreach { + case ("query", ValueDocument(query)) ⇒ q = query + case ("update", ValueDocument(update)) ⇒ u = update + case opt ⇒ o += opt + } + + Some((col, q, u, o.result())) + } + + case _ ⇒ None + } +} + /** * Extractor of properties for a document used a BSON value * (when operator is used, e.g. `{ 'age': { '\$gt': 10 } }`). diff --git a/reactive-mongo/src/test/scala/DriverSpec.scala b/reactive-mongo/src/test/scala/DriverSpec.scala index 48cf8571..b355f3b1 100644 --- a/reactive-mongo/src/test/scala/DriverSpec.scala +++ b/reactive-mongo/src/test/scala/DriverSpec.scala @@ -263,7 +263,8 @@ class DriverSpec extends org.specs2.mutable.Specification List(BSONDocument("doc" → 1), BSONDocument("doc" → 2.3d)) ) { d ⇒ AcolyteDSL.withFlatCollection(d, "anyCol") { - _.find(query1.body.head).cursor[BSONDocument]().collect[List]() + _.find(query1.body.head). + cursor[BSONDocument]().collect[List]() } } } aka "query result" must beLike[List[BSONDocument]] { @@ -310,6 +311,44 @@ class DriverSpec extends org.specs2.mutable.Specification }.map(_.isEmpty) aka "query result" must beTrue.await(0, timeout) } + "support query options" in { implicit ee: EE ⇒ + withFlatDriver { implicit drv: MongoDriver ⇒ + AcolyteDSL.withFlatQueryHandler({ + case Request("acolyte.test3", RequestBody( + List(("filter", BSONString("valC"))) :: List( + ("$orderby", ValueDocument(List(("foo", BSONInteger(1))))), + ("$readPreference", ValueDocument( + List(("mode", BSONString("primary"))))) + ) :: Nil)) ⇒ + QueryResponse(BSONDocument("lorem" → 1.2D)) + }) { con: MongoConnection ⇒ + AcolyteDSL.withFlatCollection(con, query3.collection) { + _.find(query3.body.head).sort(BSONDocument("foo" → 1)). + cursor[BSONDocument]().collect[List]() + } + } + }.map(_.size) aka "query result" must beEqualTo(1).await(0, timeout) + } + + "support findAndModify" in { implicit ee: EE ⇒ + withFlatDriver { implicit drv: MongoDriver ⇒ + AcolyteDSL.withFlatQueryHandler({ + case FindAndModifyRequest("test3", List(("id", BSONInteger(1))), + List(("title", BSONString("foo"))), opts) ⇒ + QueryResponse.findAndModify(BSONDocument(opts)) + }) { con: MongoConnection ⇒ + AcolyteDSL.withFlatCollection(con, query3.collection) { + _.findAndUpdate( + BSONDocument("id" → 1), + BSONDocument("title" → "foo") + ).map(_.value) + } + } + } aka "query result" must beSome(BSONDocument( + "upsert" → false, "new" → false + )).await(0, timeout) + } + "as error when connection handler is empty" in { implicit ee: EE ⇒ awaitRes(withFlatDriver { implicit drv: MongoDriver ⇒ AcolyteDSL.withFlatCollection(AcolyteDSL.handle, query3.collection) {