在Slick 3.0中结合分页和非查询分组的好方法是什么?

时间:2015-08-14 18:53:20

标签: sql scala slick slick-3.0

为了简化,假设我有三个表:

val postTable = TableQuery[Posts]
val postTagTable = TableQuery[PostTags]
val tagTable = TableQuery[Tags]

一个帖子可以包含多个标签,而postTagTable只包含该关系。

现在我可以像这样查询帖子和标签:

val query = for {
    post <- postTable
    postTag <- postTagTable if post.id === postTag.postId
    tag <- tagTable if postTag.tagId === tag.id
} yield (post, tag)

val postTags = db.run(query.result).map {
    case result: Seq[(Post,Tag)] => 
        result.groupBy(_._1).map {
            case (post, postTagSeq) => (post, postTagSeq.map(_._2))
        }
}

哪会给我一个Future[Seq[(Post, Seq(Tag))]]

到目前为止一切顺利。

但是,如果我想为帖子添加分页怎么办? 由于一个Post可以包含多个Tags以及上述查询,因此我不知道查询中有多少行take,以便获取,例如10 {{1} }}

有没有人知道在一个查询中使用特定数量的帖子获得相同结果的好方法?

我实际上甚至不确定如何在没有嵌套查询的情况下在本机SQL中处理此问题,因此如果有人在该方向上有建议,我也很乐意听到它。

谢谢!

编辑

您知道,我目前正在做什么样的查询:

Posts

但这显然会导致嵌套查询。这就是我想避免的。

编辑2

另一种可能的2查询解决方案:

val pageQuery = postTable drop(page * pageSize) take(pageSize)

val query = for {
    pagePost <- pageQuery
    post <- postTable if pagePost.id === post.id
    postTag <- postTagTable if post.id === postTag.postId
    tag <- tagTable if postTag.tagId === tag.id
} yield (post, tag)

val postTags = db.run(query.result).map {
    case result: Seq[(Post,Tag)] => 
        result.groupBy(_._1).map {
            case (post, postTagSeq) => (post, postTagSeq.map(_._2))
        }
}

但这需要两次访问数据库并使用val pageQuery = postTable drop(page * pageSize) map(_.id) take(pageSize) db.run(pageQuery.result) flatMap { case ids: Seq[Int] => val query = for { post <- postTable if post.id inSetBind ids postTag <- postTagTable if post.id === postTag.postId tag <- tagTable if postTag.tagId === tag.id } yield (post, tag) val postTags = db.run(query.result).map { case result: Seq[(Post,Tag)] => result.groupBy(_._1).map { case (post, postTagSeq) => (post, postTagSeq.map(_._2)) } } } 运算符,因此它可能不如连接查询一样好。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

你可以这样做:

  def findPagination(from: Int, to: Int): Future[Seq[(Post, Seq[Tag])]] = {
    val query:DBIO[Seq[(Album,Seq[Genre])]] = postRepository.findAll(from, to).flatMap{posts=>
      DBIO.sequence(
        posts.map{ post=>
          tagRepository.findByPostId(post.id).map(tags=>(post,tags))
        }
      )
    }
    db.run(query)
  }

内部PostRepository

def findAll(from: Int, limit: Int): DBIO[Seq[Post]] = postTable.drop(from).take(limit).result

内部TagRepository

  def findByPostId(id: Int): DBIO[Seq[Tag]] = {
    val query = for {
      tag <- tagTable
      pstTag <- postTagTable if pstTag.postId === id && tag.id === pstTag.tagId
    } yield tag
    query.result
  }

修改

如果没有在单个查询中进行子选择,我就无法做到这一点。您当前的解决方案是最好的。您还可以通过删除不必要的&#34; join&#34;

来优化您的查询
val query = for {
    pagePost <- pageQuery
    postTag <- postTagTable if pagePost.id === postTag.postId
    tag <- tagTable if postTag.tagId === tag.id
} yield (pagePost, tag)

您将获得大约下一个SQL(Slick 3.0.1):

SELECT x2.`postname`,
       x2.`id`,
       x3.`tagname`,
       x3.`id`
FROM
  (SELECT x4.`postname` AS `postname`, x4.`id` AS `id`
   FROM `POST` x4 LIMIT 10, 1) x2,
     `POST_TAG` x5,
     `TAG` x3
WHERE (x2.`id` = x5.`postId`)
  AND (x5.`tagId` = x3.`id`)

也许在您的情况下,预编译此查询也更有效 http://slick.typesafe.com/doc/3.0.0/queries.html#compiled-queries

答案 1 :(得分:0)

我有同样的问题。可能还很有趣:

else statement

然后查询结果:

        val query = for {
      ((p, pt), t) <- posts.filter({x => x.userid === userId}).sortBy({x=>x.createdate.desc}).take(count).
                      joinLeft (postsTags).on((x, y)=>x.postid === y.postid).
                      joinLeft (tags).on(_._2.map(_.tagid) === _.tagid)
    } yield (p.?, t.map(_.?))
    //val query = posts filter({x => x.userid === userId}) sortBy({x=>x.createdate.desc}) take(count)
    try db.run(query result)
    catch{
      case ex:Exception => {
        log error("ex:", ex)
        Future[Seq[(Option[PostsRow], Option[Option[TagsRow]])]] {
          Seq((None, None))
        }
      }
    }

}

它会像这样返回Sequence: Seq [(Post,Seq [Tag]),(Post,Seq [Tag])........]