2017-04-16 53 views
-1

我正在写一个Action in Play应该在MongoDB中添加一个文档,如果它不存在的话。我尝试了两种方法,但都没有工作。我卡住周围如何处理未来响应mongoplugin使用Play添加文档/ reactivemongoplugin

行动获取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部分

情况下,S:JsSuccess [用户2] => {

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和期货

+0

它看起来不是特定于ReactiveMongo,而是异步结果。 – cchantep

+0

你是对的。我该如何解决这个问题? –

+0

看来你的问题是将未来[未来[T]]转变为未来[T]。为此,在定义'insertFuture'时,应该使用'flatMap'而不是'map'。 –

回答

0

flatMap工作。谢谢你Cyrille Corpet和cchantep

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 

     val insertFuture: Future[Future[LastError]] = findFuture.map(option => option match { 
      case None => { 
      //no record. Can add 
      Logger.debug("No record from mongo: Can add") 
      //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 Some(x) => { 
      //duplicae record 
      Logger.debug("error from Mongo. Duplicate:" + x) 
      //2 
      Future.successful({ 
       Logger.debug("findFutureResultmap. Not adding a duplicate") 
       LastError(false, None, None, None, None, 0, false) 
      }) 

      } 
     }) 

     insertFuture.flatMap(lef => { 
      lef.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)) 
      } 
      }) 

      //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)) 
     }) 
    } 
    } 
    }