我正在努力让这个查询正确但却出错。首先,searchUser返回一个匹配的UserEntries序列,其中包含用户的唯一ID,对于每个userId,还有第二个查询从另一个表中获取一些其他用户信息+地址。
代码:
def searchUsers(pattern: String) = auth.SecuredAction.async {
implicit request =>
usersService.searchUser(pattern) flatMap { usrList =>
for {
u <- usrList
ui <- usersService.getUsersWithAddress(u.id)
} yield {
Ok(views.html.UserList(ui))
}
}
}
所用API的签名:
def searchUser(pattern: String): Future[Seq[UserEntry]] = ...
def getUsersWithAddress(userId: Long): Future[Seq[(UserProfile, Seq[String])]] = ...
错误:
[error] modules/www/app/controllers/Dashboard.scala:68: type mismatch;
[error] found : scala.concurrent.Future[play.api.mvc.Result]
[error] required: scala.collection.GenTraversableOnce[?]
[error] ui <- usersService.getUsersWithAddress(u.id)
[error] ^
[error] one error found
[error] (www/compile:compileIncremental) Compilation failed
如果我注释掉行“u&lt; - usrList”并对下一行的用户ID进行硬编码,例如“ui&lt; - usersService.getUsersWithAddress(1L)”就可以了。知道我错过了什么吗?
答案 0 :(得分:1)
当您在for comprehension中使用多个生成器时,monad必须属于同一类型。例如。你不能:
scala> for{ x <- Some("hi"); y <- List(1,2,3) } yield (x,y)
<console>:11: error: type mismatch;
found : List[(String, Int)]
required: Option[?]
for{ x <- Some("hi"); y <- List(1,2,3) } yield (x,y)
^
您可以做的是将一个或另一个转换为匹配正确的类型。对于上面的例子,那将是:
scala> for{ x <- Some("hi").toSeq; y <- List(1,2,3) } yield (x,y)
res2: Seq[(String, Int)] = List((hi,1), (hi,2), (hi,3))
在您的特定情况下,您的一个生成器是GenTraversableOnce,另一个是Future。您可以使用Future.successful(theList)来获得两个期货。请参阅此处的答案:
Unable to use for comprehension to map over List within Future
答案 1 :(得分:0)
根据@Brian的回答得出一个解决方案..以下工作(无法输入格式化程序代码块作为注释 - 所以添加为答案):
usersService.searchUser(pattern) flatMap { usrList =>
val q = for {
u <- usrList
} yield (usersService.getUserWithAddress(u.id))
val r = Future.sequence(q)
r map { ps =>
Ok(views.html.UserList(ps))
}
}
为了理解,积累了Futures,然后将序列展平,然后进行映射。希望这就是它的完成方式!
注意:我还必须将getUserWithAddress的签名更改为X而不是Seq [X]