2013-06-07 5 views
10

Slick을 사용하여 다 대다 관계를 쿼리하려고하지만 다양한 오류가 발생합니다. 가장 눈에 띄는 것은 " T (사용자, 기술)을 풀고 G "로 포장하십시오.Scala Slick : groupBy 문제 및 모양 누락

테이블의 구조는 다음과 유사합니다 궁극적으로

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 

내가이 쿼리를 생성하는 스칼라 코드의 다양한 시도했지만, 형상 오류 위의 원인이 무엇의 예는 유사

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

, 나는 매우 감사하게 될 것입니다 해결책은 아직 없습니다.

(GROUP_CONCAT 실제로 string_agg 것, 그래서 또한, 나는 postgre을 사용하고 있습니다)

편집

그래서 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 <에 대해서만 정의 된 모양이 있기 때문에 혼합 유형으로 User는 모양이 없습니다. 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을 1515100893 @", 숫자 키 값마다 변경 곳. 이것은 내가 원하는 쿼리가 아니지만 그다지 이상한 문제는 아닙니다.

+0

여러 테이블에 가입해야하는 상황에서 직접 쿼리를 작성했습니다. 그건 그렇고, 더 나은 공연 변종 (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.id에 가입하십시오 .userId = u.id GROUP BY u.id " – Ashalynd

+0

하지만 어쨌든, 그룹화하면 맵 (id -> List ((u, s))을 얻을 수 있습니다. {case} 표현식에 정확히 어떻게 기술되어있는지는 아닙니다. – Ashalynd

답변

1

비슷한 상황에서도 실행되었습니다. 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) 
    } 
    } 
} 

그리고 기본 (포스트 그레스)보기 :

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

이보십시오. 기대했던 것을 얻을 수 있기를 바랍니다. 케이스 클래스 아래의 매끄러운 코드를 찾으십시오.

click here 리프팅 포함에 관한 내용입니다.

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)