【问题标题】:How to query with '$in' over '_id' in reactive mongo and play如何在反应式mongo中使用'$in'查询'_id'并播放
【发布时间】:2013-10-29 12:11:44
【问题描述】:

我有一个使用playframework 2.2.0play2-reactivemongo 0.10.0-SNAPSHOT 设置的项目。我想通过 id 查询一些文档,方式类似于:

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

【问题讨论】:

  • 为什么不Json.obj("_id" -> Json.obj("$in" -> ids.map(BSONObjectID(_))))
  • @Dom 因为你必须在隐式范围内拥有Write[BSONObjectID] 的实例,而play-reactivemongo 只提供部分实例。此外,写一个感觉效率不高,因为在这种情况下你将转换BSONValue -> JsValue -> BSONValue
  • 好的,明白你的意思,谢谢。

标签: mongodb scala playframework playframework-2.2 reactivemongo


【解决方案1】:

注意:我在 ReactiveMongo 邮件列表中的回复副本。

首先,很抱歉我的回答延迟了,我可能错过了您的问题。 Play-ReactiveMongo 无法自行猜测 Json 数组的值是 ObjectId。这就是为什么你必须为每个看起来像这样的 id 创建一个 Json 对象:{"$oid": "526fda0f9205b10c00c82e34"}。当 ReactiveMongo Play 插件看到第一个字段为 $oid 的对象时,它会将其视为 ObjectId,以便驱动程序可以发送此值的正确类型(在本例中为 BSONObjectID。)

这实际上是一个更普遍的问题:JSON 格式与 BSON 格式不完全匹配。这就是数字类型(BSONIntegerBSONLongBSONDouble)、BSONRegexBSONDateTimeBSONObjectID 的情况。您可以在 MongoDB 文档中找到更多详细信息:http://docs.mongodb.org/manual/reference/mongodb-extended-json/

【讨论】:

    【解决方案2】:

    我设法解决了:

    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 格式仅在 "$oid" 后跟字符串时才考虑 BSONObjectID

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

    不过,我希望有一个更清洁的解决方案。如果不是,我想这会是一个不错的拉取请求。

    【讨论】:

    • 在哪里注册BSONObjectIDFormat
    • @HarmeetSinghTaara 你可以在任何你想要的地方拥有它。您需要做的是将它纳入您需要执行此操作的任何地方。喜欢import package.BSONObjectIDFormat
    • 当我调用request.body.validate[User] validate 方法时,这会自动吗?我的请求格式如{ "_id":{"$oid":"54fd4b7084071e6a6ab13cee"} "name" : "Akka", "age" : 30, "created" : 1425886070013 }
    【解决方案3】:

    我想知道以这种方式将 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))
    

    你的 objectIds 可能不是有效的,这取决于 string id 是否真的是 BSONObjectID 的 stringify 版本

    【讨论】:

      【解决方案4】:

      如果您导入 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]()
      

      【讨论】:

        【解决方案5】:

        我尝试了以下方法,它对我有用:

        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]]()))
        

        【讨论】:

          猜你喜欢
          • 2021-01-20
          • 2011-09-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多