【问题标题】:How to represent a GeoJSON point in a ReactiveMongo model?如何在 ReactiveMongo 模型中表示 GeoJSON 点?
【发布时间】:2014-01-02 07:56:52
【问题描述】:

为了在 MongoDB 中进行地理空间查询,带有位置的文档(带有 2d2dsphere 地理空间索引)应如下所示:

{
    _id: …,
    loc: {
        type: "Point",
        coordinates: [ <longitude>, <latitude> ]
    }
}

我对 Scala、ReactiveMongo 和 Play 框架非常陌生,但在我看来,使用这样一个位置的一个明显方法是通过一个案例类,例如:

case class Point(lon: Double, lat: Double)

网站 API 处理的 JSON 表示应该类似于:

{
    _id: …
    loc: [ <longitude>, <latitude> ]
}

现在,我不知道如何告诉我的 ReactiveMongo 模型在这些格式之间进行序列化/反序列化。

我的控制器如下所示:

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.json._
import scala.concurrent.Future

// Reactive Mongo imports
import reactivemongo.api._
import scala.concurrent.ExecutionContext.Implicits.global

// Reactive Mongo plugin
import play.modules.reactivemongo.MongoController
import play.modules.reactivemongo.json.collection.JSONCollection

object Application extends Controller with MongoController {
    def collection: JSONCollection = db.collection[JSONCollection]("test")

    import play.api.data.Form
    import models._
    import models.JsonFormats._

    def createCC = Action.async {
        val user = User("John", "Smith", Point(-0.0015, 51.0015))
        val futureResult = collection.insert(user)
        futureResult.map(_ => Ok("Done!"))
    }
}

我尝试使用 PointWriter 和 PointReader。这是我的models.scala:

package models

import reactivemongo.bson._
import play.modules.reactivemongo.json.BSONFormats._

case class User(
    // _id: Option[BSONObjectID],
    firstName: String,
    lastName: String,
    loc: Point)

case class Point(lon: Double, lat: Double)

object Point {
    implicit object PointWriter extends BSONDocumentWriter[Point] {
        def write(point: Point): BSONDocument = BSONDocument(
            "type" -> "Point",
            "coordinates" -> Seq(point.lat, point.lon)
        )
    }

    implicit object PointReader extends BSONReader[BSONDocument, Point] {
        def read(doc: BSONDocument): Point = Point(88, 88)
    }
}

object JsonFormats {
    import play.api.libs.json.Json
    import play.api.data._
    import play.api.data.Forms._

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

    implicit val pointFormat = Json.format[Point]
}

当我调用控制器操作createCC 时,我希望新创建的文档有一个格式正确的 Point 对象,但我实际得到的是这样的:

{
    "_id": ObjectId("52ac76dd1454bbf6d96ad1f1"),
    "loc": {
        "lon": -0.0015,
        "lat": 51.0015 
    }
}

所以我尝试使用PointWriterPointReader 告诉ReactiveMongo 如何将这样的Point 对象写入数据库根本没有效果。

谁能帮我理解我必须做什么?

(我来自 PHP 背景,并尝试了解 Scala...)

更新: 感谢 tmbo 的回答,我想出了这个作家:

val pointWrites = Writes[Point]( p =>
    Json.obj(
        "type" -> JsString("Point"),
        "coordinates" -> Json.arr(JsNumber(p.lon), JsNumber(p.lat))
    )
)

【问题讨论】:

    标签: mongodb scala playframework playframework-2.2 reactivemongo


    【解决方案1】:

    您面临的问题与JSONCollectionBSONCollection 之间的混淆有关。

    BSONCollectionreactivemongo 使用的默认集合。这个实现需要一个 BSONDocumentWriter 和一个 BSONReader 的实现,以便一个案例类获得(反)序列化。

    另一方面,

    JSONCollectionplay-reactive 模块 使用的默认集合实现。由于您在 db.collection[JSONCollection]("test") 中将集合定义为 JSONCollection,因此您需要提供隐式 json 格式。

    你提供的json格式是

    implicit val pointFormat = Json.format[Point]
    

    这会将对象序列化为以下格式

    {
        "lon": -0.0015,
        "lat": 51.0015 
    }
    

    如果你想将你的Point序列化为一个数组,你需要替换上面的隐式pointFormat

    import play.api.libs.json._
    import play.api.libs.json.Reads._
    
    case class Point(lng: Double, lat: Double)
    
    object Point {
    
      val pointWrites = Writes[Point]( p => Json.toJson(List(p.lng, p.lat)))
    
      val pointReads = minLength[List[Double]](2).map(l => Point(l(0), l(1)))
    
      implicit val pointFormat = Format(pointReads, pointWrites)
    }
    

    你实际上不需要BSONReaderBSONDocumentWriter

    编辑: 这是一个读取,它也验证了文档的 type 属性:

    val pointReads =
      (__ \ 'type).read[String](constraints.verifying[String](_ == "Point")) andKeep
        (__ \ 'coordinates).read[Point](minLength[List[Double]](2).map(l => Point(l(0), l(1))))
    

    【讨论】:

    • 嘿@tmbo,感谢您的出色回答!虽然这不会给我完全需要的格式,但我的问题的核心是,为什么我的阅读器/作者被忽略了。这将对我有很大帮助,我会在几个小时内发布我的结果。
    • 我想将其调整为(反)序列化为不同的格式应该不难。但如果您需要任何帮助,请告诉我。
    • 感谢您的回答,我很容易就知道作者应该是什么样子。这就是我想出的并且它有效:(请参阅上面我的问题中的更新。)但我正在与读者斗争,这让我认为我可能需要返回几步并学习更多关于 Scala 的基本知识和播放。
    • 我添加了一个更适合您的结构的替代方案
    猜你喜欢
    • 1970-01-01
    • 2013-03-11
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-23
    • 1970-01-01
    相关资源
    最近更新 更多