我有一系列反馈评级(通讯,及时性和交付),所有这些都可以包含1-5值。我需要的是计算每个评级中有多少人评定5星,然后是4,然后是3,然后是1。
这是我的用户架构
var UserSchema = new mongoose.Schema({
username: String,
fullname: String,
email: {
type: String,
lowercase: true,
unique: true
},
password: String,
feedback: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Feedback'
}]
});
反馈架构
var FeedbackSchema = new mongoose.Schema({
postname: String,
userWhoSentFeedback: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
message: String,
feedbacktype: String,
thumbsup: Boolean,
rating: {
delivery: Number,
timeliness: Number,
communication: Number
}
});
到目前为止,我使用$ match,$ unwind,$ lookup和$ count尝试获取值但失败了。这是我正在运行的代码...
router.get('/testagg', (req, res)=>{
User.aggregate([
{"$match":
{ "username": "user1"}
},
{
"$lookup": {
"from": "feedbacks",
"localField": "feedback",
"foreignField": "_id",
"as": "outputResult"
}
}, {"$unwind": "$outputResult"},
{"$match": {"outputResult.rating.communication": {$eq: 1}}},{"$count": 'Communication_1'},
{
"$project": {
outputResult: 1,
Communication_1: 1
}
}
], (err, user)=>{
console.log(user)
res.json(user);
})
})
使用此代码,这是我得到的结果
[
{
"Communication_1": 1
}
]
所以我试图通过实施多个$ match(这不起作用)来获取通信的所有评级数字
router.get('/testagg', (req, res)=>{
User.aggregate([
{"$match":
{ "username": "user1"}
},
{
"$lookup": {
"from": "feedbacks",
"localField": "feedback",
"foreignField": "_id",
"as": "outputResult"
}
}, {"$unwind": "$outputResult"},
{"$match": {"outputResult.rating.communication": {$eq: 1}}},{"$count": 'Communication_1'},
{"$match": {"outputResult.rating.communication": {$eq: 2}}},{"$count": 'Communication_2'},
{"$match": {"outputResult.rating.communication": {$eq: 3}}},{"$count": 'Communication_3'},
{"$match": {"outputResult.rating.communication": {$eq: 4}}},{"$count": 'Communication_4'},
{"$match": {"outputResult.rating.communication": {$eq: 5}}},{"$count": 'Communication_5'},
{
"$project": {
outputResult: 1,
Communication_1: 1,
Communication_2: 1,
Communication_3: 1,
Communication_4: 1,
Communication_5: 1
}
}
], (err, user)=>{
console.log(user)
res.json(user);
})
})
但我得到了一个空洞的回应。所以我认为我做错了。
任何帮助将不胜感激!谢谢!
**更新
我也试过这段代码。只是为了获得1和4的通信值。
router.get('/testagg', (req, res)=>{
User.aggregate([
{"$match":
{ "username": "user1"}
},
{
"$lookup": {
"from": "feedbacks",
"localField": "feedback",
"foreignField": "_id",
"as": "outputResult"
}
}, {"$unwind": "$outputResult"}, {
"$project": {
"outputResult.rating": 1,
comm1: { $cond: [{$eq: ['$outputResult.rating.communication', 1]}, 1, 0]},
comm4: { $cond: [{$eq: ['$outputResult.rating.communication', 4]}, 1, 0]}
}
},
{ $group: {
_id: '$outputResult.rating',
total: { $sum: 1 },
comm1: { $sum: '$comm1'},
comm4: { $sum: '$comm4'}
}
}
], (err, user)=>{
console.log(user)
res.json(user);
})
})
这是我得到的结果
[
{
"_id": {
"communication": 1,
"timeliness": 1,
"delivery": 1
},
"total": 1,
"comm1": 1,
"comm4": 0
},
{
"_id": {
"communication": 5,
"timeliness": 5,
"delivery": 5
},
"total": 1,
"comm1": 0,
"comm4": 0
},
{
"_id": {
"communication": 4,
"timeliness": 4,
"delivery": 5
},
"total": 1,
"comm1": 0,
"comm4": 1
}
]
嗯,这是重要的,但这不是我想要的,我想要的是每个评级的总数
这是我想要的输出
{
"comm1" : 1,
"comm2" : 0,
"comm3" : 0,
"comm4" : 1,
"comm5" : 1
}
答案 0 :(得分:1)
这里可能更大的问题是你实际上是跨文档汇总的吗?或者你实际上只是想要一个"单个用户"?因为这对于你应该如何"真的有所不同。接近这个。但是,让我们根据您可以采取的措施来解决问题。
老实说,我认为你正在以错误的方式看待这个问题。如果您要获得多个密钥的多个评级("通信","交付","及时性"),那么尝试命名为"命名密钥"对于每一个都是繁琐和笨拙的。事实上恕我直言,这样的输出是彻头彻尾的"凌乱"。
正如我所看到的,你最好使用自然结构来制作"列表"这是一个"数组",您可以在以后的代码中轻松迭代和访问。
为此,我建议您寻找包含每个"键"的结果。然后包含它自己的数组"得分"和他们的"不同的数量"你实际上是在追求它是一种更清洁和机器可读的方法,因为替代方法意味着“迭代密钥”#34;在后面的代码中,然后真的"凌乱"。
所以,考虑到你实际上已经考虑到了#34;聚合"跨文档,那么你应该做这样的事情:
User.aggregate(
[
{ "$match": { "username": "user1" }
{ "$lookup": {
"from": "feedbacks",
"localField": "feedback",
"foreignField": "_id",
"as": "feedback"
}},
{ "$unwind": "$feedback" },
{ "$project": {
"username": 1,
"feedback": [
{ "k": "delivery", "v": "$feedback.rating.delivery" },
{ "k": "timeliness", "v": "$feedback.rating.timeliness" },
{ "k": "communication", "v": "$feedback.rating.communication" }
]
}},
{ "$unwind": "$feedback" },
{ "$group": {
"_id": {
"username": "$username",
"type": "$feedback.k",
"score": "$feedback.v",
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": {
"username": "$_id.username",
"type": "$_id.type"
},
"scores": {
"$push": {
"score": "$_id.score",
"count": "$count"
}
}
}},
{ "$group": {
"_id": "$_id.username",
"feedback": {
"$push": {
"type": "$_id.type",
"scores": "$scores"
}
}
}}
],
function(err,users) {
}
)
此处的基本流程是在$lookup
到"加入"之后,您$unwind
生成的数组,因为您希望汇总其中的详细信息" #34;无论如何。接下来我们要做的就是让你上面提到的"键"数组的成员也是如此。这是因为为了进一步处理这个问题,我们还将$unwind
该内容根据"反馈"生成有效的新文档。会员以及"每个键"。
接下来的几个阶段都是用$group
完成的,这些是按顺序进行的:
分组并计算" distinct"根据所提供的"用户名"形成的每个分数的反馈密钥。
分组"键"按"用户名"分隔文件,添加"分数"和他们对阵列的不同计数。
仅使用"用户名"还要为每个键创建一个数组条目"包含"分数"阵列也是如此。
另一种方法是考虑到你只需要一个单一的用户名",而这些数据是在一个单一的文件中获得的。无论如何。因此,你真正想要做的唯一事情是在服务器上#34;是$lookup
执行"加入"。在那之后,处理"反馈"更加简单。带有客户端代码的数组,产生完全相同的不同结果。
只需使用$lookup
进行连接,然后处理结果:
User.aggregate(
[
{ "$match": { "username": "user1" }
{ "$lookup": {
"from": "feedbacks",
"localField": "feedback",
"foreignField": "_id",
"as": "feedback"
}},
],
function(err,users) {
users = users.map(doc => {
doc.feedback = [].concat.apply([],doc.feedback.map(
r => Object.keys(r.rating).map(k => ({ k: k, v: r.rating[k] }) )
)).reduce((a,b) => {
if ( a.findIndex(e => JSON.stringify({ k: e.k , v: e.v }) == JSON.stringify(b) ) != -1 ) {
a[a.findIndex(e => JSON.stringify({ k: e.k , v: e.v }) == JSON.stringify(b) )].count += 1;
} else {
a = a.concat([{ k: b.k, v: b.v, count: 1 }]);
}
return a;
},[]).reduce((a,b) => {
if ( a.findIndex(e => e.type == b.k) != -1 ) {
a[a.findIndex(e => e.type == b.k)].scores.push({ score: b.v, count: b.count })
} else {
a = a.concat([{ type: b.k, scores: [{ score: b.v, count: b.count }] }]);
}
return a;
},[]);
return doc;
});
res.json(users)
}
)
事实上,如果它是一个单一的"用户,那么你最后可能不会res.json(users[0])
,因为.aggregate()
的结果始终是一个数组,无论返回多少结果。
这实际上只是在"feedback"
数组上使用.map()
和.reduce()
JavaScript函数,以便重新整形并返回"不同的计数"。应用了相同类型的方法,但如果这确实是单个文档响应,或者甚至是一个小的响应而没有实际需要跨文档聚合,那么它就更清晰,可能和#34;快"处理方式。
理论上我们可以写一个非常复杂的"聚合管道的版本,对于单个文档执行完全相同的步骤,如此处的代码所示。然而,它真的很复杂"并且依赖于现代方法,对于真正没有意义的东西,减少"是从服务器传输的数据。
所以如果你需要这个"跨文件"然后在第一个列表中使用完整聚合管道。但如果你只需要一个"单个用户"一次,或者"截然不同的结果"对于小型选择的每个用户,客户端处理代码是可行的方法。
两者都产生相同的输出,正如我所提到的那样,它比你前进的方向更友好,并且根据需要在更多代码中更容易处理:
{
"_id" : "user 1",
"feedback" : [
{
"type" : "communication",
"scores" : [
{
"score" : 2,
"count" : 2
},
{
"score" : 4,
"count" : 1
},
{
"score" : 5,
"count" : 1
},
{
"score" : 3,
"count" : 1
}
]
},
{
"type" : "delivery",
"scores" : [
{
"score" : 3,
"count" : 1
},
{
"score" : 4,
"count" : 1
},
{
"score" : 2,
"count" : 1
},
{
"score" : 5,
"count" : 2
}
]
},
{
"type" : "timeliness",
"scores" : [
{
"score" : 1,
"count" : 1
},
{
"score" : 5,
"count" : 1
},
{
"score" : 4,
"count" : 1
},
{
"score" : 3,
"count" : 1
},
{
"score" : 2,
"count" : 1
}
]
}
]
}