Как запросить «$in» поверх «_id» в реактивном монго и играть

У меня есть проект с playframework 2.2.0 и play2-reactivemongo 0.10.0-SNAPSHOT. Я хотел бы запросить несколько документов по их идентификаторам примерно так:

def usersCollection = db.collection[JSONCollection]("users")
val ids: List[String] = /* fetched from somewhere else */
val query = ??
val users = usersCollection.find(query).cursor[User].collect[List]()

В качестве запроса я пробовал:

Json.obj("_id" -> Json.obj("$in" -> ids))                        // 1
Json.obj("_id.$oid" -> Json.obj("$in" -> ids))                   // 2
Json.obj("_id" -> Json.obj("$oid" -> Json.obj("$in" -> ids)))    // 3

для которых первый и второй возвращают пустые списки, а третий завершается с ошибкой assertion 10068 invalid operator: $oid.


person mcveat    schedule 29.10.2013    source источник
comment
Почему не Json.obj("_id" -> Json.obj("$in" -> ids.map(BSONObjectID(_))))?   -  person Dominik Bucher    schedule 31.10.2013
comment
@Dom, потому что тогда вам нужно иметь экземпляр Write[BSONObjectID] в неявной области, а play-reactivemongo предлагает только частичный. Кроме того, написание одного кажется неэффективным, так как в этом случае вы будете выполнять преобразование BSONValue -> JsValue -> BSONValue.   -  person mcveat    schedule 31.10.2013
comment
Хорошо, понял, что ты имеешь в виду, спасибо.   -  person Dominik Bucher    schedule 31.10.2013


Ответы (5)


ПРИМЕЧАНИЕ: копия моего ответа в списке рассылки ReactiveMongo.

Во-первых, извините за задержку моего ответа, возможно, я пропустил ваш вопрос. Play-ReactiveMongo не может самостоятельно догадаться, что значения массива Json являются ObjectId. Вот почему вам нужно создать объект Json для каждого идентификатора, который выглядит так: {"$oid": "526fda0f9205b10c00c82e34"}. Когда плагин ReactiveMongo Play видит объект, первое поле которого равно $oid, он обрабатывает его как ObjectId, чтобы драйвер мог отправить правильный тип для этого значения (в данном случае BSONObjectID).

На самом деле это более общая проблема: формат JSON не совсем соответствует формату BSON. Это относится к числовым типам (BSONInteger, BSONLong, BSONDouble), BSONRegex, BSONDateTime и BSONObjectID. Вы можете найти более подробную информацию в документации MongoDB: http://docs.mongodb.org/manual/reference/mongodb-extended-json/ .

person Stephane Godbillon    schedule 11.11.2013

Мне удалось решить это с помощью:

val objectIds = ids.map(id => Json.obj("$oid" -> id))
val query = Json.obj("_id" -> Json.obj("$in" -> objectIds))
usersCollection.find(query).cursor[User].collect[List]()

поскольку формат play-reactivemongo рассматривает BSONObjectID только тогда, когда за «$oid» следует строка

implicit object BSONObjectIDFormat extends PartialFormat[BSONObjectID] {
  def partialReads: PartialFunction[JsValue, JsResult[BSONObjectID]] = {
    case JsObject(("$oid", JsString(v)) +: Nil) => JsSuccess(BSONObjectID(v))
  }
  val partialWrites: PartialFunction[BSONValue, JsValue] = {
    case oid: BSONObjectID => Json.obj("$oid" -> oid.stringify)
  }
}

Тем не менее, я надеюсь, что есть более чистое решение. Если нет, я думаю, это делает хороший запрос на вытягивание.

person mcveat    schedule 29.10.2013
comment
где прописать BSONObjectIDFormat ? - person Harmeet Singh Taara; 09.03.2015
comment
@HarmeetSinghTaara, вы можете иметь его где угодно. Что вам нужно сделать, так это ввести его в область действия везде, где вам нужно это сделать. Нравится import package.BSONObjectIDFormat - person mcveat; 09.03.2015
comment
когда я вызываю метод request.body.validate[User] validate, это будет автоматически? мой формат запроса как { "_id":{"$oid":"54fd4b7084071e6a6ab13cee"} "name" : "Akka", "age" : 30, "created" : 1425886070013 } - person Harmeet Singh Taara; 09.03.2015

Мне интересно, не является ли преобразование id в BSONObjectID более безопасным:

val ids: List[String] = ???
val bsonObjectIds = ids.map(BSONObjectID.parse(_)).collect{case Success(t) => t}

это будет генерировать только действительные идентификаторы BSONObjectID (и отбрасывать недействительные). Если вы сделаете это следующим образом:

val objectIds = ids.map(id => Json.obj("$oid" -> id))

ваши objectId могут быть недействительными, в зависимости от того, действительно ли строковый идентификатор является строковой версией BSONObjectID или нет

person fmasion    schedule 05.01.2014

Если вы импортируете play.modules.reactivemongo.json._, он будет работать без форматирования $oid.

import play.modules.reactivemongo.json._
...
val ids: Seq[BSONObjectID] = ???
val selector = Json.obj("_id" -> Json.obj("$in" -> ids))
usersCollection.find(selector).cursor[User].collect[Seq]()
person mgosk    schedule 09.01.2017

Я пробовал со следующим, и это сработало для меня:

val listOfItems = BSONArray(51, 61)

val query = BSONDocument("_id" -> BSONDocument("$in" -> listOfItems))

val ruleListFuture = bsonFutureColl.flatMap(_.find(query, Option.empty[BSONDocument]).cursor[ResponseAccDataBean]().
      collect[List](-1, Cursor.FailOnError[List[ResponseAccDataBean]]()))
person swapnil shashank    schedule 10.07.2020