Я пытаюсь использовать 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, а также доступный источник, но у меня пока нет решения.
(Кроме того, я использую postgre, поэтому group_concat на самом деле будет string_agg)
ИЗМЕНИТЬ
Итак, похоже, что когда используется groupBy, применяется преобразованная проекция, потому что что-то вроде
(for {
us <- UserSkills
u <- us.user
s <- us.skill
} yield (u,s)).map(_._1)
работает отлично, потому что _._ 1 дает тип Users, у которого есть Shape, так как пользователи являются таблицей. Однако, когда мы вызываем xs.first(как мы это делаем, когда вызываем groupBy), мы фактически возвращаем отображаемый проекционный тип (User, Skill), или если мы сначала применяем карту (_._ 1), получаем тип 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 }
Я получаю странную ошибку формы "NoSuchElementException: key not found: @1515100893", где значение числового ключа меняется каждый раз. Это не тот запрос, который я хочу, но это странная проблема. Тем не менее.