This code is creating a query for use in retrieving a user's profile on a web back end. It creates a query that assembles the necessary information into a DTO (which is just a case class) that is subsequently sent back as JSON.
def getProfile(userId: Long)={
val q = for{
((((u,p),a),b), ba) <- filterById(userId) join
People on (_.personId === _.id) joinLeft
Addresses on (_._2.addressId === _.id) joinLeft
Businesses on (_._1._2.businessId === _.id) joinLeft
Addresses on (_._2.flatMap(_.addressId) === _.id)
}yield(p, a, b, ba)
db.run(q.result.headOption).map{ _.map{case(p,a,b,ba) =>
val business = b match {
case Some(b) => Some(dtos.Business(b.name, b.abn, b.adminFee, ba, b.id))
case _ => None
}
dtos.ProfileInfo(p, a, business)
}}
}
I've included the result processing (db.run(...)
) for context only.
I'm looking for a more readable way to express the query construction.
My experience reading this is "Wait, what?? ... on (_._1._2.flatMap(_.addressId)
.... what is that doing?? Why flatmap there and not here: on (_._1._2.businessId
. These are actually straight forwards things, but don't read at all straight fowards.
I'm looking for a way of expressing this that doesn't require the amount of deduction needed to read this version. I have to "deduce" what _._1._2 is, and why it needs to be flattened, which I don't have to do with the equivalent SQL.
Notes:
- This code comes from an existing application (not written by me) which I am extending.
- Users, People, Addresses, Businesses are (obviously?) the tables.
- People and Businesses have Addresses.
- Users have a Person(*), People have a Business
filterByUserId(userId)
is basically equivalent toUsers.filter(_.id === userId
)The equivalent SQL is:
select p.*, a1.*, b.*, a2.* from Users u innerJoin People p on (u.personId == p.id) leftJoin Addresses a1 on (p.addressId == a1.id) leftJoin Businesses b on (p.businessId == b.id) leftJoin Addresses a2 on ( b.addressId == a2.id)
You should experiment with something like this:
The other solution would be to make joins without using for comprehension, so you wouldn't have to use underscores to extract values from tuples:
You haven't provided full definitions of your data so I wasn't able to fully test those solutions, but this should give you some insight into a different way of defining joins in Slick. Let me know if this was helpful at all.