2016-03-14 3 views
2

Новое в игре, scala и reactivemongo, и документация не очень дружелюбна.ReactiveMongo w/Play Scala

Я вижу раздел Bulk Insert в 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) 
} 

}

ответ

3

Вот полный пример того, как вы можете это сделать:

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]. validValidationResult - переменная типа Seq[Person] и содержит действительные данные на данный момент. То, что мы хотим вставить в базу данных.

Для этого нам необходимо подготовить документы путем сопоставления каждого документа с помощью ImplicitlyDocumentProducer вашей целевой коллекции (здесь personCollection). То выходит вам bulkDocs типа Seq[personCollection.ImplicitlyDocumentProducer]. Вы можете просто использовать bulkInsert() с этим:

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

Мы используем _ * здесь восклицательный знак с послед, так как bulkInsert() ожидает и с переменным числом аргументов не Seq. См. this thread for more info about it. И это в основном это уже.

Код для повторного воспроизведения обрабатывает результаты воспроизведения и проверяет полученный орган запроса, чтобы убедиться, что он содержит достоверные данные.

Вот несколько общих советов для работы с игры/reactivemongo/SCALA/фьючерсы:

Избегайте Await.result. В основном он не нужен в производственном коде. Идея фьючерсов заключается в выполнении неблокирующих операций. Заблокировать их снова Await.result. Это может быть полезно для отладки или тестового кода, но даже тогда есть, как правило, лучшие способы решения проблем. Фьючерсы Scala (в отличие от java) очень мощные, и вы можете много сделать с ними, см., Например, flatMap/map/filter/foreach/.. в будущем scaladoc. Этот код, например, использует именно это. Он использует Action.async вместо Action по методу контроллера. Это означает, что он должен вернуть Future[Result] вместо Result. Это здорово, потому что ReactiveMongo возвращает кучу фьючерсов для всех операций. Итак, все, что вам нужно сделать, это выполнить bulkInsert, который возвращает Будущее и использует map(), чтобы нанести на карту возвращенный Future[MultiBulkWriteResult] на Future[Result]. Это не приводит к блокировке, и игра может отлично работать с возвращенным будущим.

Конечно, приведенный выше пример можно немного улучшить, я попытался сохранить его простым. Например, вы должны возвращать правильные сообщения об ошибках при возврате BadRequest (сбой проверки тела запроса) или InternalServerError (сбой записи базы данных). Вы можете получить дополнительную информацию об ошибках от invalidValidationResult и insertResult. И вы можете использовать Форматы вместо многих Reads/Writes (а также использовать их для ReactiveMongo).Для получения дополнительной информации об этом загляните в документацию по игре json, а также в реактивный документ mongo.

+0

Спасибо, что определенно прояснил некоторые вещи для меня. Знаете ли вы какие-либо ресурсы для будущего моего понимания. Я родом из mavc-проекта MEAN, и все это превращается из JSON в BSON, а читатели и фьючерсы для меня совершенно новы, а также синтаксис play scala. – Jeff

+0

Да, это во многом. Я предлагаю вам пройти его медленно и проверить документацию для каждого класса или метода/функции, которые вы не знаете. [Play docs] (https://www.playframework.com/documentation/2.5.x/ScalaHome) неплохие, для чтения/записи проверьте главу о json. ReactiveMongo также имеет один для [эквивалента BSON] (http://reactivemongo.org/releases/0.11/documentation/bson/typeclasses.html). Для проверки фьючерсов [этот блогпост] (например, http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html), например , – alextsc

+0

Обратите внимание, что я уже «ленив» в моем примере кода выше, я использую функции макросов, предоставляемые как игровым, так и реактивным монго, для генерации операций чтения/записи. Они могут быть написаны вручную. Не путайте, когда видите это. Это одно и то же, и сначала вы должны практиковать это «вручную». – alextsc

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" 
) 

Испытан с игрой 2.5.1, хотя он должен компилировать в предыдущих версиях игры.

+1

с использованием 'person' в качестве результата JSONCollection в' ImplicitlyDocumentProducer' не может быть найден. Однако, когда я изменил определение 'person' на BSONCollection, был найден' ImplicitlyDocumentProducer', но '(_)' создал несоответствие типа, где было найдено 'models.Person', но' PersonController.this.personCollection.ImplicitlyDocumentProducer' требовалось. – Jeff

+0

Просмотрите свой импорт. Специально collection._ –

+0

Полный фрагмент кода компилирует и вставляет данные. –

1

FYI, как было сказано в предыдущих ответах, есть два способа манипулирования данными JSON: использовать модуль ReactiveMongo + проигрывать библиотеку JSON или использовать библиотеку BSON от ReactiveMongo.

Документация модуля ReactiveMongo для платформы воспроизведения - available online. Здесь вы можете найти примеры кода.

Смежные вопросы