使用 Slick 通过一个查询生成一页新闻

generate a page of news with one query with Slick

我想通过一个查询生成一页带有相关标签的新闻。但我做不对。我不明白的是为什么根据 Idea 编辑器和编译器,错误行上方的行是正确的。

  val newsTable = TableQuery[NewsTable]
  val newsTagsTable = TableQuery[NewsTags]
  val newsTagsAssociationsTable = TableQuery[newsTagsAssociations]//2 columns: newsId and tagId

  case class FullNews(news:Option[News],tags:Seq[NewsTag])

  def getPage(page: Long = 0, pageSize: Int = 10): Future[Page[FullNews]] = {
    val offset = pageSize * page
    val total = newsTable.size.result
    val selectedNews = newsTable.sortBy(x => x.id.desc).drop(offset).take(pageSize)

    val fullNews = selectedNews.result.map(a => a.map(news => {

      val tagsId = newsTagsAssociationsTable.filter(x => x.idNews === news.id).map(_.idTag)
      val tagsQuery =  newsTagsTable.filter(tag => tag.id in tagsId)
      for (tags <- tagsQuery .result) yield FullNews(Some(news), tags)
    }))

    val theFullNews: Future[Seq[FullNews]] = db.run(fullNews )//error at this line
    val totalFuture = db.run(total)

    for (
      newsSeq <- theFullNews;
      t <- totalFuture
    ) yield Page[FullNews](newsSeq, page, offset, t, pageSize)

  }

游戏编译器说:

Blockquote type mismatch; [error] found : slick.dbio.DBIOAction[Seq[slick.dbio.DBIOAction[models.FullNews,slick.dbio.NoStream,slick.dbio.Effect.Read]],slick.dbio.NoStream,slick.dbio.Effect.Read] [error] required: slick.dbio.DBIOAction[Seq[models.FullNews],slick.dbio.NoStream,Nothing]

提前致谢。

您的用例通常通过使用连接来解决。我假设并非每个 news 都有 tags。这就是我要使用 outer join 的原因。未经测试的代码:

// FullNews always has a news, right? Removed option type.
case class FullNews(news: News, tags: Seq[NewsTag])

def getPage(page: Long = 0, pageSize: Int = 10): Future[Page[FullNews]] = {
  val offset = pageSize * page

  // let's query fullNews first. 
  // fullNews is basically Query[Seq[News, Option[NewsTag]]]
  val fullNews = newsTable.sortBy(x => x.id.desc).drop(offset).take(pageSize)
    .joinLeft(newsTagsAssociationsTable).on(_.id === _.newsId)
    .joinLeft(newsTagsTable).on(_._2.map(_.tagId) === _.id)
    .map {
      case ((news, _), tag) => (news, tag)
    }

  // query the total size
  val totalNews = newsTable.size

  // Now that we have our queries in place, create an action
  // and map the result to FullNews:
  val fullNewsAction = fullNews.result.map { result => 
    result.groupBy(_._1).map {
      case (news, grp) => FullNews(news, grp.flatMap(_._2).distinct)
    }
  }

  // For the sake of completeness, let's also create an action for totalNews
  val totalNewsAction = totalNews.result

  // Compose an action consisting of fullNewsAction and totalNewsAction
  val pageAction = for {
    n <- fullNewsAction
    t <- totalNewsAction
  } yield Page(n, page, offset, t, pageSize)

  // Finally execute pageAction on the db
  db.run(pageAction.transactionally)
}

如果每个 news 至少有一个 tag 关联,您可以将外部联接替换为内部联接。有关详细信息,请参阅 slick docs