mongodb聚合查询不是';t使用$sum返回正确的sum
我收集了以下格式的文件:mongodb聚合查询不是';t使用$sum返回正确的sum,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我收集了以下格式的文件: { _id:"53fe74a866455060e003c2db", name:"sam", subject:"maths", marks:"77" } { _id:"53fe79cbef038fee879263d2", name:"ryan", subject:"bio", marks:"82" } { _id:"53fe74a866456060e003c2de", name:"tony", subject:"maths", marks:"86
{
_id:"53fe74a866455060e003c2db",
name:"sam",
subject:"maths",
marks:"77"
}
{
_id:"53fe79cbef038fee879263d2",
name:"ryan",
subject:"bio",
marks:"82"
}
{
_id:"53fe74a866456060e003c2de",
name:"tony",
subject:"maths",
marks:"86"
}
我想计算一下所有主科为“数学”的学生的总分。所以我应该得到163作为总数
db.students.aggregate([{ $match : { subject : "maths" } },
{ "$group" : { _id : "$subject", totalMarks : { $sum : "$marks" } } }])
现在我应该得到以下结果-
{"result":[{"_id":"53fe74a866455060e003c2db", "totalMarks":163}], "ok":1}
但我明白了-
{"result":[{"_id":"53fe74a866455060e003c2db", "totalMarks":0}], "ok":1}
有人能指出我在这里可能做错了什么吗?您当前的模式将
字段数据类型标记为string,并且您需要一个整数数据类型用于聚合框架来计算总和。另一方面,您可以使用来计算总和,因为它允许在其映射函数中对对象属性使用本机JavaScript方法,如parseInt()
。总的来说,你有两个选择
选项1:更新架构(更改数据类型)
第一种方法是更改模式或在文档中添加另一个具有实际数值而不是字符串表示形式的字段。如果您的集合文档大小相对较小,则可以使用mongodb的游标find()
、forEach()
和update()
方法组合来更改标记模式:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
对于相对较大的集合大小,您的db性能将很慢,建议使用以下方法:
MongoDB版本>=2.6和<3.2:
var bulk = db.student.initializeUnorderedBulkOp(),
counter = 0;
db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "marks": parseInt(doc.marks) }
});
counter++;
if (counter % 1000 === 0) {
// Execute per 1000 operations
bulk.execute();
// re-initialize every 1000 update statements
bulk = db.student.initializeUnorderedBulkOp();
}
})
// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute();
var ops = [],
cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});
cursor.forEach(function (doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id } ,
"update": { "$set": { "marks": parseInt(doc.marks) } }
}
});
if (ops.length === 1000) {
db.student.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) db.student.bulkWrite(ops);
MongoDB 3.2版及更新版本:
var bulk = db.student.initializeUnorderedBulkOp(),
counter = 0;
db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "marks": parseInt(doc.marks) }
});
counter++;
if (counter % 1000 === 0) {
// Execute per 1000 operations
bulk.execute();
// re-initialize every 1000 update statements
bulk = db.student.initializeUnorderedBulkOp();
}
})
// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute();
var ops = [],
cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});
cursor.forEach(function (doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id } ,
"update": { "$set": { "marks": parseInt(doc.marks) } }
}
});
if (ops.length === 1000) {
db.student.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) db.student.bulkWrite(ops);
选项2:运行MapReduce
第二种方法是使用JavaScript函数parseInt()
重写查询
在操作中,定义处理每个输入文档的映射函数。此函数将转换后的标记
字符串值映射到每个文档的主题
,并发出主题
和转换后的标记对。这就是可以应用JavaScript本机函数parseInt()
的地方。注意:在函数中,此
指地图缩减操作正在处理的文档:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
接下来,使用两个参数keySubject
和valuesMarks
定义相应的reduce函数valuesMarks
是一个数组,其元素是由map函数发出的整数标记
值,并按keySubject
分组。
该函数将值标记
数组减少为其元素之和
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
对于您的集合,上述内容将把您的MapReduce聚合结果放入一个新集合db。示例\u results
。因此,db.example\u results.find()
将输出:
/* 0 */
{
"_id" : "maths",
"value" : 163
}
您当前的模式将
字段数据类型标记为字符串,您需要一个整数数据类型,以便聚合框架计算出总和。另一方面,您可以使用来计算总和,因为它允许在其映射函数中对对象属性使用本机JavaScript方法,如parseInt()
。总的来说,你有两个选择
选项1:更新架构(更改数据类型)
第一种方法是更改模式或在文档中添加另一个具有实际数值而不是字符串表示形式的字段。如果您的集合文档大小相对较小,则可以使用mongodb的游标find()
、forEach()
和update()
方法组合来更改标记模式:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
对于相对较大的集合大小,您的db性能将很慢,建议使用以下方法:
MongoDB版本>=2.6和<3.2:
var bulk = db.student.initializeUnorderedBulkOp(),
counter = 0;
db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "marks": parseInt(doc.marks) }
});
counter++;
if (counter % 1000 === 0) {
// Execute per 1000 operations
bulk.execute();
// re-initialize every 1000 update statements
bulk = db.student.initializeUnorderedBulkOp();
}
})
// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute();
var ops = [],
cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});
cursor.forEach(function (doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id } ,
"update": { "$set": { "marks": parseInt(doc.marks) } }
}
});
if (ops.length === 1000) {
db.student.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) db.student.bulkWrite(ops);
MongoDB 3.2版及更新版本:
var bulk = db.student.initializeUnorderedBulkOp(),
counter = 0;
db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "marks": parseInt(doc.marks) }
});
counter++;
if (counter % 1000 === 0) {
// Execute per 1000 operations
bulk.execute();
// re-initialize every 1000 update statements
bulk = db.student.initializeUnorderedBulkOp();
}
})
// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute();
var ops = [],
cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});
cursor.forEach(function (doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id } ,
"update": { "$set": { "marks": parseInt(doc.marks) } }
}
});
if (ops.length === 1000) {
db.student.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) db.student.bulkWrite(ops);
选项2:运行MapReduce
第二种方法是使用JavaScript函数parseInt()
重写查询
在操作中,定义处理每个输入文档的映射函数。此函数将转换后的标记
字符串值映射到每个文档的主题
,并发出主题
和转换后的标记对。这就是可以应用JavaScript本机函数parseInt()
的地方。注意:在函数中,此
指地图缩减操作正在处理的文档:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
接下来,使用两个参数keySubject
和valuesMarks
定义相应的reduce函数valuesMarks
是一个数组,其元素是由map函数发出的整数标记
值,并按keySubject
分组。
该函数将值标记
数组减少为其元素之和
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
对于您的集合,上述内容将把您的MapReduce聚合结果放入一个新集合db。示例\u results
。因此,db.example\u results.find()
将输出:
/* 0 */
{
"_id" : "maths",
"value" : 163
}
返回金额0的可能原因有:
求和的字段不是整数,而是字符串
确保该字段包含数值。
您使用了错误的$sum语法
db.c1.aggregate([{
$group:{
_id:“$item”,
价格:{
$sum:“$price”
},
计数:{
$sum:1
}
}
}])
确保使用“$price”而不是“price”
发生此错误的最愚蠢的错误之一是:
指定字段名时在引号内使用空格或制表符
示例-“$price”不起作用但是,“$price”会起作用。
返回金额0的可能原因有:
求和的字段不是整数,而是字符串
确保该字段包含数值。
您使用了错误的$sum语法<