根据条件计算现有字段的分数 [英] Calculate a score from an existing fields with conditions

查看:66
本文介绍了根据条件计算现有字段的分数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用MongoDB 2.6.9和NodeJs 0.10.37,并且有一个集合vols,它表示航班.

I'm working on MongoDB 2.6.9 and NodeJs 0.10.37 and I have a collection vols which means flights.

> db.vols.findOne()
{
    "_id" : ObjectId("5717a5d4578f3f2556f300f2"),
    "Orig" : "AGP",
    "Dest" : "OTP",
    "Flight" : 126,
    "Routing" : "AGP-OTP",
    "Stops" : 0,
    "Seats" : 169,
    "Ops_Week" : 3,
    "Eff_Date" : "2016-04-14",
    "Mkt_Al" : "0B",
    "Dep_Time" : 1110,
    "Thru_Point" : "",
    "Arr_Time" : 1600,
    "Block_Mins" : 230

}

每个文档均指某航空公司的一次航班,并提供了详细信息,例如,上一个文档指的是直接完成的航班(停靠点:0).但是下一个航班是停下来的.

Each document refers to one flight done by an Airline Company and it gives details, for instance, the previous document refers to a flight done directly ( Stops : 0 ). But the next one, the flight was with stop.

db.vols.findOne({Stops:1})
{
    "_id" : ObjectId("5717a5d4578f3f2556f301c5"),
    "Orig" : "CEK",
    "Dest" : "IKT",
    "Flight" : 7756,
    "Routing" : "KZN-CEK-OVB-IKT",
    "Stops" : 1,
    "Seats" : 70,
    "Ops_Week" : 2,
    "Eff_Date" : "2016-04-11",
    "Mkt_Al" : "2G",
    "Dep_Time" : 1655,
    "Thru_Point" : "OVB",
    "Arr_Time" : 140,
    "Block_Mins" : 345
}

重要提示:

每个Airline在每条路线(Origin-Destination)中都有一个score

Each Airline has a score in every route ( Origin - Destination )

如何计算分数?

因此,我需要进行这些计算并将新的字段"QSI"插入到我的集合vols中.

So, I need to do these calculations and insert a new Field " QSI " into my collection vols.

重要提示:

c4中的平均经过时间表示:

Average elapsed time in c4 means this :

例如,我们有一个停靠的航班,例如:从 B A C 的航班,例如,整个航班60分钟,但从 A B 花费20分钟,从 B C 花费20分钟,这个平均值应该返回40分钟.

For example we have a flight with stop let's say : a flight from A to C by B, the whole flight makes for example 60 min, but from A to B makes 20 min and from B to C makes 20 min, this average should return 40 min.

我尝试了此解决方案,但对于c4来说,事情看起来并不像是:

I tried this Solution, but for c4 things don't look like smething work :

var mongoose = require('mongoose'),
    express  = require('express'),
    Schema   = mongoose.Schema;

mongoose.connect('mongodb://localhost/ramtest');

var volsSchema = new Schema({}, { strict : false, collection : 'vols' });
var MyModel    = mongoose.model("MyModel", volsSchema);

mongoose.set('debug', true);

mongoose.connection.on("open", function(err) {
  if (err) throw err;

  var bulkUpdateOps = MyModel.collection.initializeUnorderedBulkOp(),
      counter       = 0;

  MyModel.find({}).lean().exec(function(err, docs) {
    if (err) throw err;

    docs.forEach(function(doc) {
      // computations
      var c1, c2, c3, c4, qsi, first_leg, second_leg, total_flight;

      c1 = 0.3728 + (0.00454 * doc.Seats);
      c2 = (doc.Stops == 1) ? 0.03 : 1;
      c3 = doc.Ops_Week;

      if (doc.Stops == 1) {
        var Mkt_Air        = doc.Mkt_Al,
            Origin         = doc.Orig,
            Destination    = doc.Dest,
            Thru_Point     = doc.Thru_Point,
            Effective_Date = doc.Eff_Date,
            Block_Mins     = doc.Block_Mins;

        MyModel.find({ Mkt_Al : Mkt_Air }, { Orig : Origin }, { Dest : Thru_Point }, { Eff_Date : Effective_Date }).lean().exec(function(err, docs) {
          docs.forEach(function(doc) {
            var first_leg = doc.Block_Mins;
            MyModel.find({ Mkt_Al : Mkt_Air }, { Orig : Thru_Point }, { Dest : Destination }, { Eff_Date : Effective_Date }).lean().exec(function(err, docs) {
              docs.forEach(function(doc) {
                var second_leg = doc.Block_Mins, total_flight = second_leg + first_leg;
                c4 = Math.pow((Block_Mins / total_flight), -0.675);
                qsi = c1 * c2 * c3 * c4;
              }); // the end of docs.forEach(function (doc){
            }); // the end of MyModel.find..
          }); // the end of docs.forEach(function (doc){
        }); // the end of MyModel.find..
      } // end if
      else {
        c4 = 1;
      }

      qsi = c1 * c2 * c3 * c4;

      counter++;

      bulkUpdateOps.find({ "_id" : doc._id }).updateOne({
        "$set" : { "Qsi" : qsi }
      });

      if (counter % 500 == 0) {
        bulkUpdateOps.execute(function(err, result) {
          if (err) throw err;
          bulkUpdateOps = MyModel.collection.initializeUnorderedBulkOp();
          console.log(result);
          console.log(doc);
        });
      }

    });

    if (counter % 500 != 0) {
      bulkUpdateOps.execute(function(err, result) {
        if (err) throw err;
        console.log(result);
      });
    }
  });

  var app = express();
  app.listen(3000, function() {
    console.log('Ready to calculate and insert the QSI');
  });
});

问题:

我认为问题出在MyModel.find上,例如如果我在此指令内丢失数据...,则Stops = 0时,我的score的计算是干净的,但是如果Stops = 1,我的分数取值为,并且在像callback(null, docs)这样的迭代之后出现错误,请谁提供帮助??

I think that the problem is with MyModel.find like if i lose data inside this instruction ..., my score is calculated cleanly when Stops = 0, but if Stops = 1, My score takes the value Nan, and I have an error after some iterations like that callback(null, docs) please who can help ??

如何实现以上目标?

推荐答案

您的实现存在几个问题.首先,您使用的是 find() 方法您为查询指定了太多参数时,输入错误:

There are several issues with your implementation. Firstly, you are using the find() method incorrectly as you are specifying too many arguments for the query:

MyModel.find(
    { Mkt_Al : Mkt_Air }, 
    { Orig : Origin }, 
    { Dest : Thru_Point }, 
    { Eff_Date : Effective_Date }
).lean().exec(function(err, docs) { .. }

应该是

MyModel.find({ 
    Mkt_Al: Mkt_Air, 
    Orig: Origin, 
    Dest: Thru_Point, 
    Eff_Date: Effective_Date 
}).lean().exec(function(err, docs) { ... }


同样,您不应该使用 find() 方法,因为您只需要一个与查询匹配的文档即可在计算中使用.从上一个封闭的问题中提取复杂的算法:


Again, you shouldn't be using the find() method in this instance because you only need a single document that matches the query to use in your computations. Taking the complex algorithm from your previous closed question:

现在,我要计算一个得分 c4 并将其插入我的收藏中:

Now I want to calculate a score c4 and insert it into my collection :

为此,我应该像这样计算一个值 c4 :

To do that I should calculate a value c4 just like this :

1)首先,我要验证每个文档是否为 ( Field2 == 1 ) ,如果是真的,我 继续,否则很简单 c4 取值1.

1) First I verify for each document if ( Field2 == 1 ) if it's true I continue else it's simple c4 takes value 1.

2)然后我应该做一个"for"循环,看看哪个文档验证了这些 条件: doc.Field1 == this.Field1 && doc.Field6 == this.Field6 && doc.Field7 == this.Field8

2) Then I should make a loop "for" and see which document verify these conditions : doc.Field1 == this.Field1 && doc.Field6 == this.Field6 && doc.Field7 == this.Field8

3)然后,我将 doc.Field4 添加到另一个文档的 Field4

3) Then I take doc.Field4 wich will be added to another document's Field4

4)我继续,进行另一个循环并查找另一个文档 验证这些条件:

4) I continue and I make another loop and look for another document wich verify these conditions :

它应该具有相同的 Field1 ,就像之前的文档及其文档一样 Field6 等于上一个文档 Field7 及其 Field8 就像第一个文档中的Field8一样

it should have the same Field1 just like the previous document and its Field6 equal to the previous document Field7 and its Field8 the same as Field8 in the first document

5)然后,我选择 doc.Field4 并将其添加到上一个 doc.Field4

5) Then I take doc.Field4 and add it to the previous doc.Field4

使用MyModel.findOne()应该足以完成上面的任务3、4和5.但是,由于调用的异步性质,您将需要嵌套查询,但是幸运的是嵌套调用的深度不大于3,否则您将发现自己有一张通往Callback Hell的单程票.为了避免这些常见的陷阱,最好使用 Promises (因为默认情况下本地猫鼬查询可以返回 Promise )或使用 node-async 程序包处理此类情况的功能数量.

Using MyModel.findOne() should suffice for tasks 3, 4 and 5 above. However, because of the asynchronous nature of the calls, you would need to nest the queries but fortunately the depth of the nested calls is not greater than 3 otherwise you will find yourself with a one-way ticket to Callback Hell. To avoid these common pitfalls, better to use Promises (since the native mongoose queries by default can return a Promise) or use the node-async package which includes a number of functions for dealing with situations like this.

如果使用 async 库,它可以有效地使您运行多个相互依赖的异步任务(例如MyModel.findOne()调用),并在它们全部完成时执行其他操作.在上面,您可以使用 async.series() 方法

If using the async library, it efficiently allows you to run run multiple asynchronous tasks (like the MyModel.findOne() calls) that depend on each other and when they all finish do something else. In the above, you could use the async.series() method.

下面的示例演示了上述概念,您可以从测试数据库中的以下示例文档中计算 Qsi .

The following example demonstrates the above concept where you can calculate the Qsi from the following sample documents in the test db.

填充测试数据库的vol集合:

db.vols.insert([
    {    
        "Mkt_Al" : "2G",
        "Stops" : 0,
        "Seats" : 169,
        "Block_Mins" : 230,                
        "Ops_Week" : 3,        
        "Orig" : "AGP",
        "Dest" : "OTP",
        "Thru_Point" : "",
    },
    {    
        "Mkt_Al" : "2G",
        "Stops" : 1,
        "Seats" : 260,              
        "Block_Mins" : 260,
        "Ops_Week" : 2,  
        "Orig" : "CEK",
        "Dest" : "IKT",
        "Thru_Point" : "OVB",
    },
    {    
        "Mkt_Al" : "2G",
        "Stops" : 0,
        "Seats" : 140,
        "Block_Mins" : 60,
        "Ops_Week" : 2,        
        "Orig" : "BEK",
        "Dest" : "OTP",
        "Thru_Point" : "",
    },
    {    
        "Mkt_Al" : "2G",
        "Stops" : 0,
        "Seats" : 160,
        "Block_Mins" : 90,
        "Ops_Week" : 3,        
        "Orig" : "CEK",
        "Dest" : "OVB",
        "Thru_Point" : "",
    },
    {    
        "Mkt_Al" : "2G",        
        "Stops" : 0,
        "Seats" : 60,
        "Block_Mins" : 50,
        "Ops_Week" : 3,        
        "Orig" : "OVB",
        "Dest" : "IKT",
        "Thru_Point" : "",
    }
])

Node.js应用:

var mongoose = require('mongoose'),
    express = require('express'),
    async = require('async'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');
var volSchema = new Schema({},{ strict: false, collection: 'vols' }),    
    Vol = mongoose.model("Vol", volSchema);

mongoose.set('debug', false);

mongoose.connection.on("open", function (err) {
    if (err) throw err;  
    var bulkUpdateOps = Vol.collection.initializeUnorderedBulkOp(), 
        counter = 0;

    Vol.find({}).lean().exec(function (err, docs) {
        if (err) throw err; 
        var locals = {};

        docs.forEach(function(doc) {            
            locals.c1 = 0.3728 + (0.00454 * doc.Seats);         
            locals.c3 = doc.Ops_Week;

            if (doc.Stops == 1) {               
                async.series([
                    // Load doc with first leg first
                    function(callback) {
                        Vol.findOne({ 
                            Mkt_Al: doc.Mkt_Al,
                            Orig: doc.Orig,
                            Dest: doc.Dest                          
                        }).lean().exec(function (err, flight) {
                            if (err) return callback(err);
                            locals.first_leg = flight.Block_Mins;
                            callback();
                        });
                    },
                    // Load second leg doc 
                    // (won't be called before task 1's "task callback" 
                    // has been called)
                    function(callback) {                    
                        Vol.findOne({ 
                            Mkt_Al: doc.Mkt_Al,
                            Orig: doc.Thru_Point,
                            Dest: doc.Dest                          
                        }).lean().exec(function (err, flight) {
                            if (err) return callback(err);
                            locals.second_leg = flight.Block_Mins;
                            callback();
                        });
                    }
                ], function(err) { // This function gets called after the
                    // two tasks have called their "task callbacks"
                    if (err) throw err;
                    // Here locals will be populated with `first_leg` 
                    // and `second_leg`
                    // Just like in the previous example
                    var total_flight = locals.second_leg + locals.first_leg;                    
                    locals.c2 = 0.03;
                    locals.c4 = Math.pow((doc.Block_Mins / total_flight), -0.675);                    

                }); 
            } else {
                locals.c2 = 1;
                locals.c4 = 1;
            }

            counter++;
            console.log(locals);
            bulkUpdateOps.find({ "_id" : doc._id }).updateOne({ 
                "$set": { 
                    "Qsi": (locals.c1 * locals.c2 * locals.c3 * locals.c4) 
                } 
            });

            if (counter % 500 == 0) {
               bulkUpdateOps.execute(function(err, result) {          
                    if (err) throw err; 
                    bulkUpdateOps = Vol.collection.initializeUnorderedBulkOp();                        
                });
            } 
        });

        if (counter % 500 != 0) {
            bulkUpdateOps.execute(function(err, result) {
                if (err) throw err; 
                console.log(result.nModified);                
            });
        }   
    });
});

示例输出:

db.vols.find()

/* 1 */
{
    "_id" : ObjectId("5767e7549ebce6d574702221"),
    "Mkt_Al" : "2G",
    "Stops" : 0,
    "Seats" : 169,
    "Block_Mins" : 230,
    "Ops_Week" : 3,
    "Orig" : "AGP",
    "Dest" : "OTP",
    "Thru_Point" : "",
    "Qsi" : 3.42018
}

/* 2 */
{
    "_id" : ObjectId("5767e7549ebce6d574702222"),
    "Mkt_Al" : "2G",
    "Stops" : 1,
    "Seats" : 260,
    "Block_Mins" : 260,
    "Ops_Week" : 2,
    "Orig" : "CEK",
    "Dest" : "IKT",
    "Thru_Point" : "OVB",
    "Qsi" : 3.1064
}

/* 3 */
{
    "_id" : ObjectId("5767e7549ebce6d574702223"),
    "Mkt_Al" : "2G",
    "Stops" : 0,
    "Seats" : 140,
    "Block_Mins" : 60,
    "Ops_Week" : 2,
    "Orig" : "BEK",
    "Dest" : "OTP",
    "Thru_Point" : "",
    "Qsi" : 2.0168
}

/* 4 */
{
    "_id" : ObjectId("5767e7549ebce6d574702224"),
    "Mkt_Al" : "2G",
    "Stops" : 0,
    "Seats" : 160,
    "Block_Mins" : 90,
    "Ops_Week" : 3,
    "Orig" : "CEK",
    "Dest" : "OVB",
    "Thru_Point" : "",
    "Qsi" : 3.2976
}

/* 5 */
{
    "_id" : ObjectId("5767e7549ebce6d574702225"),
    "Mkt_Al" : "2G",
    "Stops" : 0,
    "Seats" : 60,
    "Block_Mins" : 50,
    "Ops_Week" : 3,
    "Orig" : "OVB",
    "Dest" : "IKT",
    "Thru_Point" : "",
    "Qsi" : 1.9356
}

这篇关于根据条件计算现有字段的分数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆