【问题标题】:mongoose subdocument sorting猫鼬子文档排序
【发布时间】:2015-10-01 16:12:33
【问题描述】:

我有一个文章架构,它有一个子文档comments,其中包含我为这篇特定文章获得的所有 cmets。

我要做的是按 id 选择一篇文章,填充其作者字段以及 cmets 中的作者字段。然后按日期对 cme​​ts 子文档进行排序。

文章架构:

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 }
});

文章架构上的静态方法:(我想在这里对 cme​​ts 进行排序,我可以这样做吗?)

  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 对象,我可以保留我的getter / virtuals,但是方法呢??

无论如何,我对普通对象执行排序逻辑并完成。

我很确定有很多更好的方法可以做到这一点,请告诉我。

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    从 MongoDB 的当前版本开始,您必须在数据库检索后对数组进行排序。但这很容易使用 Lodash 中的_.sortBy() 在一行中完成。

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

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

    【讨论】:

      【解决方案2】:

      您还可以在检索并填充结果后使用 JavaScript 的原生 Array 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;
      });
      

      【讨论】:

        【解决方案3】:

        我可以把它写成几件事,但考虑到“让猫鼬对象回来”似乎是主要考虑因素。

        因此,您“可以”做很多事情。但是由于您正在将引用“填充”到 Object 中,然后想要更改数组中对象的顺序,因此实际上只有一种方法可以一劳永逸地解决这个问题。

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


        如果您希望您的“cmets”数组按它们“created_at”的日期排序,这甚至可以分解为多种可能性:

        1. 它“应该”已按“插入”顺序添加,因此“最新”是您注意的最后一个,但您也可以使用 @ 在最近(过去几年)的 MongoDB 版本中“修改”它987654321@ 作为$push 的修饰符:

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

          这会将数组元素“添加”到现有数组的“第一个”(0) 索引处,因此它始终位于最前面。

        2. 由于逻辑原因或仅在您“想确定”的地方使用“位置”更新失败,那么$sort 修饰符到$push 的时间甚至更长:

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

          这将对包含指定值的数组元素文档的属性进行“排序”。你甚至可以这样做:

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

          这将一次性按指定字段对整个集合中的每个“cmets”数组进行排序。

        其他解决方案可以使用.aggregate() 对数组进行排序和/或在您完成该操作后或在普通对象上执行您自己的.sort() 后“重新转换”为猫鼬对象。

        这两者实际上都涉及创建一个单独的模型对象和“模式”,其中嵌入的项目包括“引用”信息。所以你可以在这些行上工作,但是当你可以首先将数据排序为“最需要”的意思时,这似乎是不必要的开销。

        另一种方法是确保像“virtuals”这样的字段总是“序列化”成带有.toObject()的对象格式,并且只是忍受所有方法现在都消失了并使用所提供的属性的事实。

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

        【讨论】:

        • 谢谢。你的方法是迄今为止最简单的。当您尝试在 Mongoose API 中工作时,事情会变得有点尴尬,即 findById -> push -> .save()..
        • @backdesk 这也是 Mongoose API。不同之处在于更新实际上是原子的,而不是在客户端对检索到的文档进行修改然后保存更改。当然,您也可以在保存之前简单地对数组进行排序。但我建议您宁愿使用原子修改,因为不可能像客户端修改和使用 save() 那样覆盖更改
        • 你能解释一下“$created_at”吗?谢谢!
        • @BlakesSeven 你能告诉我使用 $position 的成本是多少吗?如果要影响现有元素位置,这似乎相当昂贵。
        猜你喜欢
        • 2016-08-17
        • 1970-01-01
        • 2019-05-31
        • 1970-01-01
        • 1970-01-01
        • 2019-07-19
        • 1970-01-01
        • 2015-06-26
        • 2017-05-07
        相关资源
        最近更新 更多