MongoDB从现有字段计算得分并将其放在同一集合中的新字段中

时间:2016-04-28 22:33:54

标签: mongodb

我正在研究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
        }

我如何实现上述目标?

2 个答案:

答案 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的逻辑是从17的数字恰好等于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(); }