.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
}
]
}
]