KeystoneJS列出了聚合结果的分页

时间:2016-12-15 14:59:20

标签: mongoose keystonejs

在KeystoneJS中,我们正在使用聚合来查询Mongo,只是抓取整个集合并返回它。现在,我们要获得更多数据,我们需要进行分页。 KeystoneJS支持分页,但从外观上看它只支持where选项参数中的find命令。 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');

从此返回的结果我希望能够分页。我正在探索使用mongoose来实现它,或者只是使用javascript过滤数组,但是因为我看到Keystone内置了分页符,所以我想问一下贡献者是否支持它。

2 个答案:

答案 0 :(得分:0)

首先,您是否尝试过简单:

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

我真的没有看到为什么这不应该起作用的原因,虽然我不熟悉猫鼬如何在幕后工作。 Keystone当然允许你像这样进行分页调用。

如果不这样做,你可以把它分成两个电话。首先对帖子进行标准分页调用,以获取该页面上帖子的ID并将其保存到数组中。然后,您可以进行聚合调用,该调用首先匹配您刚刚创建的数组中具有ID的帖子。如果值在数组here中,则有关于如何匹配的StackOverflow帖子。那应该达到你想要的?

最后,你确定所有聚合等都是必要的。我不是百分之百在这里实现的,因为我从来没有真正使用过聚合,但是你可以将postTopics作为帖子上的关系字段,这样你就可以调用populate了吗?同样,每个主题都可能与其子主题等有关系。我认为你的用例对此来说太复杂了,但我想我会提到它以防万一。

答案 1 :(得分:0)

对于那些稍后发现的人,我能够利用Mongoose来帮助清理这个查询。不幸的是,对于paginate,似乎还没有考虑post populate过滤。 paginate将返回与where子句匹配的所有帖子,但如果我需要在填充的字段上进行更多过滤,这将无济于事。当我们填充时,分页已经完成。所以,如果paginate最初返回10个中的10个,在我在exec中执行过滤后,我可能最终会得到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;
    };