将 Slick monadic join 的一系列结果映射到 Json
Mapping a sequence of results from Slick monadic join to Json
我正在使用 Play 2.4 和 Slick 3。1.x,特别是 Slick-Play plugin v1.1.1。首先,一些上下文...我在 DAO 中有以下 search/filter 方法,它将 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(
(
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
参数)在控制器中呈现 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;每个结果对象中的结果数组(行) 联接嵌套在对象中,键为:“_1”、“_2”等。
所以问题是:我应该如何重组它?
在 Slick 文档中似乎没有任何内容专门涵盖此类场景。因此,我将不胜感激关于什么可能是重构此 Seq 向量的最佳方法的一些输入,以重命名每个连接或什至将其展平并仅保留某些字段?
这最好是在 DAO 搜索方法返回之前(通过某种方式映射它?)还是在我从搜索方法返回 Future 结果向量之后在控制器中完成?
或者我想知道将这种突变完全从其他地方抽象出来是否更好,也许使用 transformer?
你需要JSON Reads/Writes/Format Combinators
首先,您的所有 类(Flight
、ScheduledFlight
、Airline
、Airport
)必须有 Writes[T]
.
简单的方法是使用 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)
)
)
我正在使用 Play 2.4 和 Slick 3。1.x,特别是 Slick-Play plugin v1.1.1。首先,一些上下文...我在 DAO 中有以下 search/filter 方法,它将 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(
(
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
参数)在控制器中呈现 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;每个结果对象中的结果数组(行) 联接嵌套在对象中,键为:“_1”、“_2”等。
所以问题是:我应该如何重组它?
在 Slick 文档中似乎没有任何内容专门涵盖此类场景。因此,我将不胜感激关于什么可能是重构此 Seq 向量的最佳方法的一些输入,以重命名每个连接或什至将其展平并仅保留某些字段?
这最好是在 DAO 搜索方法返回之前(通过某种方式映射它?)还是在我从搜索方法返回 Future 结果向量之后在控制器中完成?
或者我想知道将这种突变完全从其他地方抽象出来是否更好,也许使用 transformer?
你需要JSON Reads/Writes/Format Combinators
首先,您的所有 类(Flight
、ScheduledFlight
、Airline
、Airport
)必须有 Writes[T]
.
简单的方法是使用 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)
)
)