2013-06-07 20 views
10

Próbuję użyć Slicka do sprawdzenia relacji wiele do wielu, ale mam do czynienia z różnymi błędami, z których najważniejsza to "Nie wiem jak rozpakować (User, Skill) do T i spakować do G ".Scala Slick: Problemy z groupBy i brakującymi kształtami

Struktura tabel jest podobny do poniższego:

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) 
} 

Ostatecznie, co chcę osiągnąć coś formy

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 

ale zanim spędzić czas próbuje group_concat do pracy, próbowałem utworzyć prostsze zapytanie (które, jak uważam, jest nadal aktualne ...)

SELECT u.* FROM user_skill us, users u, skills s WHERE us.skillId = s.id && us.userId = u.id GROUP BY u.id 

Próbowałem różnych kodu scala do wytworzenia tej kwerendy, ale przykładem tego, co powoduje błąd kształt powyżej

(for { 
    us <- UserSkills 
    user <- us.user 
    skill <- us.skill 
} yield (user, skill)).groupBy(_._1.id).map { case(_, xs) => xs.first } 

Podobnie dodaje produkuje opakowania błąd odnośnie „user” zamiast „(użytkownik, Zręcznościowe)”

(for { 
    us <- UserSkills 
    user <- us.user 
    skill <- us.skill 
} yield (user, skill)).groupBy(_._1.id).map { case(_, xs) => xs.map(_._1).first } 

Jeśli ktoś ma jakieś sugestie, byłbym bardzo wdzięczny: spędziłem większość wczoraj i szorowania grup google/google jak również zręczny źródła, ale nie mam jeszcze rozwiązanie.

(Również używam Postgre tak GROUP_CONCAT byłby rzeczywiście string_agg)

EDIT

Więc wydaje się, gdy jest używany GroupBy, wyznaczoną projekcja zostanie zastosowane, ponieważ coś

(for { 
    us <- UserSkills 
    u <- us.user 
    s <- us.skill 
} yield (u,s)).map(_._1) 

działa poprawnie, ponieważ _._ 1 daje typ Użytkownicy, którzy mają Kształt, ponieważ Użytkownicy są tabelą. Jednakże, kiedy wywołujemy xs.first (tak jak to robimy, gdy nazywamy groupBy), faktycznie otrzymujemy odwzorowany typ rzutowania (User, Skill), lub jeśli zastosujemy najpierw mapę (_._ 1), otrzymamy typ User, który nie jest Użytkownikiem! O ile wiem, nie ma kształtu z użytkownikiem jako typem mieszanym, ponieważ jedyne zdefiniowane kształty dotyczą kształtu [kolumna [T], T, kolumna [T]] oraz tabeli T <: węzeł tabeli, kształt [T , NothingContainer # TableNothing, T] jak zdefiniowano w slick.lifted.Shape. Ponadto, jeśli zrobię coś podobnego

(for { 
    us <- UserSkills 
    u <- us.user 
    s <- us.skill 
} yield (u,s)) 
    .groupBy(_._1.id) 
    .map { case (_, xs) => xs.map(_._1.id).first } 

mam dziwny błąd w postaci „NoSuchElementException: klucz nie został znaleziony: @ 1515100893”, gdzie wartość klucz numeryczny zmienia się za każdym razem. Nie jest to zapytanie, które chcę, ale jest to jednak dziwna kwestia.

+0

Skończyło się na pisaniu bezpośredniego zapytania w sytuacji, gdy musiałem dołączyć do wielu tabel. Nawiasem mówiąc, lepszym wariantem wydajności (pod warunkiem, że u.id, s.id, us.userId i us.skillId są kluczami) mogą być: "SELECT u. *, Group_concat (s.name) FROM user_skill nas DOŁĄCZ DO SKŁADANIA umiejętności s ON us.skillId = s.id JOIN users u ON us.userId = u.id GROUP BY u.id " – Ashalynd

+0

Ale w każdym razie, kiedy grupujesz, dostajesz mapę (id -> lista ((u, s)) i to jest nie dokładnie, jak jest to opisane w twoim {case} wyrażeniu. – Ashalynd

Odpowiedz

1

Występuję również przeciwko podobnym sytuacjom. Chociaż uwielbiam pracować ze Scala i Slick, uważam, że są chwile, kiedy łatwiej jest zdenormalizować obiekt w samej bazie danych i połączyć Slick Table do widoku.

Na przykład mam aplikację, która ma obiekt drzewa, który jest znormalizowany na kilka tabel bazy danych. Ponieważ czuję się komfortowo z SQL, myślę, że jest to rozwiązanie czystsze niż pisanie zwykłego zapytania Scala Slick.Kod Scala:

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) 
    } 
    } 
} 

i podstawowego (postgres) Widok:

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'; 
2

Spróbuj tego. Mam nadzieję, że przyniesie to, czego się spodziewałeś. Znajdź świetny kod pod klasami przypadków.

click here za odniesienie w odniesieniu do osadzania z uniesionym dachem.

case class User(val name: String, val picture: Option[URL], val id: Option[UUID]) 
      class Users(_tableTag: Tag) extends Table[User](_tableTag,"users") { 
       def name = column[String]("name") 
       def picture = column[Option[URL]]("picture") 
       def id = column[UUID]("id") 
       def * = name ~ picture ~ id.? <> (User, User.unapply _) 
      } 
      lazy val userTable = new TableQuery(tag => new Users(tag)) 

      case class Skill(val name: String, val id: Option[UUID]) 
      class Skills(_tableTag: Tag) extends Table[Skill](_tableTag,"skill") { 
       def name = column[String]("name") 
       def id = column[UUID]("id") 
       def * = name ~ id.? <> (Skill, Skill.unapply _) 
      } 
      lazy val skillTable = new TableQuery(tag => new Skills(tag)) 

      case class UserSkill(val userId: UUID, val skillId: UUID, val id: Option[UUID]) 
      class UserSkills(_tableTag: Tag) extends Table[UserSkill](_tableTag,"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) 
      } 
      lazy val userSkillTable = new TableQuery(tag => new UserSkills(tag)) 






(for {((userSkill, user), skill) <- userSkillTable join userTable.filter on 
        (_.userId === _.id) join skillTable.filter on (_._1.skillId === _.id) 
       } yield (userSkill, user, skill)).groupBy(_.2.id)