I use MongoDB and Mongoose as my ODM and I'm trying to make a query using populate
and group by
in the same statement.
我使用MongoDB和Mongoose作为我的ODM,我正在尝试使用populate和group by在同一语句中进行查询。
Here is my simple documents models :
这是我的简单文档模型:
var userSchema = new Schema({
username: String
});
var messageSchema = new Schema({
from: { type: Schema.ObjectId, ref: 'User' },
to: { type: Schema.ObjectId, ref: 'User' },
message: String,
date: { type: Date, default: Date.now }
});
I'm just trying to get every messages for one user, group by each users he talks with. I tried like this :
我只是试图为每个用户收集每条消息,并与他谈话的每个用户进行分组。我试过这样的:
this.find({ 'to': user })
.sort({ 'date': 1 })
.group('from')
.populate(['from', 'to'])
.exec(callback);
But, unfortunately, my model doesn't have group
method. Do you have any solution, to get this working ?
但是,不幸的是,我的模型没有组方法。你有任何解决方案,让这个工作吗?
Thank you.
谢谢。
2 个解决方案
#1
12
Example using $lookup populate, lookup populates as an array, hence the $unwind.
使用$ lookup populate的示例,lookup填充为数组,因此$ unwind。
Message.aggregate(
[
{ "$match": { "to": user } },
{ "$sort": { "date": 1 } },
{ "$group": {
"_id": "from",
"to": { "$first": "$to" },
"message": { "$first": "$message" },
"date": { "$first": "$date" },
"origId": { "$first": "$_id" }
}},
{ "$lookup": {
"from": "users",
"localField": "from",
"foreignField": "_id",
"as": "from"
}},
{ "$lookup": {
"from": "users",
"localField": "to",
"foreignField": "_id",
"as": "to"
}},
{ "$unwind": { "path" : "$from" } },
{ "$unwind": { "path" : "$to" } }
],
function(err,results) {
if (err) throw err;
return results;
}
)
#2
9
The better option to use here is .aggregate()
, which is a native code implementation unlike the .group()
method of MongoDB which uses the JavaScript engine to process results.
这里使用的更好选项是.aggregate(),这是一个本机代码实现,与MongoDB的.group()方法不同,后者使用JavaScript引擎处理结果。
Methods like .populate()
are not directly supported though, and this is by design since the aggregation pipeline and other methods do not strictly return a response that is based on the current model's schema. Since it would be wrong to "assume" that is what you are doing, it is just a raw object response.
但是直接支持像.populate()这样的方法,这是设计的,因为聚合管道和其他方法不会严格返回基于当前模型模式的响应。因为“假设”是你正在做的事情是错误的,它只是一个原始对象响应。
There is however nothing stopping you from "casting" the response as mongoose documents and then calling the model form of .populate()
with the required paths:
然而,没有什么能阻止你将响应“转换”为mongoose文档,然后使用所需的路径调用.populate()的模型形式:
Message.aggregate(
[
{ "$match": { "to": user } },
{ "$sort": { "date": 1 } },
{ "$group": {
"_id": "from",
"to": { "$first": "$to" },
"message": { "$first": "$message" },
"date": { "$first": "$date" },
"origId": { "$first": "$_id" }
}}
],
function(err,results) {
if (err) throw err;
results = result.map(function(doc) {
doc.from = doc._id
doc._id = doc.origId;
delete doc.origId;
return new Message( doc )
});
User.populate( results, { "path": "from to" }, function(err,results) {
if (err) throw err;
console.log( JSON.stringify( results, undefined, 4 ) );
});
}
)
Of course, that just really returns the $first
message from each "from" as is implied by the operator.
当然,这只是真正从运营商隐含的每个“来自”返回$ first消息。
Perhaps what you really mean by "group by" is actually to "sort":
也许你所说的“分组”实际意味着“排序”:
Message.find({ "to": user })
.sort({ "from": 1, "date": 1 })
.populate("from to")
.exec(function(err,messsages) {
if (err) throw err;
console.log( JSON.stringify( messages, undefined, 4 ) );
});
As your context says "all messages" and not something that would otherwise be implied by a grouping operator such as with .aggregate()
or the .group()
collection method. So the messages a "grouped together" via sorting, rather than any particular grouping.
正如您的上下文所说的“所有消息”而不是分组运算符所暗示的内容,例如.aggregate()或.group()集合方法。所以消息通过排序“组合在一起”,而不是任何特定的分组。
The latter sounds like what you are really asking, but if you actually intended real "grouping" then there is the aggregation example along with how to use .populate()
with that.
后者听起来像你真正要求的,但如果你真的打算真正的“分组”,那么就有聚合示例以及如何使用.populate()。
#1
12
Example using $lookup populate, lookup populates as an array, hence the $unwind.
使用$ lookup populate的示例,lookup填充为数组,因此$ unwind。
Message.aggregate(
[
{ "$match": { "to": user } },
{ "$sort": { "date": 1 } },
{ "$group": {
"_id": "from",
"to": { "$first": "$to" },
"message": { "$first": "$message" },
"date": { "$first": "$date" },
"origId": { "$first": "$_id" }
}},
{ "$lookup": {
"from": "users",
"localField": "from",
"foreignField": "_id",
"as": "from"
}},
{ "$lookup": {
"from": "users",
"localField": "to",
"foreignField": "_id",
"as": "to"
}},
{ "$unwind": { "path" : "$from" } },
{ "$unwind": { "path" : "$to" } }
],
function(err,results) {
if (err) throw err;
return results;
}
)
#2
9
The better option to use here is .aggregate()
, which is a native code implementation unlike the .group()
method of MongoDB which uses the JavaScript engine to process results.
这里使用的更好选项是.aggregate(),这是一个本机代码实现,与MongoDB的.group()方法不同,后者使用JavaScript引擎处理结果。
Methods like .populate()
are not directly supported though, and this is by design since the aggregation pipeline and other methods do not strictly return a response that is based on the current model's schema. Since it would be wrong to "assume" that is what you are doing, it is just a raw object response.
但是直接支持像.populate()这样的方法,这是设计的,因为聚合管道和其他方法不会严格返回基于当前模型模式的响应。因为“假设”是你正在做的事情是错误的,它只是一个原始对象响应。
There is however nothing stopping you from "casting" the response as mongoose documents and then calling the model form of .populate()
with the required paths:
然而,没有什么能阻止你将响应“转换”为mongoose文档,然后使用所需的路径调用.populate()的模型形式:
Message.aggregate(
[
{ "$match": { "to": user } },
{ "$sort": { "date": 1 } },
{ "$group": {
"_id": "from",
"to": { "$first": "$to" },
"message": { "$first": "$message" },
"date": { "$first": "$date" },
"origId": { "$first": "$_id" }
}}
],
function(err,results) {
if (err) throw err;
results = result.map(function(doc) {
doc.from = doc._id
doc._id = doc.origId;
delete doc.origId;
return new Message( doc )
});
User.populate( results, { "path": "from to" }, function(err,results) {
if (err) throw err;
console.log( JSON.stringify( results, undefined, 4 ) );
});
}
)
Of course, that just really returns the $first
message from each "from" as is implied by the operator.
当然,这只是真正从运营商隐含的每个“来自”返回$ first消息。
Perhaps what you really mean by "group by" is actually to "sort":
也许你所说的“分组”实际意味着“排序”:
Message.find({ "to": user })
.sort({ "from": 1, "date": 1 })
.populate("from to")
.exec(function(err,messsages) {
if (err) throw err;
console.log( JSON.stringify( messages, undefined, 4 ) );
});
As your context says "all messages" and not something that would otherwise be implied by a grouping operator such as with .aggregate()
or the .group()
collection method. So the messages a "grouped together" via sorting, rather than any particular grouping.
正如您的上下文所说的“所有消息”而不是分组运算符所暗示的内容,例如.aggregate()或.group()集合方法。所以消息通过排序“组合在一起”,而不是任何特定的分组。
The latter sounds like what you are really asking, but if you actually intended real "grouping" then there is the aggregation example along with how to use .populate()
with that.
后者听起来像你真正要求的,但如果你真的打算真正的“分组”,那么就有聚合示例以及如何使用.populate()。