如何用Scala Slick编写一对多查询,它返回类似于"(Model1,Option[Seq[Model2]



我知道以前有人问过这个问题,但我想不通。在我的数据模型中,我有一个包含任意数量图像的新闻模型。

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)) ),
|  ( NewsDataModel(1L), None             ),
|  ( NewsDataModel(2L), None             ),
|  ( NewsDataModel(3L), Some(Image(3L))  ),
| )
|
val results: Seq[(NewsDataModel, Option[Image])] = List((NewsDataModel(1),Some(Image(1))), (NewsDataModel(1),Some(Image(10))), (NewsDataModel(1),None), (NewsDataModel(2),None), (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),Some(Image(1))), (NewsDataModel(1),Some(Image(10))), (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结果是一个映射,但您可以将其转换为(例如(具有类型签名(接近(您想要的类型的List:

scala> flat.toList
val res18: List[(NewsDataModel, Seq[Image])] = List((NewsDataModel(3),List(Image(3))), (NewsDataModel(1),List(Image(1), Image(10))), (NewsDataModel(2),List()))

你可以找到很多方法来做到这一点,但关键是你是在Scala中做的,而不是Slick(SQL(。请特别注意,我使用的groupBy方法是集合库中的Scala方法,而不是Slick方法(它将是SQLGROUP 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)

最新更新