adheus
adheus

Reputation: 4045

Getting tuple of objects and list from Slick 2 query

I have this query that should get a list of message with its sender info and their list of recipients (consider groupID and senderID function parameters):

(for {
    groupUsers <- Users.users if targets.groupID === groupID
    mRecipients <- recipients if mRecipients.targetID === groupUsers.id
    message <- messages if message.senderID === groupUsers.id || message.id === mRecipients.messageID
    sender <- Users.users if sender.id === m.senderID
} yield (message, sender ,mRecipients)).list.map{ result => new FullMessage(result ._1, result ._2, result ._3) }

But as result I got a tuple of (Message, User, Recipient) when I wanted to have a tuple of (Message, User, List[Recipient]). How do I manage to get such result?

Thank you all.

Upvotes: 0

Views: 390

Answers (1)

sarveshseri
sarveshseri

Reputation: 13985

Well... the code will produce the output according to what you wrote and not according to what you want. Focus on this part...

yield (message, sender ,mRecipients)

You are yielding a tuple of (message, sender, mRecipents)

And you did this inside the for comprehension

mRecipients <- recipients if mRecipients.targetID === targetID
message <- messages if message.senderID === senderID || message.id === mRecipients.messageID
sender <- Users.users if sender.id === m.senderID

Here each of the expressions like recipients if mRecipients.targetID === targetID evaluates to a Query monad.

To understand whats wrong with this, It is very similar to following and lets think what happens when we do this,

scala>:pa
// Entering paste mode (ctrl-D to finish

for{
  i <- List( 1, 2, 3 )
  j <- List( 1, 2, 3 )
  k <- List( 1, 2, 3 )
} yield( i, j, k )

// Exiting paste mode, now interpreting.

res1: List[(Int, Int, Int)] = List((1,1,1), (1,1,2), (1,1,3), (1,2,1),
(1,2,2), (1,2,3), (1,3,1), (1,3,2), (1,3,3), (2,1,1), (2,1,2), (2,1,3),
(2,2,1), (2,2,2), (2,2,3), (2,3,1), (2,3,2), (2,3,3), (3,1,1), (3,1,2),
(3,1,3), (3,2,1), (3,2,2), (3,2,3), (3,3,1), (3,3,2), (3,3,3))

In simple terms, for comprehensions do something similar to unwraping the monad and get the iterator of the values inside it... and then it wraps the yielded value into the monad again.

So, if you do soemthing like,

scala>:pa
// Entering paste mode (ctrl-D to finish

for{
  l <- Some( List( 1, 2, 3 ) )
  i <- Some( 1 )
} yield ( l, i ) 

// Exiting paste mode, now interpreting.
res1: Option[(List[Int], Int)] = Some((List(1, 2, 3),1))

Also... Your data model feels kind of wrong.

What is recipients ?? Tell me that and may be I will be able to solve your problem in a better and efficient way.

But considering what I can guess about your data model, now if you want list of recipients the right way, you will have to do something like,

// List[ ( Message, List[ Recipients] ) ]
val messageAndRecipentsList = ( for {

  // First get all possible recipents
  possibleRecipient <- recipients if possibleRecipient.targetID === targetID

  // Now get all relevant messages for sender and possibleRecipeint
  message <- messages if message.senderID === senderID || message.id === possibleRecipient.messageID

  // Now get list of recipents for each of these messages
  messageRecipients <- for {
    messageRecipient <- recipients if messageRecipient.messageID === message.id
  } yield ( messageRecipient ).list

} yield ( message, messageRecipients ) ).list

// Now lets add sender for each of these messages, I am guessing sender
// should be exactly one for each sendorId. So... instead of querying for
// same user for every message lets just query it once and add to every
// tuple of the above list,

val sender = ( for {
  sender <- Users.users if sender.id === senderID
} yield sender ).list.head


val finalList = messageAndRecipentsList.map ( {
  case ( message, msaageRecipents ) => new FullMessage( message, sender, messageRecipients )
} )

Upvotes: 1

Related Questions