2016-12-15 54 views
0

在KeystoneJS中,我們正在使用聚合來查詢Mongo並只抓取整個集合並返回它。現在,我們正在獲取更多數據,我們需要分頁。 KeystoneJS支持分頁,但從外觀上看,它只支持where選項參數中的find命令。有沒有什麼辦法可以使KeystoneJS提供的分頁能夠對聚合查詢的結果進行分頁?KeystoneJS列表聚合結果的分頁

實例彙總查詢:

keystone.list('Posts').model.aggregate([ 
{'$match': {"state":"published"}}, 
{'$unwind': {'path':"$actions", 'preserveNullAndEmptyArrays': false}}, 
     {'$lookup': { 
     'from':"actions", 
     'localField': "actions", 
     'foreignField': "_id", 
     'as': "actions"}}, 
     {'$match': {"actions.key": action}}, 
{'$unwind': {'path':"$postTopics"}}, 
     {'$lookup': { 
     'from':"posttopics", 
     'localField': "postTopics", 
     'foreignField': "_id", 
     'as': "posttopics"}}, 
     {'$match': {"posttopics.key": topic}}, 
{'$unwind': {'path':"$postSubTopics"}}, 
     {'$lookup': { 
     'from':"postsubtopics", 
     'localField': "postSubTopics", 
     'foreignField': "_id", 
     'as': "postsubtopics"}}, 
     {'$match': {"postsubtopics.key": subtopic}}, 
{'$unwind': 
    {'path':"$postSubTopics", 
    'preserveNullAndEmptyArrays': true}}, 
    {'$unwind': 
    {'path':"$postTopics", 
    'preserveNullAndEmptyArrays': true}}, 
    {'$lookup': { 
    'from':"postsubtopics", 
    'localField': "postSubTopics", 
    'foreignField': "_id", 
    'as': "postsubtopics"}}, 
    {'$lookup': { 
    'from':"posttopics", 
    'localField': "postTopics", 
    'foreignField': "_id", 
    'as': "posttopics"}}, 
    {'$match': { 
    '$or': 
     [ 
     { "postsubtopics.searchKeywords": keyword }, 
     { "posttopics.searchKeywords": keyword } 
     ] 
    }} 
]).sort('-publishedDate'); 

,從這個我想是能夠通過分頁返回回來的結果。我正在探索使用貓鼬做它,或者只是用javascript過濾數組,但是由於我看到Keystone已經內置了分頁,我想問問貢獻者是否支持。

回答

0

首先,你嘗試過簡單:

keystone.list('Posts').model.paginate({ 
       page: req.query.page || 1, 
       perPage: 10, 
       maxPages: 10, 
      }) 
      .aggregate([...]) 

我真的不明白了一個道理,爲什麼不應該工作,雖然我不熟悉的框架下如何貓鼬作品。 Keystone當然可以讓你像這樣做一個分頁電話。

如果失敗了,你可以將它分成兩個調用。首先在Posts上做一個標準的分頁調用,以獲取該頁面上的帖子的ID並將其保存到數組中。然後,您可以進行一次聚合調用,首先匹配您剛創建的數組中具有ids的帖子。如果數值在數組here中,有如何匹配的StackOverflow帖子。那應該達到你想要的?

最後,你確定所有的聚合等是必要的。我並不是100%,因爲我從未實際使用過聚合,但是您可以將postTopics作爲帖子上的關係字段,這樣可以讓您調用populate?同樣,每個主題都可能與其子主題等有關係。我假設你的用例對於這個過於複雜,但我認爲我會提及它以防萬一。

+0

感謝您的意見。我已經嘗試過上面顯示的查詢,並且嘗試了很多變體,但都沒有奏效。你上面的例子抱怨聚合不是一個函數。我確實已經掌握了所有數據,並使用JS自己執行分頁。 Mongo中的聚合框架似乎越來越流行,我只是好奇,是否有一個簡單的即插即用的keystone分頁來支持聚合而不是find查詢。 – lshaffer

0

對於那些稍後發現此問題的人,我能夠利用Mongoose來幫助清理此查詢。不幸的是,對於分頁,似乎還不可能考慮後置填充過濾。頁面將返回與where子句匹配的所有帖子,但如果我需要在填充字段上過濾更多內容,這並不會起作用。當我們填充時,分頁已經完成。因此,如果paginate在初始時返回10個,那麼在執行過濾器之後,我最終可能只有10箇中的3個。我最終創建了一個全局PostsPaginator對象來幫助我完成此操作,而不是使用Keystone的分頁。

var populateObjects = [ 
     { 
     path: 'actions', 
     match: { 'key': action} 
     }, 
     { 
     path: 'postTopics', 
     match: { '$or': [ 
      {'key': topic}, 
      { "searchKeywords": keyword }] 
     } 
     }, 
     { 
     path: 'postSubTopics', 
     match: { '$or': [ 
      {'key': subtopic}, 
      { "searchKeywords": keyword }] 
     } 
     } 
    ]; 

keystone.list('Posts').model 
.find({'state': 'published', 
      'publishedDate': {'$lte': currentDate}}) 
.populate(populateObjects) 
.sort({'publishedDate': -1}).lean() 
.exec(function (err, result) { 
result = result.filter((doc) => {//logic to check if wanted fields are populated} 
} 



// In another file... 

    PostsPaginator.getPostsFromPostsArray = function(req, res, postsArray, pageNumber, postsPerPage) { 
     /*attributes: 
      pageNumber - number: the current page we are displaying for 
      totalPosts - number: total number of posts that exist for the query 
      totalPages - number: total number of pages there will be for pagination 
      pagePosts - array: the posts that will be returned to display for the page 
      isValidPage - boolean: if invalid page number is input return false 
      pagePath - string: the url path of the page requested 
      */ 
     var posts = {}; 
     postsPerPage = postsPerPage || 10; 
     posts.pageNumber = pageNumber; 

     posts.totalPosts = postsArray.length; 
     posts.totalPages = Math.ceil(postsArray.length/postsPerPage); 

     var start = postsPerPage * (pageNumber - 1); 
     var end = postsPerPage * pageNumber; 
     posts.pagePosts = postsArray.slice(start, end); 
     posts.isValidPage = start <= posts.totalPosts; 
     posts.pagePath = req.path; 
     return posts; 
    };