2016-12-15 60 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; 
    };