Last active
December 6, 2016 07:19
-
-
Save mariussoutier/5192209 to your computer and use it in GitHub Desktop.
ReactiveMongo Play Plugin Extensions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package json | |
import reactivemongo.bson._ | |
import reactivemongo.bson.handlers.DefaultBSONHandlers._ | |
import play.api.libs.json._ | |
import play.api.libs.json.Json._ | |
import play.api.libs.json.util._ | |
import play.api.libs.json.Writes._ | |
import play.api.libs.functional.syntax._ | |
object Formats { | |
val objectIDRegExFormat = "^[0-9a-fA-F]{24}$".r | |
def isObjectIDValid(input: String): Boolean = (objectIDRegExFormat findFirstIn input).nonEmpty | |
implicit object ObjectIdReads extends Format[BSONObjectID] { | |
def reads(json: JsValue): JsResult[BSONObjectID] = json.asOpt[JsObject] map { oid => | |
(oid \ "$oid" ).asOpt[String] map { str => | |
if (isObjectIDValid(str)) | |
JsSuccess(new BSONObjectID(str)) | |
else | |
JsError("Invalid ObjectId %s".format(str)) | |
} getOrElse (JsError("Value is not an ObjectId")) | |
} getOrElse (JsError("Value is not an ObjectId")) | |
def writes(oid: BSONObjectID): JsValue = Json.obj("$oid" -> JsString(oid.stringify)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package models.mongo | |
import scala.concurrent.{Future, ExecutionContext} | |
// Reactive Mongo imports | |
import reactivemongo.api._ | |
import reactivemongo.bson._ | |
import reactivemongo.bson.handlers.DefaultBSONHandlers._ | |
import reactivemongo.core.commands._ | |
// Reactive Mongo plugin | |
import play.modules.reactivemongo._ | |
import play.modules.reactivemongo.PlayBsonImplicits._ | |
// Play Json imports | |
import play.api.libs.json._ | |
import json.Formats._ | |
trait MongoModel[T, ID] { | |
val collection: Collection | |
implicit val ec: ExecutionContext | |
/** | |
* Find all items for the given query. | |
* Implicit JsValue -> T must be in scope | |
*/ | |
def find[T](q: QueryBuilder)(implicit reader: Reads[T]): Future[List[T]] = { | |
val jsonValuesFuture: Future[List[JsValue]] = collection.find[JsValue](q).toList | |
// Transform future of JSON values to future of T, but only keep the successfully parsed ones | |
jsonValuesFuture map { jsonValues => | |
jsonValues map { json => | |
Json.fromJson[T](json) // JsResult | |
} collect { case JsSuccess(transaction, _) => transaction } | |
} | |
} | |
/** | |
* Find all items for the given JsValue query. | |
* Implicit JsValue -> T must be in scope | |
*/ | |
def find[T](q: JsValue)(implicit reader: Reads[T]): Future[List[T]] = find[T](QueryBuilder().query(q)) | |
/** | |
* Find one item and maybe return it. | |
* Implicit JsValue -> T must be in scope | |
*/ | |
def findOne(q: JsValue)(implicit reader: Reads[T]): Future[Option[T]] = { | |
val res: Future[Option[JsValue]] = collection.find[JsValue](QueryBuilder().query(q)).headOption | |
res map { jsValueOpt => | |
jsValueOpt map { value => | |
Json.fromJson[T](value).asOpt // JsResult => Option || .getOrElse directly? | |
} getOrElse (None) | |
} | |
} | |
def insert(t: T)(implicit writer: Writes[T]): Future[LastError] = | |
collection.insert(Json.toJson(t)) | |
/* | |
* Writes an updated version of a model class to the database. | |
*/ | |
//TODO def update(t: T)(implicit writer: Writes[T]): Future[LastError] = | |
// collection.update() | |
def removeById(id: ID)(implicit writer: Writes[ID]): Future[LastError] = | |
collection.remove(Json.obj("_id" -> id)) | |
// -- Convenience | |
/** | |
* Find one item by its _id and maybe return it. | |
* Implicit JsValue -> T must be in scope | |
* Implicit ID -> JsValue must be in scope | |
*/ | |
def findOneById(id: ID)(implicit reader: Reads[T], writer: Writes[ID]): Future[Option[T]] = | |
findOne(Json.obj("_id" -> id)) | |
def count(q: Option[BSONDocument] = None): Future[Int] = | |
collection.db.command(Count(collectionName = collection.name, query = q)) | |
def all(implicit reader: Reads[T]): Future[List[T]] = | |
find[T](QueryBuilder().query(BSONDocument())) | |
// For performance reasons, this is not implemented in terms of findOne, but find().limit() | |
def exists: Future[Boolean] = | |
collection.find[JsValue, JsValue](Json.obj()).headOption.map(_.isDefined) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package util | |
import play.api.mvc._ | |
import reactivemongo.bson._ | |
/** | |
* In Build.scala, add the following: | |
* routesImport += "util.RouteFormats._" | |
*/ | |
object RouteFormats { | |
type BSONObjectID = reactivemongo.bson.BSONObjectID | |
val objectIDRegExFormat = "^[0-9a-fA-F]{24}$".r | |
def isObjectIDValid(input: String): Boolean = (objectIDRegExFormat findFirstIn input).nonEmpty | |
implicit object BSONObjectIDQueryStringBindable extends QueryStringBindable[BSONObjectID] { | |
def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, BSONObjectID]] = | |
params.get(key).flatMap(_.headOption).map { _ match { | |
case str: String if (isObjectIDValid(str)) => Right(BSONObjectID(str)) | |
case _ => Left("Cannot parse parameter " + key + " as BSONObjectID") | |
} | |
} | |
def unbind(key: String, value: BSONObjectID): String = key + "=" + value.stringify | |
} | |
/* Allows to use BSONObjectID as a path variable */ | |
implicit object BSONObjectIDPathBindable extends PathBindable[BSONObjectID] { | |
def bind(key: String, value: String) = value match { | |
case str: String if (isObjectIDValid(str)) => Right(BSONObjectID(str)) | |
case _ => Left("Cannot parse parameter " + key + " as BSONObjectID") | |
} | |
def unbind(key: String, value: BSONObjectID): String = value.stringify | |
} | |
implicit def BSONObjectIDJavascriptLitteral = new JavascriptLitteral[BSONObjectID] { | |
def to(value: BSONObjectID) = value.stringify | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Really helpful! Thank you!