2015-08-14 7 views
5

爲了簡化假設我有三個表:在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 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)) 
     } 
} 

但是,這顯然導致嵌套查詢。這是我想避免的。

EDIT 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運營商,所以它可能不是那麼好作爲連接查詢。

有什麼建議嗎?

+0

在這種情況下,slick的'groupBy'幫助不?如果你在查詢中用Post來做'groupBy',然後'take'? – Ixx

+0

如果我在查詢上進行groupBy操作,我需要使用map來聚合我沒有分組的所有內容。因此,如果我將帖子分組(在查詢中),我無法獲得標籤爲列表,但只有例如數它們。 – thwiegan

回答

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(油滑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

+0

但即使它只觸及數據庫一次,這將導致n + 1語句(其中n是pageSize),不是嗎? – thwiegan

+0

是的,你可以獲得n + 1個點擊數據庫。 1次獲得「分頁」帖子,n次獲得每個帖子的標籤 –

+0

好的,所以你要避免嵌套查詢,但是對數據庫做一些往返運行甚至更糟糕imo – thwiegan

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 [標籤]),(郵政,Seq [Tag])........]

相關問題