如何使用 Scala Slick 编写一对多查询,其中 returns 类似这样的东西`(Model1, Option[Seq[Model2]])`
How to write a One to Many Query with Scala Slick which returns something like this `(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 不会自动为您创建该数据结构。 (我发现从行和表的角度来思考 Slick 以及您可以在便携式 SQL 中做什么而不是从“object-relational 映射器”或类似的角度来思考 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
结果是一个映射,但您可以将其转换为(例如)具有类型签名(接近)您想要的类型的列表:
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 方法(这将是 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)
我知道以前有人问过这个问题,但我想不通。 在我的数据模型中,我有一个包含任意数量图像的新闻模型。
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 不会自动为您创建该数据结构。 (我发现从行和表的角度来思考 Slick 以及您可以在便携式 SQL 中做什么而不是从“object-relational 映射器”或类似的角度来思考 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
结果是一个映射,但您可以将其转换为(例如)具有类型签名(接近)您想要的类型的列表:
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 方法(这将是 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)