我在我的博客平台中使用了MongoDB,用户可以创建自己的博客。所有博客的所有条目都在一个entries集合中。一个条目的文档如下:
{
'blog_id':xxx,
'timestamp':xxx,
'title':xxx,
'content':xxx
}
就像问题所说的那样,有没有什么方法可以选择每个博客的最后3个条目?
我在我的博客平台中使用了MongoDB,用户可以创建自己的博客。所有博客的所有条目都在一个entries集合中。一个条目的文档如下:
{
'blog_id':xxx,
'timestamp':xxx,
'title':xxx,
'content':xxx
}
就像问题所说的那样,有没有什么方法可以选择每个博客的最后3个条目?
blog_id
和timestamp
字段进行排序,然后进行初始分组,创建一个按降序排列的原始文档数组。之后,你可以使用该文档数组进行切片操作以返回前3个元素。db.entries.aggregate([
{ '$sort': { 'blog_id': 1, 'timestamp': -1 } },
{
'$group': {
'_id': '$blog_id',
'docs': { '$push': '$$ROOT' },
}
},
{
'$project': {
'top_three': {
'$slice': ['$docs', 3]
}
}
}
])
从 Mongo 5.2
开始,这是一个完美的使用案例,可以使用新的$topN
聚合累加器:
// { blog_id: "a", title: "plop", content: "smthg" }
// { blog_id: "b", title: "hum", content: "meh" }
// { blog_id: "a", title: "hello", content: "world" }
// { blog_id: "a", title: "what", content: "ever" }
db.collection.aggregate([
{ $group: {
_id: "$blog_id",
messages: { $topN: { n: 2, sortBy: { _id: -1 }, output: "$$ROOT" } }
}}
])
// {
// _id: "a",
// messages: [
// { blog_id: "a", title: "what", content: "ever" },
// { blog_id: "a", title: "hello", content: "world" }
// ]
// }
// {
// _id: "b",
// messages: [
// { blog_id: "b", title: "hum", content: "meh" }
// ]
// }
该操作应用了一个$topN
组累加,它:
n: 2
)sortBy: {_id: -1}
定义的,这意味着按照插入顺序的相反顺序。output: "$$ROOT"
),因为$$ROOT
代表正在处理的整个文档。在基本的MongoDB中,如果您可以接受以下两点,则可以实现此目标:
如果可以,请按照以下步骤操作:
创建新介绍时,执行正常的插入,然后执行此更新以增加所有帖子(包括您刚刚为此博客插入的帖子)的年龄:
db.entries.update({blog_id: BLOG_ID}, {age:{$inc:1}}, false, true)
在查询时,请使用以下查询语句,它将返回每个博客最近的3个条目:
db.entries.find({age:{$lte:3}, timestamp:{$gte:STARTOFMONTH, $lt:ENDOFMONTH}}).sort({blog_id:1, age:1})
请注意,这种解决方案实际上是并发安全的(没有具有重复年龄的条目)。
这个答案使用了drcosta从另一个问题中的map reduce技术,解决了问题。
在MongoDB中,如何使用map reduce技术来实现按最近时间排序的分组操作
mapper = function () {
emit(this.category, {top:[this.score]});
}
reducer = function (key, values) {
var scores = [];
values.forEach(
function (obj) {
obj.top.forEach(
function (score) {
scores[scores.length] = score;
});
});
scores.sort();
scores.reverse();
return {top:scores.slice(0, 3)};
}
function find_top_scores(categories) {
var query = [];
db.top_foos.find({_id:{$in:categories}}).forEach(
function (topscores) {
query[query.length] = {
category:topscores._id,
score:{$in:topscores.value.top}
};
});
return db.foo.find({$or:query});
使用分组(聚合)是可能的,但这将创建一个完整的表扫描。
你真的需要恰好3个吗?或者你可以设置一个限制...例如:从上周/上个月最多获取3篇文章?