【问题标题】:Mongoose sort in populate is not working填充中的猫鼬排序不起作用
【发布时间】:2016-04-17 10:29:40
【问题描述】:

我试图让我的通知按日期降序排序。我相信他们目前正在按升序排列。我尝试了两种不同的方法来在我的填充语句中对通知进行排序

尝试 1

Notification.populate(user.notifications, { path: 'project', model: 'Project', sort: { 'created': -1 }},
                        function(err, notifications) {
                            // console.log('nested population of user', notifications);
                            socket.emit('take notifications', {
                                notifications: notifications,
                                num_unread: unread
                            });
                        });

尝试 2

Notification.populate(user.notifications, { path: 'project', model: 'Project', sort: { 'created': -1 }},
                        function(err, notifications) {
                            // console.log('nested population of user', notifications);
                            socket.emit('take notifications', {
                                notifications: notifications,
                                num_unread: unread
                            });
                        });

我也尝试过切换 1 和 -1 参数,但这没有任何作用。

通知架构

var NotificationSchema = new Schema({
    created: {
        type: Date,
        default: Date.now
    },
    type: {
        type: String,
        enum: ['rfc']
    },
    read: {
        type: Boolean,
        default: false
    },
    from: {
        type: Schema.ObjectId,
        ref: 'User'
    },
    project: {
        type: Schema.ObjectId,
        ref: 'Project'
    }
});

【问题讨论】:

    标签: javascript node.js mongodb mongoose mongodb-query


    【解决方案1】:

    .populate() 的这种形式与 "sort" 选项的使用方式不同,因为该选项具有不同的预期用途。

    您基本上只需要一个常规的 JavaScript Array.sort() 或者因为您想要排序的属性在填充之前已经在父文档中,那么只需将 .sort() 光标修饰符应用于初始 .find() 反正。

    所以要么在.find() 查询中:

    Notification.find()
      .populate({ path: 'project', model: 'Project'})
      .sort({ "created": -1 })
      .exec(function(err,notifications) {
          // sorted by cursor
      });
    

    或者用Array.sort()对数组内容调用Model.populate()

    Notification.populate(
      user.notifications,
      { path: 'project', model: 'Project' },
      function(err,notifications) {
       notifications.sort(function(a,b) { return a.created < b.created });
       // now it's sorted
      }
    )
    

    因此,根据您的情况,当前字段上的父项的 .sort() 不是填充的,要么通过“光标”排序应用,要么仅通过对手头的常规数组进行排序。


    预期用途说明

    考虑以下示例,其中将是“用户”模型内容:

    {
      "_id" : ObjectId("5712ebbc37ba497f25b6b800"),
      "name" : "Bill",
      "sport" : ObjectId("5712e5af18a74c2810d5a5a8"),
      "followers" : [
              ObjectId("5712e001a83d6da651770e27"),
              ObjectId("5712e05da83d6da651770e28"),
              ObjectId("5712e06fa83d6da651770e29")
      ]
    }
    {
      "_id" : ObjectId("5712ebbc37ba497f25b6b801"),
      "name" : "Ted",
      "sport" : ObjectId("5712e5da18a74c2810d5a5a9"),
      "followers" : [
              ObjectId("5712e001a83d6da651770e27"),
              ObjectId("5712e06fa83d6da651770e29")
      ]
    }
    

    然后是带有相关“关注者”项目的集合:

    { "_id": ObjectId("5712e001a83d6da651770e27"), "name": "Fred" },
    { "_id": ObjectId("5712e05da83d6da651770e28"), "name": "Sally" },
    { "_id": ObjectId("5712e06fa83d6da651770e29"), "name": "Abe" }
    

    当然还有“运动”项目:

    { "_id" : ObjectId("5712e5af18a74c2810d5a5a8"), "name" : "Tennis" }
    { "_id" : ObjectId("5712e5da18a74c2810d5a5a9"), "name" : "Golf" }
    

    .populate() 的“排序”选项随后旨在处理诸如“关注者”内容之类的内容,当填充此内容时,项目将反映“排序”的顺序,而不是它们在其中的顺序发生的顺序人口之前的数组。

    因此,即使您为要填充的两个路径都提供了“排序”选项,实际上也仅对“数组”内容进行了排序:

      User.populate(
        users,
        [
          { "path": "sport", "options": { "sort": { "name": 1 } } },
          { "path": "followers", "options": { "sort": { "name": 1 } } }
        ],
        function(err,users) {
          console.log(JSON.stringify(users,undefined,2));
          callback(err);
        }
      );
    

    结果将是:

    [
      {
        "_id": "5712ebbc37ba497f25b6b800",
        "name": "Bill",
        "sport": {
          "_id": "5712e5af18a74c2810d5a5a8",
          "name": "Tennis",
          "__v": 0
        },
        "__v": 0,
        "followers": [
          {
            "_id": "5712e06fa83d6da651770e29",
            "name": "Abe",
            "__v": 0
          },
          {
            "_id": "5712e001a83d6da651770e27",
            "name": "Fred",
            "__v": 0
          },
          {
            "_id": "5712e05da83d6da651770e28",
            "name": "Sally",
            "__v": 0
          }
        ]
      },
      {
        "_id": "5712ebbc37ba497f25b6b801",
        "name": "Ted",
        "sport": {
          "_id": "5712e5da18a74c2810d5a5a9",
          "name": "Golf",
          "__v": 0
        },
        "__v": 0,
        "followers": [
          {
            "_id": "5712e06fa83d6da651770e29",
            "name": "Abe",
            "__v": 0
          },
          {
            "_id": "5712e001a83d6da651770e27",
            "name": "Fred",
            "__v": 0
          }
        ]
      }
    ]
    

    这是正常行为,正如预期的那样。但是您要问的是对填充属性返回的users 列表进行“排序”。那是完全不同的事情,由普通客户端 .sort() 在数组上处理:

      User.populate(
        users,
        [
          { "path": "sport", "options": { "sort": { "name": 1 } } },
          { "path": "followers", "options": { "sort": { "name": 1 } } }
        ],
        function(err,users) {
          users.sort(function(a,b) {
            return a.sport.name > b.sport.name;
          });
          console.log(JSON.stringify(users,undefined,2));
          callback(err);
        }
      );
    

    实际上,这实际上会对列表进行排序,就像对任何常规数组进行排序一样。这就是您对填充属性进行排序的方式,因为填充实际上发生在客户端中。


    查找和排序的服务器端处理

    在某些情况下,您实际上希望这种排序发生在服务器上。此类事情包括“分页”结果,将所有结果返回给客户端 API,然后在排序后提取所需的页面,效率不高。

    从 v3.2.x 及以上版本的 MongoDB 的现代版本包括 $lookup 聚合管道方法。这基本上做了.populate() 在“客户端”上所做的事情,而不是在“服务器”上做的事情。

    它没有所有相同的选项来过滤或直接从$lookup“排序”项目,但由于这是在聚合框架中实现的,因此可以在单独的管道阶段执行这些操作。

    包括一个示例清单,显示了在客户端中使用 .populate() 处理 .sort() 以及使用 $lookup 的聚合管道技术。

    两者都得出相同的结果,不同之处在于 $lookup 是您在返回客户端 API 之前需要对结果进行排序时使用的技术,例如在“分页”数据时。

    var async = require('async'),
        mongoose = require('mongoose'),
        Schema = mongoose.Schema;
    
    mongoose.connect('mongodb://localhost/follower');
    
    var sportSchema = new Schema({
      name: String
    });
    
    var followerSchema = new Schema({
      name: String
    });
    
    var userSchema = new Schema({
      name: String,
      sport: { type: Schema.Types.ObjectId, ref: 'Sport' },
      followers: [{ type: Schema.Types.ObjectId, ref: 'Follower' }]
    });
    
    var Sport = mongoose.model('Sport',sportSchema),
        Follower = mongoose.model('Follower',followerSchema),
        User = mongoose.model('User',userSchema);
    
    async.series(
      [
        function(callback) {
          async.each([Sport,Follower,User],function(model,callback) {
            model.remove({},callback);
          },callback);
        },
        function(callback) {
          async.parallel(
            [
              function(callback) {
                Sport.create([
                  { "_id": "5712e5af18a74c2810d5a5a8", "name": "Tennis" },
                  { "_id": "5712e5da18a74c2810d5a5a9", "name": "Golf" }
                ],callback);
              },
              function(callback) {
                Follower.create([
                  { "_id": "5712e001a83d6da651770e27", "name": "Fred" },
                  { "_id": "5712e05da83d6da651770e28", "name": "Sally" },
                  { "_id": "5712e06fa83d6da651770e29", "name": "Abe" }
                ],callback);
              },
              function(callback) {
                User.create([
                  {
                     "name": "Bill",
                     "sport": "5712e5af18a74c2810d5a5a8",
                     "followers": [
                        "5712e001a83d6da651770e27",
                        "5712e05da83d6da651770e28",
                        "5712e06fa83d6da651770e29"
                      ]
                  },
                  {
                     "name": "Ted",
                     "sport": "5712e5da18a74c2810d5a5a9",
                     "followers": [
                        "5712e001a83d6da651770e27",
                        "5712e06fa83d6da651770e29"
                      ]
                  }
                ],callback);
              }
            ],
            callback
          );
        },
        function(callback) {
          console.log("Populate Output");
          User.find().exec(function(err,users) {
            if (err) callback(err);
            User.populate(
              users,
              [
                { "path": "sport", "options": { "sort": { "name": 1 } } },
                { "path": "followers", "options": { "sort": { "name": 1 } } }
              ],
              function(err,users) {
                users.sort(function(a,b) {
                  return a.sport.name > b.sport.name;
                });
                console.log(JSON.stringify(users,undefined,2));
                callback(err);
              }
            );
          });
        },
        function(callback) {
          console.log("Aggregate Output");
          User.aggregate(
            [
              { "$lookup": {
                "from": "sports",
                "localField": "sport",
                "foreignField": "_id",
                "as": "sport"
              }},
              { "$unwind": "$sport" },
              { "$unwind": "$followers" },
              { "$lookup": {
                "from": "followers",
                "localField": "followers",
                "foreignField": "_id",
                "as": "followers"
              }},
              { "$unwind": "$followers" },
              { "$sort": { "_id": 1, "followers.name": 1 } },
              { "$group": {
                "_id": "$_id",
                "name": { "$first": "$name" },
                "sport": { "$first": "$sport" },
                "followers": { "$push": "$followers" }
              }},
              { "$sort": { "sport.name": 1 } }
            ],
            function(err,users) {
              console.log(JSON.stringify(users,undefined,2));
              callback(err);
            }
          );
        }
      ],
      function(err) {
        if (err) throw err;
        mongoose.disconnect();
      }
    );
    

    输出

    Populate Output
    [
      {
        "_id": "5712ebbc37ba497f25b6b801",
        "name": "Ted",
        "sport": {
          "_id": "5712e5da18a74c2810d5a5a9",
          "name": "Golf",
          "__v": 0
        },
        "__v": 0,
        "followers": [
          {
            "_id": "5712e06fa83d6da651770e29",
            "name": "Abe",
            "__v": 0
          },
          {
            "_id": "5712e001a83d6da651770e27",
            "name": "Fred",
            "__v": 0
          }
        ]
      },
      {
        "_id": "5712ebbc37ba497f25b6b800",
        "name": "Bill",
        "sport": {
          "_id": "5712e5af18a74c2810d5a5a8",
          "name": "Tennis",
          "__v": 0
        },
        "__v": 0,
        "followers": [
          {
            "_id": "5712e06fa83d6da651770e29",
            "name": "Abe",
            "__v": 0
          },
          {
            "_id": "5712e001a83d6da651770e27",
            "name": "Fred",
            "__v": 0
          },
          {
            "_id": "5712e05da83d6da651770e28",
            "name": "Sally",
            "__v": 0
          }
        ]
      }
    ]
    Aggregate Output
    [
      {
        "_id": "5712ebbc37ba497f25b6b801",
        "name": "Ted",
        "sport": {
          "_id": "5712e5da18a74c2810d5a5a9",
          "name": "Golf",
          "__v": 0
        },
        "followers": [
          {
            "_id": "5712e06fa83d6da651770e29",
            "name": "Abe",
            "__v": 0
          },
          {
            "_id": "5712e001a83d6da651770e27",
            "name": "Fred",
            "__v": 0
          }
        ]
      },
      {
        "_id": "5712ebbc37ba497f25b6b800",
        "name": "Bill",
        "sport": {
          "_id": "5712e5af18a74c2810d5a5a8",
          "name": "Tennis",
          "__v": 0
        },
        "followers": [
          {
            "_id": "5712e06fa83d6da651770e29",
            "name": "Abe",
            "__v": 0
          },
          {
            "_id": "5712e001a83d6da651770e27",
            "name": "Fred",
            "__v": 0
          },
          {
            "_id": "5712e05da83d6da651770e28",
            "name": "Sally",
            "__v": 0
          }
        ]
      }
    ]
    

    【讨论】:

      猜你喜欢
      • 2017-01-10
      • 2019-09-16
      • 2014-12-19
      • 2016-12-24
      • 2018-06-10
      • 2013-06-20
      • 1970-01-01
      • 2014-05-01
      • 2021-02-03
      相关资源
      最近更新 更多