【问题标题】:ReactiveMongo w/ Play Scala带有 Play Scala 的 ReactiveMongo
【发布时间】:2016-06-29 23:00:54
【问题描述】:

新玩、scala 和 reactivemongo,文档对新手不太友好。

我在See Bulk Insert 看到了批量插入部分

但我不知道他们为什么不显示它包含在方法中? 我期待一个包含多个对象的 JSON 数据的请求。如何设置批量插入来处理多个插入并返回错误。

例如单次插入方法如下:

def createFromJson = Action(parse.json) {

request =>
  try {
    val person = request.body.validate[Person].get

    val mongoResult = Await.result(collection.insert(person),Duration.apply(20,"seconds"))
    if(mongoResult.hasErrors) throw new Exception(mongoResult.errmsg.getOrElse("something unknown"))


    Created(Json.toJson(person))
}
catch {
    case e: Exception => BadRequest(e.getMessage)
}

}

【问题讨论】:

    标签: scala playframework playframework-2.0 reactivemongo


    【解决方案1】:

    这是一个完整的例子:

    class ExampleController @Inject()(database: DefaultDB) extends Controller {
    
      case class Person(firstName: String, lastName: String)
    
      val personCollection: BSONCollection = database.collection("persons")
      implicit val PersonJsonReader: Reads[Person] = Json.reads[Person]
      implicit val PersonSeqJsonReader: Reads[Seq[Person]] = Reads.seq(PersonJsonReader)
      implicit val PersonJsonWriter: Writes[Person] = Json.writes[Person]
      implicit val PersonSeqJsonWriter: Writes[Seq[Person]] = Writes.seq(PersonJsonWriter)
      implicit val PersonBsonWriter = Macros.writer[Person]
    
      def insertMultiple = Action.async(parse.json) { implicit request =>
        val validationResult: JsResult[Seq[Person]] = request.body.validate[Seq[Person]]
    
        validationResult.fold(
          invalidValidationResult => Future.successful(BadRequest),
          // [1]
          validValidationResult => {
            val bulkDocs = validValidationResult.
              map(implicitly[personCollection.ImplicitlyDocumentProducer](_))
    
            personCollection.bulkInsert(ordered = true)(bulkDocs: _*).map {
              case insertResult if insertResult.ok =>
                Created(Json.toJson(validationResult.get))
              case insertResult => 
                InternalServerError
            }
          }
        )
      }
    }
    

    所有内容都位于 [1] 之后的行中。 validValidationResultSeq[Person] 类型的变量,此时包含有效数据。这就是我们要插入数据库的内容。

    为此,我们需要通过将每个文档映射到目标集合的ImplicitlyDocumentProducer(此处为personCollection)来准备文档。剩下的就是bulkDocs 类型的Seq[personCollection.ImplicitlyDocumentProducer]。你可以直接使用bulkInsert()

    personCollection.bulkInsert(ordered = true)(bulkDocs: _*)
    

    我们在这里使用 _* 来分解 Seq,因为 bulkInsert() 需要可变参数而不是 Seq。见this thread for more info about it。基本上已经这样了。

    剩下的代码正在处理播放结果并验证收到的请求正文以确保它包含有效数据。

    以下是一些使用 play/reactivemongo/scala/futures 的一般技巧:

    避免Await.result。您基本上在生产代码中永远不需要它。期货背后的想法是执行非阻塞操作。让他们再次使用Await.result 阻止会破坏目的。它对于调试或测试代码很有用,但即便如此,通常也有更好的方法来处理事情。 Scala 期货(不像 java 期货)非常强大,你可以用它们做很多事情,参见例如flatMap/map/filter/foreach/.. 在未来的 scaladoc 中。例如,上面的代码正是利用了这一点。它在控制器方法中使用Action.async 而不是Action。这意味着它必须返回 Future[Result] 而不是 Result。这很棒,因为 ReactiveMongo 会为所有操作返回一堆 Futures。所以你所要做的就是执行bulkInsert,它返回一个Future并使用map()将返回的Future[MultiBulkWriteResult]映射到Future[Result]。这导致没有阻塞,并且可以正常使用返回的未来。

    当然上面的例子可以改进一点,我尽量保持简单。 例如,您应该在返回 BadRequest(请求正文验证失败)或 InternalServerError(数据库写入失败)时返回正确的错误消息。您可以从invalidValidationResultinsertResult 获得有关错误的更多信息。你可以使用 Formats 而不是那么多的读/写(也可以将它们用于 ReactiveMongo)。查看 play json 文档以及响应式 mongo 文档以获取更多信息。

    【讨论】:

    • 感谢它为我解决了一些问题。你知道有什么资源可以帮助我理解。我来自 MEAN mvc 背景,所有这些从 JSON 到 BSON 的转换以及 reader 和 futures 对我来说都是全新的,还有 play scala 语法。
    • 是的,一开始很多。我建议您慢慢阅读并检查您不知道的每个类或方法/函数的文档。 play docs 非常好,对于读/写,请查看关于 json 的章节。 ReactiveMongo 也有一个用于BSON equivalent。例如,对于期货,请检查 this blogpost
    • 请注意,我在上面的示例代码中很“懒惰”,我使用 play 和响应式 mongo 提供的宏函数来生成读/写。它们也可以手写。当你看到它时不要感到困惑。这是同一件事,您应该先练习“手动”操作。
    【解决方案2】:

    虽然前面的答案是正确的。 我们可以使用 JSONCollection 减少样板文件

    package controllers
    
    import javax.inject._
    
    import play.api.libs.json._
    import play.api.mvc._
    import play.modules.reactivemongo._
    import reactivemongo.play.json.collection.{JSONCollection, _}
    import utils.Errors
    
    import scala.concurrent.{ExecutionContext, Future}
    
    
    case class Person(name: String, age: Int)
    
    object Person {
      implicit val formatter = Json.format[Person]
    }
    
    @Singleton
    class PersonBulkController @Inject()(val reactiveMongoApi: ReactiveMongoApi)(implicit exec: ExecutionContext) extends Controller with MongoController with ReactiveMongoComponents {
    
      val persons: JSONCollection = db.collection[JSONCollection]("person")
    
      def createBulkFromJson = Action.async(parse.json) { request =>
    
        Json.fromJson[Seq[Person]](request.body) match {
          case JsSuccess(newPersons, _) =>
            val documents = newPersons.map(implicitly[persons.ImplicitlyDocumentProducer](_))
    
            persons.bulkInsert(ordered = true)(documents: _*).map{ multiResult =>
              Created(s"Created ${multiResult.n} persons")
            }
    
          case JsError(errors) =>
            Future.successful(BadRequest("Could not build an array of persons from the json provided. " + errors))
        }
      }
    }
    

    在 build.sbt 中

    libraryDependencies ++= Seq(
      "org.reactivemongo" %% "play2-reactivemongo" % "0.11.12"
    )
    

    用 play 2.5.1 测试过,虽然它应该在以前的 play 版本中编译。

    【讨论】:

    • 使用 persons 作为 JSONCollection 会导致找不到 ImplicitlyDocumentProducer。但是,当我将 persons 的定义更改为 BSONCollection 时,找到了 ImplicitlyDocumentProducer,但 (_) 产生了类型不匹配,其中找到了 models.Person,但需要 PersonController.this.personCollection.ImplicitlyDocumentProducer
    • 查看您的导入。特别收藏。_
    • 完整代码sn -p编译插入数据。
    • @JonasAnso 使用响应式 mongo 0.11.12 和 playfw 2.4.6 尝试了您的 sn-p(复制粘贴)。得到 Jeff 提到的编译错误。你用什么版本?我也不知道如何解决这个问题。
    • 我已更新回复@kurochenko 添加依赖项。希望能帮助到你。你也可以看看github.com/jonasanso/play-reactive-mongo-db这个确切的代码不存在,但你可以在 CityController.createBulkFromJson 找到非常相似的东西
    【解决方案3】:

    仅供参考,正如之前的答案所说,有两种方法可以操作 JSON 数据:使用 ReactiveMongo 模块 + Play JSON 库,或者使用 ReactiveMongo 的 BSON 库。

    Play Framework 的 ReactiveMongo 模块的文档是available online。您可以在那里找到代码示例。

    【讨论】:

      猜你喜欢
      • 2015-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-21
      • 2017-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多