2014-02-01 2 views
5

Я хочу обновить документ JSON в MongoDB, как это:Play: Как vermongo реализовать с ReactiveMongo

{ 
    "_id":{"$oid":"52dfc13ec20900c2093155cf"}, 
    "email": "[email protected]", 
    "name": "joe", 
    "_version": 2 
} 

... и хотите создать vermongo документ, как это на каждом обновлении:

{ 
    "_id { "_id":{"$oid":"52dfc13ec20900c2093155cf"}, "_version": 1}, 
    "email": "[email protected]", 
    "name": "joe", 
    "_version": 1, 
    "_timestamp" : "2014-02-02T00:11:45.542" 
} 

Я пробовал решение так:

trait MyDao { 

    ... 

    private val shadowCollection = ReactiveMongoPlugin.db.collection[JSONCollection](
    collection.name + ".vermongo" 
) 

    private def toVersioned(deleted: Boolean) = __.json.update(
    (__ \ '_id).json.copyFrom((__ \ '_id \ '$oid).json.pickBranch) andThen 
    (__ \ '_id \ '_version).json.copyFrom((__ \ '_version).json.pick) andThen 
// (__ \ '_version).json.put(if (deleted) JsString(s"deleted:$version") else JsNumber(version)) andThen 
    (__ \ '_timestamp).json.put(Json.toJson(LocalDateTime.now)) 
) 

    private def version(doc: JsValue, deleted: Boolean): Future[LastError] = { 
    shadowCollection.insert(doc.transform(toVersioned(deleted)).get) 
    } 
} 

метод toVersioned имеет Чет Проблемы Е.Е.:

Line 1: Это не создает мульти-полей _id

Line 2: Он падает, когда я пытаюсь создать _version как второе поле _id

линии 3: (комментированные out), если параметр deleted равен true, я хотел бы пометить документ как удаленный, заменив "_version": 1 на "_version": "deleted:1"; мне не ясно, как справиться с этим условием.

ответ

3

Фантастический вопрос; Я собирался узнать о Play's JSON transformers целую вечность, и, наконец, отличная ситуация, чтобы попробовать их.

я должен отметить, что я был совершенно не в состоянии получить это правильно работать, пока я не добавил все важные functional.syntax._ импорт:

import play.api.libs.functional.syntax._ 

Решение началось из Gizmo ->Gremlin трансформатора пример на странице трансформаторов; вот где я получил использование and и reduce, который полностью трансформирует (прощает каламбур), как все это работает.

Так вот:

private def toVersioned(deleted: Boolean) = (__.json.update(
    (__ \ '_id).json.copyFrom((__ \ '_id \ '$oid).json.pickBranch) and 
    (__ \ '_id \ '_version).json.copyFrom((__ \ '_version).json.pick) and 
    (__ \ '_version).json.update(
     of[JsValue].map { case JsNumber(oldVersion) => 
     if (deleted) JsString(s"deleted:$oldVersion") else JsNumber(oldVersion) 
     } 
    ) and 
    (__ \ '_timestamp).json.put(Json.toJson(LocalDateTime.now)) reduce 
) 
    andThen 
    (__ \ '_id \ '$oid).json.prune 
) 

Ключевые моменты:

  • Я не мог (в пределах разумного периода времени) достичь «шаг» в _id узла в одном преобразуется, следовательно, первый проход копирует его «вниз» на один уровень, а второй проход (после andThen) удаляет старый $oid. Там почти-конечно, способ ...

  • Использование and, кажется, изменить сферу copyFrom, что позволяет _version ковыряться из верхнего уровня правильно - при использовании andThen это, казалось, только работать, если нисходящий в узлы «ниже» цели

  • Я вполне доволен обработкой deleted здесь - map выглядит как Scala- и PlayJSON-идиоматический. Of[JsValue] необходимо, потому что мы возвращаем либо JsString или JsNumber здесь, и JsValue казалось подходящим суперкласса

+0

...фантастический ответ ;-) Он работает и делает именно то, что мне нужно. Мне просто нужно было уладить все, чтобы удалить предупреждение: «матч не может быть исчерпывающим». Большое спасибо! – j3d

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