sql - Translate nested join and groupby query to Slick 3.0 -


i'm implementing todo list. user can have multiple lists , list can have multiple users. want able retrieve lists user, each of these lists contain list of users it's shared (including owner). not succeeding implementing query.

the table definitions:

case class dblist(id: int, uuid: string, name: string) class lists(tag: tag) extends table[dblist](tag, "list") {   def id = column[int]("id", o.primarykey, o.autoinc) // primary key column   def uuid = column[string]("uuid")   def name = column[string]("name")   // every table needs * projection same type table's type parameter   def * = (id, uuid, name) <> (dblist.tupled, dblist.unapply) } val lists = tablequery[lists]  case class dbuser(id: int, uuid: string, email: string, password: string, firstname: string, lastname: string) // shared user projection, data of other users user shared item can see case class dbshareduser(id: int, uuid: string, email: string, firstname: string, lastname: string, provider: string) class users(tag: tag) extends table[dbuser](tag, "user") {   def id = column[int]("id", o.primarykey, o.autoinc) // primary key column   def uuid = column[string]("uuid")       def email = column[string]("email")       def password = column[string]("password")       def firstname = column[string]("first_name")       def lastname = column[string]("last_name")       def * = (id, uuid, email, password, firstname, lastname) <> (dbuser.tupled, dbuser.unapply)   def shareduser = (id, uuid, email, firstname, lastname) <> (dbshareduser.tupled, dbshareduser.unapply) } val users = tablequery[users]   // relation n:n user-list case class dblisttouser(listuuid: string, useruuid: string) class listtousers(tag: tag) extends table[dblisttouser](tag, "list_user") {   def listuuid = column[string]("list_uuid")   def useruuid = column[string]("user_uuid")       def * = (listuuid, useruuid) <> (dblisttouser.tupled, dblisttouser.unapply)   def pk = primarykey("list_user_unique", (listuuid, useruuid))  } val listtousers = tablequery[listtousers] 

i created additional class hold database list object + users, goal map query result somehow instances of class.

case class dblistwithsharedusers(list: dblist, sharedusers: seq[dbshareduser]) 

this sql query of it, gets first lists user (inner query) join of lists list_user user in order rest of data , users each list, filters inner query. doesn't contain group part

select * list inner join list_user on list.uuid=list_user.list_uuid inner join user on user.uuid=list_user.user_uuid list.uuid in (   select (list_uuid) list_user user_uuid=<myuseruuuid> ); 

i tested , works. i'm trying implement in slick i'm getting compiler error. don't know if structure in part correct, haven't been able come better one.

def findlists(user: user) = {   val listsusersjoin = listtousers join lists join users on {     case ((listtouser, list), user) =>       listtouser.listuuid === list.uuid &&       listtouser.useruuid === user.uuid   }    // lists user (corresponds inner query in above sql)   val querytogetlistsforuser = listtousers.filter(_.useruuid===user.uuid)    // map uuids   val querytogetlistsuuidsforuser: query[rep[string], string, seq] = querytogetlistsforuser.map { ltu => ltu.listuuid }    // create query mirrors sql above (problems):   val querytogetlistswithsharedusers = (for {     listsuuids <- querytogetlistsuuidsforuser     ((listtousers, lists), users) <- listsusersjoin     if lists.uuid.inset(listsuuids) // error because inset requires traversable , passing listtousers   } yield (lists))    // group - doesn't compile because above doesn't compile:   querytogetlistswithsharedusers.groupby {case (list, user) =>     list.uuid   } ... } 

how can fix this?

thanks in advance

edit:

i put emergency solution (at least compiles), execute query using raw sql , grouping programmatically, looks this:

case class resulttmp(listid: int, listuuid: string, listname: string, userid:int, useruuid: string, useremail: string, userfirstname: string, userlastname: string, provider: string)  implicit val getlistresult = getresult(r => resulttmp(r.nextint, r.nextstring, r.nextstring, r.nextint, r.nextstring, r.nextstring, r.nextstring, r.nextstring, r.nextstring))  val = sql"""select (list.id, list.uuid, list.name, user.id, user.uuid, user.email, user.first_name, user.last_name, user.provider) list inner join list_user on list.uuid=list_user.list_uuid inner join user on user.uuid=list_user.user_uuid list.uuid in (   select (list_uuid) list_user user_uuid=${user.uuid} );""".as[resulttmp]  val result: future[vector[resulttmp]] = db.run(a)  val res: future[seq[dblistwithsharedusers]] = result.map {resultstmp =>   val mymap: map[string, vector[resulttmp]] = resultstmp.groupby { resulttmp => resulttmp.listuuid }    val r: iterable[dblistwithsharedusers] = mymap.map {case (listuuid, resultstmp) =>      val first = resultstmp(0)      val list = dblist(first.listid, listuuid, first.listname)      val users: seq[dbshareduser] = resultstmp.map { resulttmp =>        dbshareduser(resulttmp.userid, resulttmp.useruuid, resulttmp.useremail, resulttmp.userfirstname, resulttmp.userlastname, resulttmp.provider)     }      dblistwithsharedusers(list, users)   }    r.toseq } 

but that's horrible, how working normal way?

edit 2:

i'm experimenting monadic joins stuck here. example lists given user:

val listsusersjoin = {     list <- lists     listtouser <- listtousers      user_ <- users if user_.uuid === user.uuid  } yield (list.uuid, list.name, user.uuid, user.firstname ...) 

but not enough because need users lists, need 2 queries. need first lists user , find users lists, like:

val querytogetlistsforuser = listtousers.filter(_.useruuid===user.uuid)  val listsusersjoin = {     list <- lists     listtouser <- listtousers      user_ <- users /* if list.uuid in querytogetlistsforuser result  */ } yield (list.uuid, list.name, user.uuid, user.firstname ... ) 

but don't know how pass join. i'm not sure if groupby, @ least @ database level correct, far see used aggregate results single value, count or avg. need them in collection.

edit 3:

i don't know yet if right monadic join may path solution. compiles:

val listsusersjoin = {   listtouser <- listtousers if listtouser.useruuid === user.uuid   // lists user   list <- lists if list.uuid === listtouser.listuuid  // join list   listtouser2 <- listtousers if list.uuid === listtouser.listuuid // users lists     user_ <- users if user_.uuid === listtouser2.useruuid // join user  } yield (list.uuid, list.name, user.uuid, user.email, user.firstname, user.lastname) 

ah, @ that, came solution. still have test if works @ least compiler stopped shouting @ it. i’ll edit later if necessary.

val listsusersjoin = {   listtouser <- listtousers if listtouser.useruuid === user.uuid     list <- lists if list.uuid === listtouser.listuuid    listtouser2 <- listtousers if list.uuid === listtouser.listuuid   user_ <- users if user_.uuid === listtouser2.useruuid  } yield (list.id, list.uuid, list.name, user_.id, user_.uuid, user_.email, user_.firstname, user_.lastname, user_.provider)  val grouped = listsusersjoin.groupby(_._2)  val resultfuture = db.run(grouped.result).flatmap {groupedresults =>    val futures: seq[future[dblistwithsharedusers]] = groupedresults.map {groupedresult =>      val listuuid = groupedresult._1     val valuequery = groupedresult._2      db.run(valuequery.result).map {valueresult =>          val first = valueresult(0) // if there's grouped result should never empty           val list = dblist(first._1, listuuid, first._3)                  val users = valueresult.map {value =>             dbshareduser(value._4, value._5, value._6, value._7, value._8, value._9)         }          dblistwithsharedusers(list, users)     }   }   future.sequence(futures) } 

Comments

Popular posts from this blog

python - No exponential form of the z-axis in matplotlib-3D-plots -

php - Best Light server (Linux + Web server + Database) for Raspberry Pi -

c# - "Newtonsoft.Json.JsonSerializationException unable to find constructor to use for types" error when deserializing class -