MongoDB 根据现有字段计算分数并将其放入同一集合中的新字段中

标签 mongodb

我正在 Mongodb 上工作,我有一个集合,比如说 Collection1 .

我必须根据 Collection1 中的现有字段计算分数,并将结果放入新字段 Field8Collection1 .

集合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
        }

如何实现上述目标?

最佳答案

根据您的应用程序需求,您可以使用聚合框架来计算分数并使用 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(); }    

关于MongoDB 根据现有字段计算分数并将其放入同一集合中的新字段中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36926618/

相关文章:

node.js - 使用 mongoose 转义一些 HTML 标签

node.js - 在 mongoose 中使用 mongodb 多键索引方法索引引用数组

node.js - iOS Swift Parse-Server 用户登录认证

javascript - 将 mongodb 集合项添加到不和谐嵌入中

mongodb - 如何通过r2d2和actix在MongoDB中保存文档?

javascript - MongoDB:您可以将一个集合作为字段包含在另一个集合的文档中吗?

node.js - Nodejs用户注册表单报错

node.js - 如何从mapReduce mongoose 中的键获取大约值?

Node.js 大文件上传到 MongoDB 阻塞事件循环和工作池

node.js - 我的文档在 NodeJS 的 mongodb 中没有被删除