首页 文章

Slick 3.0多对多查询,将join作为可迭代

提问于
浏览
7

我使用Slick 3.0创建了一个多对多的集合,但我正在努力以我想要的方式检索数据 .

事件和兴趣之间存在多对多关系 . 这是我的表格:

case class EventDao(title: String,
                    id: Option[Int] = None)


class EventsTable(tag: Tag)
  extends Table[EventDao](tag, "events") {

  def id = column[Int]("event_id", O.PrimaryKey, O.AutoInc)
  def title = column[String]("title")

  def * = (
    title,
    id.?) <> (EventDao.tupled, EventDao.unapply)

  def interests = EventInterestQueries.query.filter(_.eventId === id)
    .flatMap(_.interestFk)
}


object EventQueries {

  lazy val query = TableQuery[EventsTable]

  val findById = Compiled { k: Rep[Int] =>
    query.filter(_.id === k)
  }
}

这是活动兴趣:

case class EventInterestDao(event: Int, interest: Int)


class EventsInterestsTable(tag: Tag)
  extends Table[EventInterestDao](tag, "events_interests") {

  def eventId = column[Int]("event_id")
  def interestId = column[Int]("interest_id")

  def * = (
    eventId,
    interestId) <> (EventInterestDao.tupled, EventInterestDao.unapply)

  def eventFk = foreignKey("event_fk", eventId, EventQueries.query)(e => e.id)
  def interestFk = foreignKey("interest_fk", interestId, InterestQueries.query)(i => i.id)
}


object EventInterestQueries {
  lazy val query = TableQuery[EventsInterestsTable]
}

最后的兴趣:

case class InterestDao(name: String,
                       id: Option[Int] = None)

class InterestsTable(tag: Tag)
  extends Table[InterestDao](tag, "interests") {

  def id = column[Int]("interest_id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def name_idx = index("idx_name", name, unique = true)

  def * = (
    name,
    id.?) <> (InterestDao.tupled, InterestDao.unapply)

  def events = EventInterestQueries.query.filter(_.interestId === id)
    .flatMap(_.eventFk)
}


object InterestQueries {

  lazy val query = TableQuery[InterestsTable]

  val findById = Compiled { k: Rep[Int] =>
    query.filter(_.id === k)
  }
}

我可以使用以下命令查询和检索(event.name,interest)的元组:

val eventInterestQuery = for {
  event <- EventQueries.query
  interest <- event.interests
} yield (event.title, interest.name)

Await.result(db.run(eventInterestQuery.result).map(println), Duration.Inf)

所以这就是我现在拥有的 .

我想要的是能够填充案例类,如:

case class EventDao(title: String,
                interests: Seq[InterestDao],
                id: Option[Int] = None)

麻烦的是,如果我像这样更新我的案例类,它会在 EventsTable 中弄乱我的 def * 投影 . 另外,我必须将 EventsTable.interests 过滤器重命名为 EventsTable.interestIds ,这有点难看,但如果有必要,我可以忍受 .

另外,我找不到编写产生 (event.name, Seq(interest.name))for 查询的方法 . 无论如何,这只是我的一个垫脚石,能够产生一个我真正想要回归的元组 .

有谁知道我怎么能做到这些事情?我也希望能够“获取”一定数量的兴趣,因此对于某些查询,所有内容都将被退回,但对于其他查询,只有前三个会被退回 .

2 回答

  • 1

    所以看完this page并在邮件列表上聊天后,我终于明白了:

    val eventInterestQuery = for {
      event <- EventQueries.query
      interest <- event.interests
    } yield (event, interest)
    
    Await.result(db.run(eventInterestQuery.result
      // convert the interests to a sequence.
      .map {
      _.groupBy(_._1)
        .map {
          case (k,v) => (k, v.map(_._2))
      }.toSeq
    }
    ), Duration.Inf)
    
  • 6

    groupBy的唯一问题是你失去了秩序 . 你可以折叠结果 . 我为我当前的项目编写了这个帮助器:

    def foldOneToMany[A, B](in: Seq[(A, Option[B])], eq: (A, B) => Boolean)
                            (f: (A, B) => A): Seq[A] = 
      in.foldLeft(List.empty[A]) {
        case (head :: tail, (_, Some(rel))) if eq(head, rel) =>
          f(head, rel) :: tail
        case (r, (el, Some(rel))) => f(el, rel) :: r
        case (r, (el, None)) => el :: r
      }.reverse
    

    它可以用一些爱 . 现在它接受函数A,B =>布尔来确定B是否属于A而函数A,B => A将B添加到A.

    Virtualeyes也有一点 . 在Postgres中,您可以使用 array_agg 函数从db中使用少一点的带宽 .

相关问题