将Slick monadic join的结果序列映射到Json

时间:2016-04-03 20:51:44

标签: json playframework slick playframework-2.4 slick-3.0

我使用Play 2.4和Slick 3.1.x,特别是Slick-Play plugin v1.1.1。首先,一些上下文...我在DAO中有以下搜索/过滤方法,它将4个模型连接在一起:

  def search(
              departureCity: Option[String],
              arrivalCity: Option[String],
              departureDate: Option[Date]
            ) = {
    val monadicJoin = for {
      sf <- slickScheduledFlights.filter(a =>
              departureDate.map(d => a.date === d).getOrElse(slick.lifted.LiteralColumn(true))
            )
      fl <- slickFlights if sf.flightId === fl.id
      al <- slickAirlines if fl.airlineId === al.id
      da <- slickAirports.filter(a =>
              fl.departureAirportId === a.id &&
              departureCity.map(c => a.cityCode === c).getOrElse(slick.lifted.LiteralColumn(true))
            )
      aa <- slickAirports.filter(a =>
              fl.arrivalAirportId === a.id &&
              arrivalCity.map(c => a.cityCode === c).getOrElse(slick.lifted.LiteralColumn(true))
            )
    } yield (fl, sf, al, da, aa)

    db.run(monadicJoin.result)
  }

此输出是包含序列的Vector,例如:

Vector(
  (
    Flight(Some(1),123,216,2013,3,1455,2540,3,905,500,1150),
    ScheduledFlight(Some(1),1,2016-04-13,90,10),
    Airline(Some(216),BA,BAW,British Airways,United Kingdom),
    Airport(Some(2013),LHR,Heathrow,LON,...),
    Airport(Some(2540),JFK,John F Kennedy Intl,NYC...)
  ), 
  (
    etc ...
  )
)

我目前通过在地图上调用.toJson并插入此Vector(下面的results param)来渲染控制器中的JSON,如下所示:

 flightService.search(departureCity, arrivalCity, departureDate).map(results => {
     Ok(
        Map[String, Any](
          "status" -> "OK",
          "data" -> results
        ).toJson
     ).as("application/json")
 })

虽然这种作品有效,却产生output in an unusual format;每个结果对象中的结果数组(行),连接嵌套在具有键的对象内:&#34; _1&#34;,&#34; _2&#34;等等。

所以问题是:我该如何重组这个?

在Slick文档中似乎没有任何具体涵盖此类场景的内容。因此,我会感谢一些关于重构这个Seq's Vector的最佳方法的一些意见,以期重新命名每个连接,甚至将其展平并仅保留某些字段?

在我从DAO搜索方法返回之前(通过以某种方式映射它?)或在我从搜索方法中返回Future结果向量之后在控制器中最好完成吗?

或者我想知道是否最好将这种突变完全抽象出其他地方,或许使用transformer

1 个答案:

答案 0 :(得分:0)

您需要JSON Reads/Writes/Format Combinators

首先,您的所有课程必须Writes[T]FlightScheduledFlightAirlineAirport)。 简单的方法是使用Json宏

implicit val flightWrites: Writes[Flight] = Json.writes[Flight]
implicit val scheduledFlightWrites: Writes[ScheduledFlight] = Json.writes[ScheduledFlight]
implicit val airlineWrites: Writes[Airline] = Json.writes[Airline]
implicit val airportWrites: Writes[Airport] = Json.writes[Airport] 

您还必须为Vector项目实现OWrites[(Flight, ScheduledFlight, Airline, Airport, Airport)]。例如:

val itemWrites: OWrites[(Flight, ScheduledFlight, Airline, Airport, Airport)] = (
  (__ \ "flight").write[Flight] and
  (__ \ "scheduledFlight").write[ScheduledFlight] and
  (__ \ "airline").write[Airline] and
  (__ \ "airport1").write[Airport] and
  (__ \ "airport2").write[Airport]
).tupled

将整个Vector写为JsAray使用Writes.seq[T]

val resultWrites: Writes[Seq[(Flight, ScheduledFlight, Airline, Airport, Airport)]] = Writes.seq(itemWrites)

我们全都响应您的数据

flightService.search(departureCity, arrivalCity, departureDate).map(results => 
  Ok(
    Json.obj(
      "status" -> "Ok", 
      "data" -> resultWrites.writes(results)
    )
 )