golang unmarshal using db tags - sql

I have a sql query which fetches the list of cities nested inside provinces nested inside countries
SELECT
C.*,
P.provinces
FROM
countries AS C
LEFT JOIN (
SELECT
P.country_id,
json_agg(json_build_object(
'id', P.id,
'name', P.name,
'slug', P.slug,
'cities', Ci.cities
)) AS provinces
FROM
provinces AS P
LEFT JOIN (
SELECT
Ci.province_id,
json_agg(json_build_object(
'id', Ci.id,
'name', Ci.name,
'slug', Ci.slug
)) AS cities
FROM
cities AS Ci
GROUP BY Ci.province_id
) AS Ci ON Ci.province_id = P.id
GROUP BY P.country_id
) AS P ON P.country_id = C.id
I am fetching this data into slice of countries
type Country struct {
Id int64 `json:"id" db:"id"`
ISOCode2 string `json:"isoCode2" db:"iso_code_2"`
ISOCode3 string `json:"isoCode3" db:"iso_code_3"`
ISONumCode string `json:"isoNumCode" db:"iso_num_code"`
Name string `json:"name" db:"name"`
Slug string `json:"slug" db:"slug"`
Provinces SliceProvince `json:"provinces" db:"provinces"`
}
type SliceProvince []Province
func (provinces *SliceProvince) Scan(src any) (err error) {
if src == nil {
return
}
var source []byte
switch src := src.(type) {
case []byte:
source = src
case string:
source = []byte(src)
default:
return fmt.Errorf("unsupported type in scan:%v", src)
}
err = json.Unmarshal(source, provinces)
return
}
type Province struct {
Id int64 `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Slug string `json:"slug" db:"slug"`
Cities SliceCity `json:"cities" db:"cities"`
}
type SliceCity []City
func (cities *SliceCity) Scan(src any) (err error) {
if src == nil {
return
}
var source []byte
switch src := src.(type) {
case []byte:
source = src
case string:
source = []byte(src)
default:
return fmt.Errorf("unsupported type in scan")
}
err = json.Unmarshal(source, cities)
return
}
type City struct {
Id int64 `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Slug string `json:"slug" db:"slug"`
}
Now, my main query is that in the scan methods for these models, I want to do unmarshalling using db tags instead of json tags. Is there any workaround which I can do for this.
I came up with a thought of unmarshalling it to a map, then change their keys from db tag ones to json tag ones, then marshal and unmarshal it to the corresponding struct. But when there would be nesting of models, or slices involved, that would increase the complexity

Related

Scan a PostgreSQL field (of ARRAY type) into a slice of Go structs

Let's say I have:
type User struct {
ID int64 `json:"id"
Posts []Post `json:"posts"
}
type Post struct {
ID int64 `json:"id"
Text string `json:"text"
}
The SQL query:
WITH temp AS (SELECT u.id AS user_id, p.id AS post_id, p.text AS post_text FROM users u JOIN posts p ON u.id=p.user_id)
SELECT user_id, ARRAY_AGG(ARRAY[post_id::text, post_text])
FROM temp
GROUP BY user_id
)
What I want is to scan rows from the query above into a slice of User objects:
import (
"context"
"fmt"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/lib/pq"
)
var out []User
rows, _ := client.Query(context.Background(), query) // No error handling for brevity
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, pq.Array(&u.Posts)); err != nil {
return
}
out = append(out, u)
}
Pretty much expectedly, the code above fails with:
pq: cannot convert ARRAY[4][2] to StringArray
This makes sense, but is there a way to read the SQL output into my slice of users?
Scanning of multi-dimensional arrays of arbitrary types, like structs, is not supported by lib/pq. If you want to scan such an array you'll have to parse and decode it yourself in a custom sql.Scanner implementation.
For example:
type PostList []Post
func (ls *PostList) Scan(src any) error {
var data []byte
switch v := src.(type) {
case string:
data = []byte(v)
case []byte:
data = v
}
// The data var holds the multi-dimensional array value,
// something like: {{"1","foo"}, {"2","bar"}, ...}
// The above example is easy to parse but too simplistic,
// the array is likely to be more complex and therefore
// harder to parse, but not at all impossible if that's
// what you want.
return nil
}
If you want to learn more about the PostgreSQL array representation syntax, see:
Array Input and Output Syntax
An approach that does not require you to implement a parser for PostgreSQL arrays would be to build and pass JSON objects, instead of PostgreSQL arrays, to array_agg. The result of that would be a one-dimensional array with jsonb as the element type.
SELECT user_id, array_agg(jsonb_build_object('id', post_id, 'text', post_text))
FROM temp
GROUP BY user_id
Then the implementation of the custom sql.Scanner just needs to delegate to lib/pq.GenericArray and another, element-specific sql.Scanner, would delegate to encoding/json.
type PostList []Post
func (ls *PostList) Scan(src any) error {
return pq.GenericArray{ls}.Scan(src)
}
func (p *Post) Scan(src any) error {
var data []byte
switch v := src.(type) {
case string:
data = []byte(v)
case []byte:
data = v
}
return json.Unmarshal(data, p)
}
type User struct {
ID int64 `json:"id"`
Posts PostList `json:"posts"`
}

How to parse a Json with Go

Have tis code:
type Foo struct {
field string
Value string
}
type Guyy struct {
info Foo
}
func main() {
GuyJson := `{"info":[{"field":"phone","value":"11111-11111"},{"field":"date","value":"05072001"},{"field":"nationality","value":"american"},{"field":"dni","value":"000012345"}]}`
var Guy Guyy
json.Unmarshal([]byte(GuyJson), &Guy)
fmt.Printf("%+v", Guy)
}
When compile I get
{info:{field: Value:}}
How can I get the field and value of nationality?
Change struct to slice (info represent list of structs)
You struct field must be exported (Marshal, Unmarshal, A Tour of GO)
Struct values encode as JSON objects. Each exported struct field becomes a member of the object, using the field name as the object key, unless the field is omitted...
type Guyy struct {
Info []Foo // 👈 exported slice
}
PLAYGROUND
var Guy Guyy
var f interface{}
json.Unmarshal([]byte(GuyJson), &f)
m := f.(map[string]interface{})
foomap := m["info"]
v := foomap.([]interface{})
for _, fi := range v {
vi := fi.(map[string]interface{})
var f Foo
f.Field = vi["field"].(string)
f.Value = vi["value"].(string)
if f.Field == "nationality" {
fmt.Println(f.Field, "was found to be", f.Value)
}
Guy.Info = append(Guy.Info, f)
}
fmt.Println(Guy)
Refer this link for complete code -> https://play.golang.org/p/vFgpE0GNJ7K

Filtering and sorting an SQL query to recreate a nested struct

I am new to Go and I'm trying to populate a struct called Reliefworker from an SQL query which I can send as a JSON payload.
Essentially I have a Reliefworker who may be assigned to many communities and the community can consist of multiple regions.
I suspect there is a clever way of doing this other than what I intend which is a primitive solution that will add a sort to the SQL (by Community) and create a function that detects if the community being added is different to the previous one, in which case I would create a new community struct type object to be appended.
type Reliefworker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
Deployment_id string `json:"deployment_id"`
}
type Community struct{
Name string `json:"name"`
community_id string `json:"community_id"`
Regions []Region `json:"regions"`
}
type Region struct{
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant string `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
Currently I have created a struct that reflects what I am actually getting from SQL whilst pondering my next move. Perhaps this might be a good stepping stone instead of attempting an on-the-fly transformation ?
type ReliefWorker_community_region struct {
Deployment_id string
Community_title int
Region_name string
Reconstruction_grant int
}
func GetReliefWorkers(deployment_id string) []Reliefworker {
fmt.Printf("Confirm I have a deployment id:%v\n", deployment_id)
rows, err := middleware.Db.Query("select deployment_id, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", brand_id)
if err != nil {
return
}
for rows.Next() {
reliefworker := Reliefworker{}
err = rows.Scan(&deployment_id, &community_title, &region_name, &reconstruction_grant)
if err != nil {
return
}
}
rows.Close()
return
}
I think a sort makes a lot of sense, primitive solutions can be the most efficient:
func GetReliefWorkers(deployment_id string) []Reliefworker {
// Added sort to query
q := "select worker_name, community_title, region_name, reconstruction_grant WHERE deployment_id=? ORDER BY community_title"
rows, err := middleware.Db.Query(q, deployment_id)
if err != nil {
return
}
defer rows.Close() // So rows get closed even on an error
c := Community{} // To keep track of the current community
cmatrix := [][]string{[]string{}} // Matrix of communities and workers
communities := []Community{} // List of communities
workers := make(map[string]Reliefworker) // Map of workers
var ccount int // Index of community in lists
for rows.Next() {
w := Reliefworker{Deployment_id: deployment_id}
r := Region{}
var ctitle string // For comparison later
err = rows.Scan(&w.Name, &ctitle, &r.Name, &r.Reconstruction_grant)
if err != nil {
return
}
if ctitle != c.Name {
communities = append(communities, c)
c = Community{}
c.Name = ctitle
ccount++
cmatrix = append(cmatrix, []string{})
}
c.Regions = append(c.Regions, r)
cmatrix[ccount] = append(cmatrix[ccount], w.Name)
workers[w.Name] = w
}
for i, c := range communities {
for _, id := range cmatrix[i] {
w := workers[id] // To avoid error
w.Communities = append(w.Communities, c)
workers[id] = w
}
}
out := []Reliefworker{}
for _, w := range workers {
out = append(out, w)
}
return out
}
Though it might make even more sense to create seperate tables for communities, regions, and workers, then query them all with a JOIN: https://www.w3schools.com/sql/sql_join_inner.asp
UPDATE: Since you only want to retrieve one Reliefworker, would something like this work?
type ReliefWorker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant int `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
func GetReliefWorkers(deployment_id string) Reliefworker {
reliefworker := Reliefworker{}
communities := make(map[string]Community)
rows, err := middleware.Db.Query("select name, community_title, region_name, region_id, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
defer rows.Close()
for rows.Next() {
c := Community{}
r := Region{}
err = rows.Scan(&reliefworker.Name, &c.Name, &r.Name, &r.Region_id, &r.Reconstruction_grant)
if err != nil {
panic(err)
}
if _, ok := communities[c.Name]; ok {
c = communities[c.Name]
}
c.Regions = append(c.Regions, r)
communities[c.Name] = c
}
for _, c := range commmunities {
reliefworker.Communities = append(reliefworker.Communities, c)
}
return reliefworker
}
Ok, my crude solution doesn't contain a shred of the intelligence #Absentbird demonstrates but I'm here to learn.
#Absentbird I love your use of maps and multidimensional arrays to hold a matrix of communities and workers. I will focus on making this part of my arsenal over the weekend.
I can accept and adapt #Absentbird's solution once I have a solution to why it gives the error "cannot assign to struct field workers[id].Communities in mapcompilerUnaddressableFieldAssign" for the line workers[id].Communities = append(workers[id].Communities, c)
Firstly apologies as I had to correct two things. Firstly I only needed to return ReliefWorkers (not an array of ReliefWorkers). Secondly ReliefWorker struct did not need to contain the Deployment_id since I already knew it.
I am new to Go so I'd really appreciate feedback on what I can do to better leverage the language and write more concise code.
My structs and solution is currently as follows:
type ReliefWorker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant int `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
type ReliefWorker_community_region struct {
Name string
Community_title string
Region_name string
Reconstruction_grant int
}
func GetReliefWorkers(deployment_id string) Reliefworker {
var reliefworker Reliefworker
var communitiesOnly []string
var name string
var allReliefWorkerData []ReliefWorker_community_region
rows, err := middleware.Db.Query("select name, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
for rows.Next() {
reliefWorker_community_region := ReliefWorker_community_region{}
err = rows.Scan(&reliefWorker_community_region.Name, &reliefWorker_community_region.Community_title, &reliefWorker_community_region.Region_name, &reliefWorker_community_region.Reconstruction_grant)
if err != nil {
panic(err)
}
name = reliefWorker_community_region.Name
allReliefWorkerData = append(allReliefWorkerData, reliefWorker_community_region)
communitiesOnly = append(communitiesOnly, reliefWorker_community_region.Community_title) //All communities go in here, even duplicates, will will create a unique set later
}
rows.Close()
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
var unique []string //Use this to create a unique index of communities
for _, v := range communitiesOnly {
skip := false
for _, u := range unique {
if v == u {
skip = true
break
}
}
if !skip {
unique = append(unique, v)
}
}
fmt.Println(unique)
reliefworker.Name = name
var community Community
var communities []Community
for _, v := range unique {
community.Name = v
communities = append(communities, community)
}
// Go through each record from the database held within allReliefWorkerData and grab every region belonging to a Community, when done append it to Communities and finally append that to ReliefWorker
for j, u := range communities {
var regions []Region
for i, v := range allReliefWorkerData {
if v.Community_title == u.Name {
var region Region
region.Name = v.Region_name
region.Reconstruction_grant = v.Reconstruction_grant
regions = append(regions, region)
}
}
communities[j].Regions = regions
}
reliefworker.Communities = communities
return reliefworker
}

Using jsonb_agg/jsonb_build_object to parse to inner structs

Whenever I try to get (select/scan) the Groups (outer struct) along with their Collaborators (inner structs), I'm getting the following error :
// sql: Scan error on column index ..., name "collaborators": unsupported Scan, storing driver.Value type []uint8 into type *[]User
I'm using sqlx (with pgx driver).
the code to fetch from db is :
func (psql *Postgres) GetGroups(someParam string) ([]Group, error) {
groups := []Group{}
err := psql.db.Unsafe().Select(&groups, <the query ...>, someParam)
....
}
type Postgres struct {
db *sqlx.DB
config *config.PostgresDB
timeout time.Duration
}
This is the SQL query :
SELECT groups.id,
groups.title,
JSONB_AGG(JSONB_BUILD_OBJECT(
'id', u.id,
'first_name', u.first_name,
'last_name', u.last_name,
'user_pic_url', u.user_pic_url)) as collaborators
FROM groups
JOIN user_group_permissions p
ON p.group_id = groups.id
JOIN users u
ON u.id = p.user_id
These are the structs :
type Group struct {
Id string `json:"id" db:"id"`
Title string `json:"title" db:"title"`
Collaborators []User `json:"collaborators" db:"collaborators"`
}
type User struct {
Id string `json:"id" db:"id"`
FirstName string `json:"first_name" db:"first_name"`
LastName string `json:"last_name" db:"last_name"`
ProfilePhoto *string `json:"profile_photo" db:"user_pic_url"`
}
I have a simple Group table , a User table and table which represents all users with Permissions to the group :
CREATE TABLE groups (
id int UNIQUE NOT NULL generated always as identity,
title text
)
CREATE TABLE users (
id bigint UNIQUE NOT NULL generated always as identity,
first_name text NOT NULL,
last_name text NOT NULL,
user_pic_url text
)
CREATE TABLE user_group_permissions (
group_id unsigned_int,
user_id unsigned_bigint,
permission unsigned_smallint,
)
CREATE DOMAIN unsigned_smallint AS smallint
CHECK(VALUE >= 0 AND VALUE < 32767);
CREATE DOMAIN unsigned_int AS int
CHECK(VALUE >= 0 AND VALUE < 2147483647);
CREATE DOMAIN unsigned_bigint AS bigint
CHECK(VALUE >= 0 AND VALUE < 9223372036854775807);
import "encoding/json"
type Group struct {
Id string `json:"id" db:"id"`
Title string `json:"title" db:"title"`
Collaborators UserList `json:"collaborators" db:"collaborators"`
}
type User struct {
Id string `json:"id" db:"id"`
FirstName string `json:"first_name" db:"first_name"`
LastName string `json:"last_name" db:"last_name"`
ProfilePhoto *string `json:"profile_photo" db:"user_pic_url"`
}
type UserList []User
func (list *UserList) Scan(src interface{}) error {
var data []byte
switch v := src.(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
return nil // or return some error
}
return json.Unmarshal(data, list)
}

Translate nested join and groupby query to Slick 3.0

I'm implementing a todo list. A user can have multiple lists and a list can have multiple users. I want to be able to retrieve all the lists for a user, where each of these lists contain a list of the users for which it's shared (including the owner). Not succeeding implementing this 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) // This is the primary key column
def uuid = column[String]("uuid")
def name = column[String]("name")
// Every table needs a * projection with the same type as the 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, this is the data of other users which a user who shared an 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) // This is the 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 an additional class to hold the database list object + the users, my goal is to map the query result somehow to instances of this class.
case class DBListWithSharedUsers(list: DBList, sharedUsers: Seq[DBSharedUser])
This is the SQL query for most of it, it gets first all the lists for the user (inner query) then it does a join of lists with list_user with user in order to get the rest of the data and the users for each list, then it filters with the inner query. It doesn't contain the group by part
select * from list inner join list_user on list.uuid=list_user.list_uuid inner join user on user.uuid=list_user.user_uuid where list.uuid in (
select (list_uuid) from list_user where user_uuid=<myUserUuuid>
);
I tested it and it works. I'm trying to implement it in Slick but I'm getting a compiler error. I also don't know if the structure in that part is correct, but haven't been able to come up with a 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
}
// get all the lists for the user (corresponds to inner query in above SQL)
val queryToGetListsForUser = listToUsers.filter(_.userUuid===user.uuid)
// map to uuids
val queryToGetListsUuidsForUser: Query[Rep[String], String, Seq] = queryToGetListsForUser.map { ltu => ltu.listUuid }
// create query that mirrors SQL above (problems):
val queryToGetListsWithSharedUsers = (for {
listsUuids <- queryToGetListsUuidsForUser
((listToUsers, lists), users) <- listsUsersJoin
if lists.uuid.inSet(listsUuids) // error because inSet requires a traversable and passing a ListToUsers
} yield (lists))
// group - doesn't compile because above doesn't compile:
queryToGetListsWithSharedUsers.groupBy {case (list, user) =>
list.uuid
}
...
}
How can I fix this?
Thanks in advance
Edit:
I put together this emergency solution (at least it compiles), where I execute the query using raw SQL and then do the grouping programmatically, it looks like 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 a = sql"""select (list.id, list.uuid, list.name, user.id, user.uuid, user.email, user.first_name, user.last_name, user.provider) from list inner join list_user on list.uuid=list_user.list_uuid inner join user on user.uuid=list_user.user_uuid where list.uuid in (
select (list_uuid) from list_user where 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 just horrible, how do I get it working the normal way?
Edit 2:
I'm experimenting with monadic joins but also stuck here. For example something like this would get all the lists for a given user:
val listsUsersJoin = for {
list <- lists
listToUser <- listToUsers
user_ <- users if user_.uuid === user.uuid
} yield (list.uuid, list.name, user.uuid, user.firstName ...)
but this is not enough because I need the get also all the users for those lists, so I need 2 queries. So I need to get first the lists for the user and then find all the users for those lists, something like:
val queryToGetListsForUser = listToUsers.filter(_.userUuid===user.uuid)
val listsUsersJoin = for {
list <- lists
listToUser <- listToUsers
user_ <- users /* if list.uuid is in queryToGetListsForUser result */
} yield (list.uuid, list.name, user.uuid, user.firstName ... )
But I don't know how to pass that to the join. I'm not even sure if groupBy, at least at database level is correct, so far I see this used only to aggregate the results to a single value, like count or avg. I need them in a collection.
Edit 3:
I don't know yet if this is right but the monadic join may be the path to the solution. This compiles:
val listsUsersJoin = for {
listToUser <- listToUsers if listToUser.userUuid === user.uuid // get the lists for the user
list <- lists if list.uuid === listToUser.listUuid // join with list
listToUser2 <- listToUsers if list.uuid === listToUser.listUuid // get all the users for the lists
user_ <- users if user_.uuid === listToUser2.userUuid // join with user
} yield (list.uuid, list.name, user.uuid, user.email, user.firstName, user.lastName)
Ah, look at that, I came up with a solution. I still have to test if works but at least the compiler stopped shouting at it. I’ll edit this later if necessary.
val listsUsersJoin = for {
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 a grouped result this should never be 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)
}