在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内置了分页符,所以我想问一下贡献者是否支持它。
答案 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;
};