【发布时间】:2017-09-12 11:21:58
【问题描述】:
我正在 Play 中编写一个 Action,如果 MongoDB 中不存在 Document,它应该添加一个 Document。我尝试了两种方法,但都没有奏效。我被困在如何处理由响应式mongoplugin返回的未来
Action 获取 JSON 数据。它验证了它。如果 JSON 没问题,我会检查用户是否存在(查看名字)。如果没有,我想添加它,否则返回错误。我无法在同一个操作中结合 ReactiveMongoPlugin 的“查找”和“插入”方法
方法 1** - 这不会编译,因为代码返回 scala.concurrent.Future[play.api.mvc.SimpleResult] 而它需要 play.api.mvc.SimpleResult。我知道这是因为我在地图中使用地图。
def registrationRequest = Action.async(parse.json) { request => {
Logger.debug("received message:" + request)
Logger.debug("received message:" + request.body)
val jr: JsResult[User2] = request.body.validate[User2]
Logger.debug("jr is " + jr)
jr match {
case s: JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
val query = BSONDocument("user.firstname" -> user.firstName)
Logger.debug("query is:" + query)
//result is of type Future[Option[BSONDocument]]
val findFuture:Future[Option[BSONDocument]] = collection.find(query).one
findFuture.map(option => option match {
case None => {
//no record. Can add
Logger.debug("No record from mongo: Can add")
val newUserDoc = BSONDocument (
"id" -> user.id,
"user" -> BSONDocument (
"firstname" -> user.firstName,
"lastname" -> user.lastName,
"email" -> BSONArray (user.email (0) ),
"password" -> user.password,
"address" -> BSONDocument (
"street" -> user.address.street,
"country" -> user.address.country
)
)
)
//addResult is of type Future[LastError]
//this code is problamatic. I am calling a map within a map which creates a Future[Future[Result]]. I need only Future[Result]
val insertResult = collection.insert (newUserDoc)
insertResult.map(le=>{
if(le.ok) {
Logger.debug("insertFuture map")
val ack = Acknowledgment(0, "insert success: ")
Logger.debug("insert success:" + Json.toJson(ack))
Ok(Json.toJson(ack))
}else {
Logger.debug("not inserting")
val ack = Acknowledgment (0, "duplicate: ")
Logger.debug ("fail ack:" + Json.toJson (ack) )
BadRequest (Json.toJson (ack) )
}
})}
case Some(x) => {
//duplicae record
Logger.debug("error from Mongo. Duplicate:" + x)
val ack = Acknowledgment(0, "duplicate: " + x.toString())
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
})
//findFutureResult is a Future[Int]
case f: JsError => Future.successful({
Logger.debug("error: " + JsError.toFlatJson(f))
val ack = Acknowledgment(0, JsError.toFlatJson(f).toString())
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
})
}
}
}
方法 2 - 在这种方法中,我打破了避免在地图中调用地图的步骤。以下代码用于 JsSuccess 部分
案例:JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
val query = BSONDocument("user.firstname" -> user.firstName)
Logger.debug("query is:" + query)
//result is of type Future[Option[BSONDocument]]
val findFuture:Future[Option[BSONDocument]] = collection.find(query).one
//findFutureResult is a Future[Int]
//to avoid calling map within map, I am creating single Futures which would convey result of one Future to another.
val findFutureResult:Future[Int] = findFuture.map(option => option match {
case None => {
//no record. Can add
Logger.debug("No record from mongo: Can add")
1 //return of 1 means record can be added
}
case Some(x) => {
//duplicae record
Logger.debug("error from Mongo. Duplicate:" + x)
2 //return of 2 means record cannot be added.
}
})
//this code would return LastError. the value of LastError can be used to check if insert was performed or not. Accordingly, I want to send OK or BadRequest
val insertFuture:Future[Future[LastError]] = findFutureResult.map(r => {r match {
case 1 => {
Logger.debug("findFutureResult map. Adding doc")
val newUserDoc = BSONDocument (
"id" -> user.id,
"user" -> BSONDocument (
"firstname" -> user.firstName,
"lastname" -> user.lastName,
"email" -> BSONArray (user.email (0) ),
"password" -> user.password,
"address" -> BSONDocument (
"street" -> user.address.street,
"country" -> user.address.country
)
)
)
//addResult is of type Future[LastError]
collection.insert (newUserDoc)
}
case 2 => Future.successful({
Logger.debug("findFutureResultmap. Not adding a duplicate")
LastError(false,None, None, None, None, 0,false )
})
}
})
//this is the problematic code. How do i check value of LastError? insertFuture is Future[Future[LastError]] and not Future[LastError]
insertFuture.map(lef=>{ lef.map(le=>{ // I cannot call map inside map as explained in approach 1
if(le.ok) {
Logger.debug("insertFuture map")
val ack = Acknowledgment(0, "insert success: ")
Logger.debug("insert success:" + Json.toJson(ack))
Ok(Json.toJson(ack))
}
else {
Logger.debug("not inserting")
val ack = Acknowledgment (0, "duplicate: ")
Logger.debug ("fail ack:" + Json.toJson (ack) )
BadRequest (Json.toJson (ack) )
}
})})
}
}
我知道代码中的问题所在。我不知道如何解决它。我认为我的方法还不错 - 我想在插入数据库之前检查数据库,但我无法将其安装在响应式mongo apis 和 Futures 周围
【问题讨论】:
-
它看起来并不是 ReactiveMongo 特有的,而是异步结果通用的。
-
你是对的。我该如何解决这个问题?
-
看来您的问题是将
Future[Future[T]]转换为Future[T]。为此,您应该在定义insertFuture时使用flatMap而不是map。 -
或
.flatMap更早 -
@ManuChadha 请尝试将问题中的代码示例减少到重现问题的最小情况。
标签: scala playframework future