ThaDon ThaDon - 2 months ago 15
Scala Question

joinLeft error: constructor cannot be instantiated to expected type

I'm converting some code from Slick 2.1 to 3.0.3, and when I migrated my join from using

leftJoin
to
joinLeft
I'm receiving this error and I'm not sure how to solve it:

[error] ContentRepoLocal.scala:84: constructor cannot be instantiated to expected type;
[error] found : (T1, T2)
[error] required: slick.lifted.Rep[Option[(repo.model.UserContentTable, repo.model.ContentTable)]]
[error] .map { case (u, (uc, c)) => (u, c.optionProjection) }
[error] ^
[error] ContentRepoLocal.scala:84: diverging implicit expansion for type slick.lifted.Shape[_ <: slick.lifted.FlatShapeLevel, Nothing, T, G]
[error] starting with method repColumnShape in trait RepShapeImplicits
[error] .map { case (u, (uc, c)) => (u, c.optionProjection) }


On this code:

def getContentsByUser(userId: UUID): Either[UserNotFoundError, List[Content]] = {

val subQuery =
for {
uc <- UserContentTable.query if uc.userId === userId.toString && (!uc.adopted.isDefined || !uc.adopted)
c <- ContentTable.query if uc.contentId === c.id
} yield (uc, c)


val query =
UserTable.query.filter(_.id === userId.toString)
.joinLeft(subQuery).on { case (u, (uc, c)) => u.id === uc.userId}
.map { case (u, (uc, c)) => (u, c.optionProjection) }

//...

}


EDIT 1:

Got a little bit further with this by refactoring my subQuery to use the
for
comprehension syntax:

val query =
for (
(u, t) <- UserTable.query.filter(_.id === userId.toString) joinLeft subQuery on { case (u, (uc, c)) => u.id === uc.userId }
) yield (u, t)


This compiles. However, according to the documentation the
yield
should be applying
t.map(_)
to convert
Null
values to
None
.

So when I refactor the line to read:

yield (u, t.map(_))


I get the error:

[error] missing parameter type for expanded function ((x$2) => t.map(x$2))
[error] ) yield (u, t.map(_))
[error] ^
[error] one error found


EDIT 2 : You can find a Minimal, Complete, and Verifiable example here.

EDIT 3: Confirmed this issue exists in Slick
3.1.1
as well

Answer

joinLeft joins subQuery as Option in Slick3. In this case, you have to re-map subQuery as follows:

val query =
   UserTable.query.filter(_.id === userId.toString)
     .joinLeft(subQuery).on { case (u, (uc, c)) => u.id === uc.userId}
     .map { case (u, sub) => (u, sub.map { case (uc, c) => c }) }