mongodb 使用 $sum 时聚合查询未返回正确的总和
mongodb aggregate query isn't returning proper sum on using $sum
我收集了学生的文件,格式如下:-
{
_id:"53fe74a866455060e003c2db",
name:"sam",
subject:"maths",
marks:"77"
}
{
_id:"53fe79cbef038fee879263d2",
name:"ryan",
subject:"bio",
marks:"82"
}
{
_id:"53fe74a866456060e003c2de",
name:"tony",
subject:"maths",
marks:"86"
}
我想得到所有学科="maths"的学生的总分。所以我应该得到 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}
有人可以指出我这里可能做错了什么吗?
您当前的模式的 marks
字段数据类型为字符串,您的聚合框架需要一个整数数据类型来计算总和。另一方面,您可以使用 MapReduce 来计算总和,因为它允许在其映射函数的 object 属性上使用原生 JavaScript 方法,例如 parseInt()
。所以总的来说你有两个选择。
选项 1:更新架构(更改数据类型)
第一个是更改架构或在您的文档中添加另一个字段,该字段具有实际数值而不是字符串表示形式。如果您的 collection 文档大小相对较小,您可以使用 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) } }
);
});
对于相对较大的 collection 大小,您的数据库性能会很慢,建议为此使用 mongo bulk updates:
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();
MongoDB 3.2 及更新版本:
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
第二种方法是使用 MapReduce 重写您的查询,您可以在其中使用 JavaScript 函数 parseInt()
。
在您的 MapReduce 操作中,定义处理每个输入文档的映射函数。此函数将转换后的 marks
字符串值映射到每个文档的 subject
,并发出 subject
和转换后的 marks
对。这是可以应用 JavaScript 本机函数 parseInt()
的地方。注意:函数中this
指的是map-reduce操作正在处理的文档:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
接下来,用两个参数keySubject
和valuesMarks
定义相应的reduce函数。 valuesMarks
是一个数组,其元素是由 map 函数发出并按 keySubject
分组的整数 marks
值。
该函数将 valuesMarks
数组缩减为其元素的总和。
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
使用您的 collection,以上将把您的 MapReduce 聚合结果放在一个新的 collection db.example_results
中。因此,db.example_results.find()
将输出:
/* 0 */
{
"_id" : "maths",
"value" : 163
}
您的金额被退回 0 的可能原因是:
您求和的字段不是整数而是字符串
确保该字段包含数值。
您使用的 $sum 语法错误。
db.c1.aggregate([{
$group: {
_id: "$item",
price: {
$sum: "$price"
},
count: {
$sum: 1
}
}
}])
确保使用“$price”而不是 "price"。
导致此错误的最愚蠢的错误之一是:
在指定字段名称时使用 space 或引号内的制表符。
示例 - "$price" 无效!!! 但是,"$price" 有效。
我收集了学生的文件,格式如下:-
{
_id:"53fe74a866455060e003c2db",
name:"sam",
subject:"maths",
marks:"77"
}
{
_id:"53fe79cbef038fee879263d2",
name:"ryan",
subject:"bio",
marks:"82"
}
{
_id:"53fe74a866456060e003c2de",
name:"tony",
subject:"maths",
marks:"86"
}
我想得到所有学科="maths"的学生的总分。所以我应该得到 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}
有人可以指出我这里可能做错了什么吗?
您当前的模式的 marks
字段数据类型为字符串,您的聚合框架需要一个整数数据类型来计算总和。另一方面,您可以使用 MapReduce 来计算总和,因为它允许在其映射函数的 object 属性上使用原生 JavaScript 方法,例如 parseInt()
。所以总的来说你有两个选择。
选项 1:更新架构(更改数据类型)
第一个是更改架构或在您的文档中添加另一个字段,该字段具有实际数值而不是字符串表示形式。如果您的 collection 文档大小相对较小,您可以使用 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) } }
);
});
对于相对较大的 collection 大小,您的数据库性能会很慢,建议为此使用 mongo bulk updates:
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();
MongoDB 3.2 及更新版本:
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
第二种方法是使用 MapReduce 重写您的查询,您可以在其中使用 JavaScript 函数 parseInt()
。
在您的 MapReduce 操作中,定义处理每个输入文档的映射函数。此函数将转换后的 marks
字符串值映射到每个文档的 subject
,并发出 subject
和转换后的 marks
对。这是可以应用 JavaScript 本机函数 parseInt()
的地方。注意:函数中this
指的是map-reduce操作正在处理的文档:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
接下来,用两个参数keySubject
和valuesMarks
定义相应的reduce函数。 valuesMarks
是一个数组,其元素是由 map 函数发出并按 keySubject
分组的整数 marks
值。
该函数将 valuesMarks
数组缩减为其元素的总和。
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
使用您的 collection,以上将把您的 MapReduce 聚合结果放在一个新的 collection db.example_results
中。因此,db.example_results.find()
将输出:
/* 0 */
{
"_id" : "maths",
"value" : 163
}
您的金额被退回 0 的可能原因是:
您求和的字段不是整数而是字符串
确保该字段包含数值。
您使用的 $sum 语法错误。
db.c1.aggregate([{ $group: { _id: "$item", price: { $sum: "$price" }, count: { $sum: 1 } } }])
确保使用“$price”而不是 "price"。
导致此错误的最愚蠢的错误之一是:
在指定字段名称时使用 space 或引号内的制表符。
示例 - "$price" 无效!!! 但是,"$price" 有效。