我正在研究Mongodb,我有一个集合,比方说Collection1
。
我必须从Collection1
中的现有字段计算得分,并将此结果放入Field8
中的新字段Collection1
。
收藏1:
db.Collection1.find().pretty().limit(2) {
"_id": ObjectId("5717a5d4578f3f2556f300f2"),
"Field1": "XXXX",
"Field2": 0,
"Field3": 169,
"Field4": 230,
"Field5": "...4.67", // This field refer to days in a week
"Field6": "ZZ",
"Field7": "LO"
}, {
"_id": ObjectId("17a5d4575f300f278f3f2556"),
"Field1": "YYYY",
"Field2": 1,
"Field3": 260,
"Field4": 80,
"Field5": "1.3....", // This field refer to days in a week
"Field6": "YY",
"Field7": "PK"
}
因此,我必须使用此公式对我的第一个收集字段进行一些计算,但我不知道如何继续?:
Score = C1*C2*C3*C4
C1 = 10 + 0.03*field3
C2 = 1 or 0.03 it depends on field2 if it equals 1 or 0
C3 = 1 or 2 .... or 7, it depends on field5 for example C3 for this document "Field5": "...4.67" should return 3, it means three days per week
C4 = 1 or field4^-0.6 if field2 equals 0 or 1
计算完这个分数后,我应该把它放在Field8
的新字段Collection1
中,得到的内容就像这样:
db.Collection1.find().pretty().limit(2) {
"_id": ObjectId("5717a5d4578f3f2556f300f2"),
"Field1": "XXXX",
"Field2": 0,
"Field3": 169,
"Field4": 230,
"Field5": "...4.67", // This field refer to days in a week
"Field6": "ZZ",
"Field7": "LO",
"Field8": Score // My calculated score
}, {
"_id": ObjectId("17a5d4575f300f278f3f2556"),
"Field1": "YYYY",
"Field2": 1,
"Field3": 260,
"Field4": 80,
"Field5": "1.3....", // This field refer to days in a week
"Field6": "YY",
"Field7": "PK",
"Field8": Score // My calculated score
}
我如何实现上述目标?
答案 0 :(得分:1)
只需创建一个返回计算值的函数,然后在更新mongodb查询中调用。
喜欢
var cal = function(row){return row.Field1 + row.Field2 * row.Field3; //根据您的要求使用您的公式};
var rows = db.collection1.find()//可以使用你的过滤器;
rows.forEach(函数(行){db.collection1.update({" _id":row._id},{$设置:{"字段8":CAL(行)}})});
答案 1 :(得分:0)
根据您的应用程序需求,您可以使用聚合框架计算得分,并使用 bulkWrite()
更新您的收藏。请考虑以下示例,该示例使用 $project
管道步骤作为算术运算符的分数计算的余地。
由于在您的问题中计算C3
的逻辑是从1
到7
的数字恰好等于7 - number of points (.)
,我能想到的唯一可行的方法是在进行聚合之前,先存储一个保存此值的额外字段。因此,您的第一步是创建额外的字段,然后您可以使用 bulkWrite()
进行操作,如下所示:
第1步:修改架构以容纳额外的daysInWeek
字段
var counter = 0, bulkUpdateOps = [];
db.collection1.find({
"Field5": { "$exists": true }
}).forEach(function(doc) {
// calculations for getting the number of points in Field5
var points, daysInWeek;
points = (doc.Field5.match(new RegExp(".", "g")) || []).length;
daysInWeek = 7 - points;
bulkUpdateOps.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { "daysInWeek": daysInWeek }
}
}
});
counter++;
if (counter % 500 == 0) {
db.collection1.bulkWrite(bulkUpdateOps);
bulkUpdateOps = [];
}
});
if (counter % 500 != 0) { db.collection1.bulkWrite(bulkUpdateOps); }
理想情况下,上述操作还可以容纳计算问题中的其他常量,从而创建Field8
。但是我相信这样的计算应该在客户端完成,让MongoDB做它在服务器上做得最好的事情。
第2步:使用汇总添加Field8
字段
创建了额外的字段daysInWeek
之后,您可以构建一个聚合管道,使用arithmetic operators的同类群组来计算新变量以进行计算(同样,建议在应用程序层上进行此类计算) )。最终投影将是计算字段的乘积,然后您可以使用聚合结果游标迭代并将Field8
添加到每个文档的集合中:
var pipeline = [
{
"$project": {
"C1": {
"$add": [
10,
{ "$multiply": [ "$Field3", 0.03 ] }
]
},
"C2": {
"$cond": [
{ "$eq": [ "$Field2", 1 ] },
1,
0.03
]
},
"C3": "$daysInWeek",
"C4": {
"$cond": [
{ "$eq": [ "$Field2", 1 ] },
{ "$pow": [ "$Field4", -0.6 ] },
1
]
}
}
},
{
"$project": {
"Field8": { "$multiply": [ "$C1", "$C2", "$C3", "$C4" ] }
}
}
],
counter = 0,
bulkUpdateOps = [];
db.collection1.aggregate(pipeline).forEach(function(doc) {
bulkUpdateOps.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { "Field8": doc.Field8 }
}
}
});
counter++;
if (counter % 500 == 0) {
db.collection1.bulkWrite(bulkUpdateOps);
bulkUpdateOps = [];
}
});
if (counter % 500 != 0) { db.collection1.bulkWrite(bulkUpdateOps); }
对于MongoDB >= 2.6
和<= 3.0
,使用Bulk Opeartions API,您需要使用光标 forEach()
来迭代集合。方法,更新集合中的每个文档。
以上聚合管道中的一些算术运算符在MongoDB >= 2.6
和<= 3.0
中不可用,因此您需要在 forEach()
迭代。
使用批量API减少服务器写入请求,方法是将每个更新批量捆绑并仅在集合中的每500个文档中向服务器发送一次以进行处理:
var bulkUpdateOps = db.collection1.initializeUnorderedBulkOp(),
cursor = db.collection1.find(), // cursor
counter = 0;
cursor.forEach(function(doc) {
// computations
var c1, c2, c3, c4, Field8;
c1 = 10 + (0.03*doc.Field3);
c2 = (doc.Field2 == 1) ? 1: 0.03;
c3 = 7 - (doc.Field5.match(new RegExp(".", "g")) || []).length;
c4 = (doc.Field2 == 1) ? Math.pow(doc.Field, -0.6) : 1;
Field8 = c1*c2*c3*c4;
bulkUpdateOps.find({ "_id": doc._id }).updateOne({
"$set": { "Field8": Field8 }
});
if (counter % 500 == 0) {
bulkUpdateOps.execute();
bulkUpdateOps = db.collection1.initializeUnorderedBulkOp();
}
})
if (counter % 500 != 0) { bulkUpdateOps.execute(); }