Nic*_*ick 4 scala mongodb playframework reactivemongo playframework-2.2
为了做到空间查询MongoDB中的文档与位置(有2d或2dsphere地理空间指数)应该是这个样子:
{
_id: …,
loc: {
type: "Point",
coordinates: [ <longitude>, <latitude> ]
}
}
Run Code Online (Sandbox Code Playgroud)
我是Scala,ReactiveMongo和Play Framework的新手,但在我看来,使用这样一个位置的一个明显方法是通过一个案例类:
case class Point(lon: Double, lat: Double)
Run Code Online (Sandbox Code Playgroud)
网站的API处理的JSON表示应该类似于:
{
_id: …
loc: [ <longitude>, <latitude> ]
}
Run Code Online (Sandbox Code Playgroud)
现在,我无法弄清楚如何告诉我的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!"))
}
}
Run Code Online (Sandbox Code Playgroud)
我试图使用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]
}
Run Code Online (Sandbox Code Playgroud)
当我调用控制器动作时,createCC我希望有一个格式正确的Point对象新创建的文档,但我实际得到的是:
{
"_id": ObjectId("52ac76dd1454bbf6d96ad1f1"),
"loc": {
"lon": -0.0015,
"lat": 51.0015
}
}
Run Code Online (Sandbox Code Playgroud)
所以我尝试使用PointWriter并PointReader告诉ReactiveMongo如何将这样的Point对象写入数据库根本没有任何效果.
任何人都可以帮我理解我必须做什么吗?
(我来自PHP背景,并尝试让我的头围绕Scala ......)
更新: 感谢tmbo的回答,我想出了这位作家:
val pointWrites = Writes[Point]( p =>
Json.obj(
"type" -> JsString("Point"),
"coordinates" -> Json.arr(JsNumber(p.lon), JsNumber(p.lat))
)
)
Run Code Online (Sandbox Code Playgroud)
你面临的问题与JSONCollection和之间的混淆有关BSONCollection.
BSONCollection是reactivemongo使用的默认集合.此实现需要a BSONDocumentWriter和a 的实现BSONReader来获取(de)序列化的case类.
JSONCollection另一方面,播放反应模块使用的默认集合实现.既然你定义的集合是一个JSONCollection在db.collection[JSONCollection]("test")您需要提供一个隐含的JSON格式.
您提供的json格式是
implicit val pointFormat = Json.format[Point]
Run Code Online (Sandbox Code Playgroud)
这会将对象序列化为以下格式
{
"lon": -0.0015,
"lat": 51.0015
}
Run Code Online (Sandbox Code Playgroud)
如果要将序列化为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)
}
Run Code Online (Sandbox Code Playgroud)
你实际上不需要BSONReader和BSONDocumentWriter.
编辑:
这是一个读取,也验证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))))
Run Code Online (Sandbox Code Playgroud)