我正在尝试使用Slick查询多对多关系,但我遇到了各种错误,最突出的是"不知道如何解压缩(用户,技能)到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)
}
Run Code Online (Sandbox Code Playgroud)
最终,我想要实现的是某种形式
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
Run Code Online (Sandbox Code Playgroud)
但在我花时间试图让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
Run Code Online (Sandbox Code Playgroud)
我已经尝试了各种scala代码来生成此查询,但是导致上述形状错误的一个示例是
(for {
us <- UserSkills
user <- us.user
skill <- us.skill
} yield (user, skill)).groupBy(_._1.id).map { case(_, xs) => xs.first }
Run Code Online (Sandbox Code Playgroud)
类似地,以下产生关于"用户"而不是"(用户,技能)"的打包错误
(for {
us <- UserSkills
user <- us.user
skill <- us.skill
} yield (user, skill)).groupBy(_._1.id).map { case(_, xs) => xs.map(_._1).first }
Run Code Online (Sandbox Code Playgroud)
如果有人有任何建议,我将非常感激:我已经花了大量的今天和昨天搜索谷歌/谷歌组以及光滑的来源,但我还没有解决方案.
(另外,我正在使用postgre,所以group_concat实际上是string_agg)
编辑
因此,当使用groupBy时,似乎应用了映射投影,因为类似于
(for {
us <- UserSkills
u <- us.user
s <- us.skill
} yield (u,s)).map(_._1)
Run Code Online (Sandbox Code Playgroud)
工作正常,因为_._ 1给出类型Users,它有一个Shape,因为Users是一个表.但是,当我们调用xs.first时(就像我们调用groupBy时那样),我们实际上会返回一个映射投影类型(User,Skill),或者如果我们先应用map(_._ 1),我们会得到User类型,这不是用户!据我所知,没有使用User作为混合类型的形状,因为定义的唯一形状是Shape [Column [T],T,Column [T]]和表T <:TableNode,Shape [T ,nothingContainer#TableNothing,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 }
Run Code Online (Sandbox Code Playgroud)
我收到一个奇怪的错误,形式为"NoSuchElementException:key not found:@ 1515100893",其中数字键值每次都会改变.这不是我想要的查询,但这是一个奇怪的问题.
我也遇到过类似的情况。虽然我喜欢使用 Scala 和 Slick,但我确实相信有时对数据库本身中的对象进行非规范化并将 Slick Table 链接到视图会更容易。
例如,我有一个应用程序,它有一个 Tree 对象,该对象被规范化为多个数据库表。由于我对 SQL 很熟悉,因此我认为这是比编写简单的 Scala Slick 查询更干净的解决方案。斯卡拉代码:
case class DbGFolder(id: String,
eTag: String,
url: String,
iconUrl: String,
title: String,
owner: String,
parents: Option[String],
children: Option[String],
scions: Option[String],
created: LocalDateTime,
modified: LocalDateTime)
object DbGFolders extends Table[DbGFolder]("gfolder_view") {
def id = column[String]("id")
def eTag = column[String]("e_tag")
def url = column[String]("url")
def iconUrl = column[String]("icon_url")
def title = column[String]("title")
def owner = column[String]("file_owner")
def parents = column[String]("parent_str")
def children = column[String]("child_str")
def scions = column[String]("scion_str")
def created = column[LocalDateTime]("created")
def modified = column[LocalDateTime]("modified")
def * = id ~ eTag ~ url ~ iconUrl ~ title ~ owner ~ parents.? ~
children.? ~ scions.? ~ created ~ modified <> (DbGFolder, DbGFolder.unapply _)
def findAll(implicit s: Session): List[GFolder] = {
Query(DbGFolders).list().map {v =>
GFolder(id = v.id,
eTag = v.eTag,
url = v.url,
iconUrl = v.iconUrl,
title = v.title,
owner = v.owner,
parents = v.parents.map { parentStr =>
parentStr.split(",").toSet }.getOrElse(Set()),
children = v.children.map{ childStr =>
childStr.split(",").toSet }.getOrElse(Set()),
scions = v.scions.map { scionStr =>
scionStr.split(",").toSet }.getOrElse(Set()),
created = v.created,
modified = v.modified)
}
}
}
Run Code Online (Sandbox Code Playgroud)
以及底层(postgres)视图:
CREATE VIEW scion_view AS
WITH RECURSIVE scions(id, scion) AS (
SELECT c.id, c.child
FROM children AS c
UNION ALL
SELECT s.id, c.child
FROM children AS c, scions AS s
WHERE c.id = s.scion)
SELECT * FROM scions ORDER BY id, scion;
CREATE VIEW gfolder_view AS
SELECT
f.id, f.e_tag, f.url, f.icon_url, f.title, m.name, f.file_owner,
p.parent_str, c.child_str, s.scion_str, f.created, f.modified
FROM
gfiles AS f
JOIN mimes AS m ON (f.mime_type = m.name)
LEFT JOIN (SELECT DISTINCT id, string_agg(parent, ',' ORDER BY parent) AS parent_str
FROM parents GROUP BY id) AS p ON (f.id = p.id)
LEFT JOIN (SELECT DISTINCT id, string_agg(child, ',' ORDER BY child) AS child_str
FROM children GROUP BY id) AS c ON (f.id = c.id)
LEFT JOIN (SELECT DISTINCT id, string_agg(scion, ',' ORDER BY scion) AS scion_str
FROM scion_view GROUP BY id) AS s ON (f.id = s.id)
WHERE
m.category = 'folder';
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1444 次 |
最近记录: |