scala光滑的一对多收藏品

时间:2014-08-28 11:02:44

标签: scala slick

我有一个数据库,其中包含具有一对多注册关系的活动。 我们的目标是通过一系列注册来获取所有活动。

通过创建具有注册的活动的笛卡尔积,可以获得用于获取该数据的所有必要数据。 但我似乎无法找到一个很好的方法将它正确地放入scala集合中; 我们的类型为:Seq[(Activity, Seq[Registration])]

case class Registration(
  id: Option[Int],
  user: Int,
  activity: Int
)

case class Activity(
  id: Option[Int],
  what: String,
  when: DateTime,
  where: String,
  description: String,
  price: Double
)

假设存在适当的光滑表和表查询,我会写:

val acts_regs = (for {
   a <- Activities
   r <- Registrations if r.activityId === a.id
} yield (a, r))
  .groupBy(_._1.id)
  .map { case (actid, acts) => ??? }
}

但我似乎无法做出适当的映射。这样做的惯用方法是什么?我希望它比使用原始笛卡尔产品更好......

在Scala中

在scala代码中,它很容易,看起来像这样:

  val activities = db withSession { implicit sess =>
    (for {
      a <- Activities leftJoin Registrations on (_.id === _.activityId)
    } yield a).list
  }

  activities
    .groupBy(_._1.id)
    .map { case (id, set) => (set(0)._1, set.map(_._2)) }

但是由于表映射器将为您创建的Activity的不必要的实例化,这似乎相当低效。 它看起来也不是很优雅......

获取注册数

当只对注册计数感兴趣时,scala方法更糟糕:

val result: Seq[Activity, Int] = ???

在Slick中

我在光滑方面的最佳尝试看起来像这样:

  val activities = db withSession { implicit sess =>
    (for {
      a <- Activities leftJoin Registrations on (_.id === _.activityId)
    } yield a)
      .groupBy(_._1.id)
      .map { case (id, results) => (results.map(_._1), results.length) }
  }

但是这会导致一个错误,即光滑无法映射“map”-line中的给定类型。

2 个答案:

答案 0 :(得分:6)

我建议:

  val activities = db withSession { implicit sess =>
    (for {
      a <- Activities leftJoin Registrations on (_.id === _.activityId)
    } yield a)
      .groupBy(_._1)
      .map { case (activity, results) => (activity, results.length) }
  }

的问题
  val activities = db withSession { implicit sess =>
    (for {
      a <- Activities leftJoin Registrations on (_.id === _.activityId)
    } yield a)
      .groupBy(_._1.id)
      .map { case (id, results) => (results.map(_._1), results.length) }
  }

是您无法在group by中生成嵌套结果。 results.map(_._1)是项目的集合。在某些情况下,SQL会执行从集合到单行的隐式转换,但Slick是类型安全的并不是。你想在Slick做什么就像results.map(_._1).head,但目前不支持。你可以得到的最接近的是(results.map(_.id).max, results.map(_.what).max, ...),这非常繁琐。因此,整个活动行的分组可能是目前最可行的解决方法。

答案 1 :(得分:1)

获取每项活动所有注册的解决方案:

    // list of all activities
    val activities = Activities
    // map of registrations belonging to those activities
    val registrations = Registrations
      .filter(_.activityId in activities.map(_.id))
      .list
      .groupBy(_.activityId)
      .map { case (aid, group) => (aid, group.map(_._2)) }
      .toMap

    // combine them
    activities
      .list
      .map { a => (a, registrations.getOrElse(a.id.get, List()))

在2个查询中完成工作。将这种类型的“分组”函数抽象为scala函数应该是可行的。