Mongoose子文档排序

8
我有一个文章模式,其中包含一个子文档comments,其中包含我得到的所有评论。
我的目标是通过id选择一篇文章,填充其作者字段以及评论中的作者字段。然后按日期对评论子文档进行排序。
文章模式如下:
var articleSchema = new Schema({
  title: { type: String, default: '', trim: true },
  body: { type: String, default: '', trim: true },
  author: { type: Schema.ObjectId, ref: 'User' },
  comments: [{
    body: { type: String, default: '' },
    author: { type: Schema.ObjectId, ref: 'User' },
    created_at: { type : Date, default : Date.now, get: getCreatedAtDate }
  }],
  tags: { type: [], get: getTags, set: setTags },
  image: {
    cdnUri: String,
    files: []
  },
  created_at: { type : Date, default : Date.now, get: getCreatedAtDate }
});
的静态方法: (我想在这里对评论进行排序,我可以做到吗?)
  load: function (id, cb) {
    this.findOne({ _id: id })
      .populate('author', 'email profile')
      .populate('comments.author')
      .exec(cb);
  },

我必须在其他地方对其进行排序:

exports.load = function (req, res, next, id) {
  var User = require('../models/User');

  Article.load(id, function (err, article) {
    var sorted = article.toObject({ getters: true });
    sorted.comments = _.sortBy(sorted.comments, 'created_at').reverse();

    req.article = sorted;
    next();
  });
};

我使用 toObject 方法将文档转换为 JavaScript 对象,这样我就可以保留我的 getters / virtuals,但是方法呢?

无论如何,我在纯对象上执行排序逻辑,然后完成。

我相信有更好的方法来完成这个任务,请告诉我。

3个回答

9
我可以将这些内容简单地写成几个事项,但是考虑到“获取mongoose对象”似乎是主要问题,因此有各种不同的解决方案。但是,既然您正在将“引用填充”到对象中,然后希望更改数组中对象的顺序,那么确实只有一种方法可以彻底解决这个问题。

在创建数据时按顺序修复数据


如果您想按照它们“created_at”的日期对“comments”数组进行排序,这甚至会分解为多种可能性:

  1. It "should" have been added to in "insertion" order, so the "latest" is last as you note, but you can also "modify" this in recent ( past couple of years now ) versions of MongoDB with $position as a modifier to $push :

    Article.update(
        { "_id": articleId },
        { 
            "$push": { "comments": { "$each": [newComment], "$position": 0 } }
        },
        function(err,result) {
            // other work in here
        }
    );
    

    This "prepends" the array element to the existing array at the "first" (0) index so it is always at the front.

  2. Failing using "positional" updates for logical reasons or just where you "want to be sure", then there has been around for an even "longer" time the $sort modifier to $push :

    Article.update(
        { "_id": articleId },
        { 
            "$push": { 
                "comments": { 
                    "$each": [newComment], 
                    "$sort": { "$created_at": -1 } 
                }
            }
        },
        function(err,result) {
            // other work in here
        }
    );
    

    And that will "sort" on the property of the array elements documents that contains the specified value on each modification. You can even do:

    Article.update(
        {  },
        { 
            "$push": { 
                "comments": { 
                    "$each": [], 
                    "$sort": { "$created_at": -1 } 
                }
            }
        },
        { "multi": true },
        function(err,result) {
            // other work in here
        }
    );
    

    And that will sort every "comments" array in your entire collection by the specified field in one hit.

其他解决方案可能使用.aggregate()对数组进行排序和/或在执行此操作或在普通对象上进行自己的.sort()之后将其“重新转换”为mongoose对象。这两者都涉及创建一个单独的模型对象和包含“引用”信息的嵌入式项的“模式”。因此,您可以沿着这些线路工作,但当您可以在第一时间将数据按照“最需要”的方式排序时,这似乎是不必要的开销。

另一种方法是确保像“虚拟”这样的字段始终以.toObject()的对象格式“序列化”,并且只是接受所有方法现在都消失了,并按所呈现的属性进行处理。

最后一种方法是“明智的”方法,但如果您通常使用“created_at”顺序,则更有意义的是以这种方式“存储”您的数据,以便在每个操作时,当您“检索”它时,它保持您将要使用的顺序。


谢谢。你的方法是迄今为止最简单的。当你试图在Mongoose API中工作时,有些棘手,例如findById -> push -> .save().. - backdesk
1
@backdesk 这也是Mongoose API。不同之处在于更新实际上是原子性的,而不是在客户端修改检索到的文档,然后保存更改。当然,您也可以在保存之前简单地对数组进行排序。但我建议您使用原子修改,因为与客户端修改和使用save()相比,不存在覆盖更改的可能性。 - Blakes Seven
请问您能解释一下"$created_at"吗?谢谢! - Marcos Pereira
@BlakesSeven,你能告诉我使用$position的成本吗?如果现有元素的位置将受到影响,它似乎会非常昂贵。 - ashwin mahajan

0

在获取并填充结果后,您还可以使用JavaScript的本地数组sort方法:

// Convert the mongoose doc into a 'vanilla' Array:
const articles = yourArticleDocs.toObject();

articles.comments.sort((a, b) => {
  const aDate = new Date(a.updated_at);
  const bDate = new Date(b.updated_at);

  if (aDate < bDate) return -1;
  if (aDate > bDate) return 1;

  return 0;
});

0

截至当前版本的MongoDB,您必须在从数据库检索后对数组进行排序。但是,使用Lodash中的_.sortBy()可以轻松地在一行代码中完成。

https://lodash.com/docs/4.17.15#sortBy

comments = _.sortBy(sorted.comments, 'created_at').reverse();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接