19

我将 mongodb 用于我的博客平台,用户可以在其中创建自己的博客。所有博客的所有条目都在条目集合中。条目的文档如下所示:

{
  'blog_id':xxx,
  'timestamp':xxx,
  'title':xxx,
  'content':xxx
}

正如问题所说,有没有办法为每个博客选择最后 3 个条目?

4

5 回答 5

29

You need to first sort the documents in the collection by the blog_id and timestamp fields, then do an initial group which creates an array of the original documents in descending order. After that you can slice the array with the documents to return the first 3 elements.

The intuition can be followed in this example:

db.entries.aggregate([
    { '$sort': { 'blog_id': 1, 'timestamp': -1 } }, 
    {       
        '$group': {
            '_id': '$blog_id',
            'docs': { '$push': '$$ROOT' },
        }
    },
    {
        '$project': {
            'top_three': { 
                '$slice': ['$docs', 3]
            }
        }
    }
])
于 2017-01-07T22:25:37.973 回答
1

如果您可以忍受两件事,那么在基本 mongo 中执行此操作的唯一方法是:

  • 您的条目文档中的一个附加字段,我们称之为“年龄”
  • 一个新的博客条目进行了额外的更新

如果是这样,您可以这样做:

  1. 在创建一个新的介绍后,进行正常的插入,然后执行此更新以增加所有帖子的年龄(包括您刚刚为该博客插入的帖子):

    db.entries.update({blog_id: BLOG_ID}, {age:{$inc:1}}, false, true)

  2. 查询时,使用以下查询将返回每个博客最近的 3 个条目:

    db.entries.find({age:{$lte:3}, 时间戳:{$gte:STARTOFMONTH, $lt:ENDOFMONTH}}).sort({blog_id:1, age:1})

请注意,此解决方案实际上是并发安全的(没有重复年龄的条目)。

于 2011-06-28T13:53:52.513 回答
1

这个答案使用来自另一个问题的 drcosta 的 map reduce 解决了问题

在 mongo 中,我如何使用 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});
于 2011-12-25T03:49:53.530 回答
1

Starting in Mongo 5.2release schedule, it's a perfect use case for the new $topN aggregation accumulator:

// { 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" }
//   ]
// }

This applies a $topN group accumulation that:

  • takes for each group the top 2 (n: 2) elements
  • top 2, as defined by sortBy: { _id: -1 }, which in this case means by reversed order of insertion
  • and for each record pushes the whole record in the group's list (output: "$$ROOT") since $$ROOT represents the whole document being processed.
于 2021-12-20T09:02:30.750 回答
0

可以使用组(聚合),但这会创建全表扫描。

你真的需要 3 个或者你可以设置一个限制......例如:上周/月最多 3 个帖子?

于 2011-06-27T21:42:01.627 回答