【发布时间】:2013-06-07 01:02:48
【问题描述】:
我正在尝试使用 Slick 查询多对多关系,但我遇到了各种错误,最突出的是“不知道如何将 (User, Skill) 解包到 T并打包到 G"。
表的结构类似于以下:
case class User(val name: String, val picture: Option[URL], val id: Option[UUID])
object Users extends Table[User]("users") {
def name = column[String]("name")
def picture = column[Option[URL]]("picture")
def id = column[UUID]("id")
def * = name ~ picture ~ id.? <> (User, User.unapply _)
}
case class Skill(val name: String, val id: Option[UUID])
object Skills extends Table[Skill]("skill") {
def name = column[String]("name")
def id = column[UUID]("id")
def * = name ~ id.? <> (Skill, Skill.unapply _)
}
case class UserSkill(val userId: UUID, val skillId: UUID, val id: Option[UUID])
object UserSkills extends Table[UserSkill]("user_skill") {
def userId = column[UUID]("userId")
def skillId = column[UUID]("skillId")
def id = column[UUID]("id")
def * = userId ~ skillId ~ id.? <> (UserSkill, UserSkill.unapply _)
def user = foreignKey("userFK", userId, Users)(_.id)
def skill = foreignKey("skillFK", skillId, Skills)(_.id)
}
最终,我想要实现的是某种形式
SELECT u.*, group_concat(s.name) FROM user_skill us, users u, skills s WHERE us.skillId = s.id && us.userId = u.id GROUP BY u.id
但在我花时间尝试让 group_concat 也能正常工作之前,我一直在尝试生成更简单的查询(我相信它仍然有效......)
SELECT u.* FROM user_skill us, users u, skills s WHERE us.skillId = s.id && us.userId = u.id GROUP BY u.id
我尝试了各种 scala 代码来生成此查询,但导致上述形状错误的一个示例是
(for {
us <- UserSkills
user <- us.user
skill <- us.skill
} yield (user, skill)).groupBy(_._1.id).map { case(_, xs) => xs.first }
同样,下面会产生一个关于“用户”而不是“(用户,技能)”的打包错误
(for {
us <- UserSkills
user <- us.user
skill <- us.skill
} yield (user, skill)).groupBy(_._1.id).map { case(_, xs) => xs.map(_._1).first }
如果有人有任何建议,我将不胜感激:我今天和昨天的大部分时间都在搜索 google/google 组以及 slick 源,但我还没有解决方案。
(另外,我使用的是 postgre,所以 group_concat 实际上是 string_agg)
编辑
所以看起来当使用 groupBy 时,映射的投影会被应用,因为类似
(for {
us <- UserSkills
u <- us.user
s <- us.skill
} yield (u,s)).map(_._1)
工作正常,因为 _._1 给出了用户类型,因为用户是一个表,所以它有一个形状。然而,当我们调用 xs.first 时(就像我们调用 groupBy 时所做的那样),我们实际上得到了一个映射的投影类型 (User, Skill),或者如果我们先应用 map(_._1),我们得到了 User 类型,这不是用户!据我所知,没有用户作为混合类型的形状,因为定义的唯一形状是用于 Shape[Column[T], T, Column[T]] 和表 T <: tablenode shape nothingcontainer t slick.lifted.shape>
(for {
us <- UserSkills
u <- us.user
s <- us.skill
} yield (u,s))
.groupBy(_._1.id)
.map { case (_, xs) => xs.map(_._1.id).first }
我收到“NoSuchElementException:找不到键:@1515100893”形式的奇怪错误,其中数字键值每次都会更改。这不是我想要的查询,但它仍然是一个奇怪的问题。
【问题讨论】:
-
我最终在不得不加入多个表的情况下编写了直接查询。顺便说一句,性能更好的变体(假设 u.id、s.id、us.userId 和 us.skillId 是键)可能是:“SELECT u.*, group_concat(s.name) FROM user_skill us JOIN Skills s ON us.skillId = s.id 加入用户 u ON us.userId = u.id GROUP BY u.id"
-
但是无论如何,当你分组时,你会得到一个 Map(id -> List((u,s)) ,这并不是你在 { case } 表达式中描述的方式。