5

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

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 Posts

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

实际上,我什至不确定在没有嵌套查询的情况下如何在本机 SQL 中处理这个问题,所以如果有人在这个方向上有建议,我也会很高兴听到它。

谢谢!

编辑

只是让你知道,我目前正在做什么样的查询:

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))
        }
}

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

编辑 2

另一个可能的 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))
                     }
        }
 }

但这需要两次访问数据库并使用in运算符,因此它可能不如连接查询好。

有什么建议么?

4

2 回答 2

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
  }

编辑

我认为如果没有在单个查询中进行子选择,您将无法做到这一点。您当前的解决方案是最好的解决方案。您还可以通过删除不必要的“加入”来优化您的查询

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也更有效

于 2015-08-15T08:09:19.550 回答
0

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

        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))
        }
      }
    }

那么这个查询的结果:

result onComplete {
case Success(x) => {
  x groupBy { x => x._1 } map {x=>(x._1, x._2.map(_._2))}

}
case Failure(err) => {
  log error s"$err"
}

}

它返回这样的序列: Seq[(Post, Seq[Tag]), (Post, Seq[Tag])........]

于 2016-03-15T08:21:38.670 回答