问题描述
我知道之前曾有人问过这个问题,但我无法弄清楚。 在我的数据模型中,我有一个新闻模型,其中包含任意数量的图像。
case class NewsDataModel(
newsId: Option[Long],name: String,description: String,author: String,creationDateTime: Option[OffsetDateTime]
)
case class Image(
id: Long,url: String,newsId: Long
)
现在,我想查询数据库以获取类似(NewsDataModel,Option[Seq[Image]])
我的查询当前按以下方式实现:
val q = newsTable.joinLeft(imagesTable).on(_.newsId === _.newsId).result
db.run(q)
计算结果为Future[Seq[(NewsDataModel,Option[Image])]]
。我猜想解决此问题的正确方法是使用groupBy
函数,但自此以来我不知道如何实现
val q = newsTable.joinLeft(imagesTable).on(_.newsId === _.newsId)
.groupBy(_._1.newsId)
.result
db.run(q)
评估为Future[Seq[(Option[Long],Query[(NewsTable,Rep[Option[ImagesTable]]),(NewsDataModel,Option[Image]),Seq])]]
解决方法
Slick不会自动为您创建该数据结构。 (我发现从行和表以及在可移植SQL中可以做什么而不是从“对象关系映射器”或类似的角度来考虑Slick很有帮助。)
您要做的是在数据库层之后将行转换为所需的Scala格式。有很多方法可以做到这一点。
这是做到这一点的一种方法。
给出此示例数据...
scala> case class NewsDataModel(newsId: Long)
class NewsDataModel
scala> case class Image(id: Long)
class Image
scala> val results = Seq(
| ( NewsDataModel(1L),Some(Image(1L)) ),| ( NewsDataModel(1L),Some(Image(10L)) ),None ),| ( NewsDataModel(2L),| ( NewsDataModel(3L),Some(Image(3L)) ),| )
|
val results: Seq[(NewsDataModel,Option[Image])] = List((NewsDataModel(1),Some(Image(1))),(NewsDataModel(1),Some(Image(10))),None),(NewsDataModel(2),(NewsDataModel(3),Some(Image(3))))
我们可以按键分组:
scala> val groups = results.groupBy { case (key,values) => key }
val groups: scala.collection.immutable.Map[NewsDataModel,Seq[(NewsDataModel,Option[Image])]] = HashMap(NewsDataModel(3) -> List((NewsDataModel(3),Some(Image(3)))),NewsDataModel(1) -> List((NewsDataModel(1),None)),NewsDataModel(2) -> List((NewsDataModel(2),None)))
并将其转换为所需的类型:
scala> val flat = groups.map { case (key,seq) => key -> seq.flatMap(_._2) }
val flat: scala.collection.immutable.Map[NewsDataModel,Seq[Image]] = HashMap(NewsDataModel(3) -> List(Image(3)),NewsDataModel(1) -> List(Image(1),Image(10)),NewsDataModel(2) -> List())
该flat
结果是一张地图,但您可以将其变成(例如)一个列表,该列表具有您想要的类型签名(接近):
scala> flat.toList
val res18: List[(NewsDataModel,Seq[Image])] = List((NewsDataModel(3),List(Image(3))),List(Image(1),Image(10))),List()))
您可以找到许多方法来做到这一点,但重点是您是在Scala中进行操作,而不是Slick(SQL)。请特别注意,我使用的groupBy
方法是集合库中的Scala方法,而不是Slick方法(这将是SQL GROUP BY
子句)。也就是说,我正在修改运行查询的结果,而不是修改查询本身。
我建议将您想要的任何转换放入方法中,然后将其应用于Slick操作。例如:
def convert(input: Seq[(NewsDataModel,Option[Image])]): Seq[(NewsDataModel,Seq[Image])] =
??? // your implementation here
val action = newsTable.joinLeft(imagesTable).on(_.newsId === _.newsId).result
val convertedAction = action.map(convert)
db.run(convertedAction)