【问题标题】:A ReactiveMongo pattern to return the created mongodb document in one RESTful request一种 ReactiveMongo 模式,用于在一个 RESTful 请求中返回创建的 mongodb 文档
【发布时间】:2013-11-08 13:01:34
【问题描述】:

环境:玩! 2.2.3、ReactiveMongo 0.10.0-SNAPSHOT

假设我有一个包含文档列表的页面(比如说“项目”)和一个按钮,该按钮会弹出一个模式对话框,其中包含要填写的字段。按下 OK 按钮后,页面会发送一个带有 JSON 正文的请求到后端:

{
    name: "Awesome Project", 
    url: "https://github.com/ab/cd", 
    repository: "git@github.com/ab/cd.git", 
    script: "empty"
}

后端将请求路由到Action,定义如下:

  def projectsCollection: JSONCollection = db.collection[JSONCollection]("projects")

  def create = Action.async(parse.json) { request =>
    projectsCollection.insert(request.body) map {
      case LastError(true,_,_,_,Some(doc),_,_) =>  Created(JsObject(List(
        "result" -> JsString("OK") ,
        "doc" -> BSONFormats.toJSON(doc)
      )))
      case LastError(false, err, code, msg, _, _, _) => NotAcceptable(JsObject(List(
        "result" -> JsString("ERROR"),
        "error" -> JsString(err.getOrElse("unknown")),
        "code" -> JsNumber(code.getOrElse[Int](0)),
        "msg" -> JsString(msg.getOrElse("no messsage"))
      )))
    }
  }

LastError 案例类有一个参数originalDocument: Option[BSONDocument],它在请求响应正文中返回,但它不是我期望的文档。我想要填充了BSONObjectID 或至少_id 本身的文档。

试图检索新创建的文档导致我走入了死胡同,因为所有内容都包含在 Future 中。

如何编写完成任务的优雅代码?

【问题讨论】:

  • 出现错误需要访问_id吗?
  • @sh1ng 我没有考虑过,因为这种情况是为了创建一个新条目,所以如果出现错误,_id 无论如何都不存在,但是是的,拥有_id失败的更新或删除可能很有用。

标签: mongodb scala playframework future reactivemongo


【解决方案1】:

一种可能的解决方案是自己生成_id 字段并将其与请求正文连接:

val json = Json.obj("_id" -> BSONFormats.toJSON(BSONObjectID.generate)) ++ request.body.as[JsObject]

然后将json的值作为insert参数,放入成功的结果体中。

projectsCollection.insert(json) map {
  case LastError(true,_,_,_,Some(doc),_,_) =>  Created(JsObject(List(
    "result" -> JsString("OK") ,
    "doc" -> BSONFormats.toJSON(doc),
    "project" -> json
  )))

【讨论】:

    【解决方案2】:

    我是 Play、scala 和 reactivemongo 的新手,所以如果我的回答很难看,请原谅我的回答。但是,我确实得到了一个插入,然后开始工作:

    def createUser = Action.async(parse.json) { request =>
    /*
     * request.body is a JsValue.
     * There is an implicit Writes that turns this JsValue as a JsObject,
     * so you can call insert() with this JsValue.
     * (insert() takes a JsObject as parameter, or anything that can be
     * turned into a JsObject using a Writes.)
     */
    val userResult = request.body.validate[User]
    userResult.fold(
    errors => {
        Future.successful(
        Result(
            header = ResponseHeader(400, Map(CONTENT_TYPE->"application/json")),
            body = Enumerator(Json.obj("status" ->"KO", "message" -> JsError.toFlatJson(errors)).toString.getBytes())
            ))
    },
    user => blocking{ 
      // `user` is an instance of the case class `models.User`
      val insertDate = new DateTime()
      val newUser = user.copy(created_at = Some(insertDate) )
    
      collection.insert(newUser).map { lastError => 
        Logger.debug(s"Successfully inserted with LastError: $lastError")
        val cursor = collection.find(newUser).cursor[User]
    
         // gather all the JsObjects in a list
    val futureUsersList: Future[List[User]] = cursor.collect[List]()
    
    
    // transform the list into a JsArray
    val futureUsersJsonArray: Future[JsArray] = futureUsersList.map { persons =>
      Json.arr(persons)
    }
    
    // everything's ok! Let's reply with the array
    val res = futureUsersJsonArray.map { persons =>
    
        Created(persons)
      } 
       Await.result(res, Duration(1000, MILLISECONDS))
    }
    
    
    }
    

    ) }

    【讨论】:

      【解决方案3】:

      我也是新手,但是在响应式 mongo 0.11 中,他们删除了 LastError 结构以支持 WriteResult。 WriteResult 上确实有一个 originalDocument 字段,但它是 unfortunately set to None with a comment 'TODO'

      我想出什么来返回创建的文档(减去 _id 字段),

        def promiseOfWriteResult[T](writeResult:Future[WriteResult])(value:T) = {
          val p = Promise[T]
          writeResult map {
            case ok if ok.ok   => p.success(value)
            case error         => p.failure(error.getCause)
          } recover { // !important
            case x : Throwable => p.failure(x)
          }
          p.future
        }
      

      这会像这样使用,

      promiseOfWriteResult(collection.insert(doc))(doc)
      

      【讨论】:

      • 鉴于您仍然没有新插入的对象 ID,这样做的目的是什么?
      猜你喜欢
      • 1970-01-01
      • 2019-12-12
      • 1970-01-01
      • 2019-02-28
      • 1970-01-01
      • 1970-01-01
      • 2013-10-04
      • 2017-08-04
      • 2021-08-01
      相关资源
      最近更新 更多