节点js + mongoose,内存不足,需要花费太多时间

时间:2015-11-06 02:05:47

标签: node.js mongodb memory mongoose time-complexity

所以我有这个问题。我正在使用节点js + mongoose,试图向用户推荐一部电影。背后的数学很简单,您可以查看其他用户对您的电影评分,找到与您相似的人,然后使用它来预测某部电影的评分。您为所有电影执行此操作,然后推荐具有最高预测评级的电影。在我的数据库中,我已经获得了100 k的评级,拥有1,6 k个电影和大约950个用户(从movielens获得了数据集)。我有一个评级模型,其中包含movieid,title,value和user。使用下面的代码,我可以预测一部电影,但是,当我尝试使用循环预测所有1部分,K6电影时,我得到这个消息“致命错误:CALL_AND_RETRY_LAST分配失败 - 处理内存不足”。我想我实施的方式占用了太多内存。这也需要很多时间。大约3-4秒预测一部电影,这意味着约100分钟预测所有1,6k部电影。有没有人有改进的建议?我对nodejs很新,这种异步环境让我感到不舒服。 继承我的代码:

Rating.find({title:"Batman Forever (1995)"}, function(err,rating){
    if (err) throw err;
    var tempSimilarValues = [];
    var count = 0;
    Rating.find({user: userV}, function(err,result) {
        var tempMovieV;
        var tempRatingArrayV = [];
        var tempMovieArrayV = [];
        while (result.length>0) {
            tempMovieV=result.pop();
            tempMovieArrayV.push(tempMovieV.movieid);
            tempRatingArrayV.push(tempMovieV.value);
        }
        //calculating averageRatingV
        var ratingV;
        var avarageRatingV=0;
        var countV=0;
        for(ratingV in tempRatingArrayV){
            countV++;
            avarageRatingV=avarageRatingV+tempRatingArrayV[ratingV];
        }
        avarageRatingV=avarageRatingV/countV;
        rating.forEach(function(obj){
            Rating.find({user: obj.user}, function(err,result2) {

                var tempRatingArray = [];
                var tempMovieArray = [];
                var tempMovie;
                var tempAverage=0;
                var ammount = result2.length
                while (result2.length>0) {
                    tempMovie=result2.pop();

                    tempMovieArray.push(tempMovie.movieid);
                    tempAverage=tempAverage+tempMovie.value
                    tempRatingArray.push(tempMovie.value);

                }

                var averageU=tempAverage/ammount;
                var similar = calSim(tempRatingArrayV,tempRatingArray, tempMovieArrayV, tempMovieArray);

                if(obj.user!=userV){
                    tempSimilarValues.push([similar,obj.user,obj.value,averageU]);
                }

                count++;

                if(count == rating.length){


                    tempSimilarValues.sort(function (a,b){
                        if (a[0] > b[0]) {
                            return -1;
                        }
                        if (a[0] < b[0]) {
                            return 1;
                        }
                        // a must be equal to b
                        return 0;

                    });

                    var similarValues=tempSimilarValues.slice(0,k);
                    var similarity;
                    var teller=0;
                    var nevner=0;
                    for (similarity in similarValues){
                        teller=teller+similarValues[similarity][0]*(similarValues[similarity][2]-similarValues[similarity][3]);
                        nevner = nevner + similarValues[similarity][0];
                    }

                    var pred = avarageRatingV + teller/nevner;
                    console.log(pred);
                }
            })
        })

    })

})

1 个答案:

答案 0 :(得分:0)

使用回调来嵌套多种方法的代码会让您最终得到 Callback Hell 。处理场景的更好方法是使用 async 库,尤其是 series() waterfall() 方法。

为了用类似的简短示例来说明这一点,该示例使用回调执行类似的多个操作来查找属于用户的评级(代码一),找到属于某个标题的一些评级(代码二),计算平均评级(代​​码三) )然后计算具有最高预测评级的那个(代码四):

codeOne(function(a){
    codeTwo(function(b){    
        codeThree(function(c){
            codeFour(function(d){
                // Final callback code        
            })
        })       

    })
})

应用运行任务函数数组的 series 方法,每个函数在前一个函数完成后运行。如果系列中的任何函数将错误传递给其回调,则不再运行任何函数,并立即使用错误值调用回调。否则,当任务完成时,回调会收到一系列结果。

async.series([
    function(callback){
        // code one
        callback(null, 'one')
    },
    function(callback){
        // code two
        callback(null, 'two')
    },
    function(callback){
        // code three
        callback(null, 'three')
    },
    function(callback){
        // code four
        callback(null, 'four')
    }],
    // optional callback
    function(err, results){
        // results is ['one', 'two', 'three', 'four']
        // final callback code
    }
)

应用运行任务函数数组的 waterfall 方法,每个方法都将结果传递给数组中的下一个。但是,如果任何任务将错误传递给它们自己的回调,则不执行下一个函数,并立即调用主回调并显示错误。:

async.waterfall([
    function(callback){
        // code one
        callback(null, 'one', 'two')
    },
    function(arg1, arg2, callback){
        // arg1 is equals 'one' and arg2 is 'two'
        // code three
        callback(null, 'three')
    },
    function(arg1, callback){      
        // arg1 is 'three'
        // code four
        callback(null, 'four');
    }], function (err, result) {
        // result is 'four'    
    }
)

在您的示例代码中,建议重新构建它以使用瀑布方法。这是我在使用异步库实现逻辑的过程,而不是100%肯定你想要实现的目标,所以这可能不起作用,但你可能会得到这个概念:

async.waterfall([
    function(callback){
        // code one - calculate the average rating for user
        Rating.find({user: userV}, function(err,result) {
            if (err) callback(err);         

            var ratingV,
                avarageRatingV = 0,
                countV = 0,
                tempRatingArray = result.map(function(rating){
                    return rating.value;
                }),
                tempMovieArrayV = result.map(function(rating){
                    return rating.movieid;
                });

            //calculating averageRatingV            
            for(ratingV in tempRatingArrayV){
                countV++;
                avarageRatingV = avarageRatingV + tempRatingArrayV[ratingV];
            }
            avarageRatingV=avarageRatingV/countV;
            callback(null, avarageRatingV, tempRatingArrayV, tempMovieArrayV);
        })
    },
    function(arg1, arg2, arg3, callback){
        // arg1 equals averageRatingV 
        // arg2 equals tempRatingArrayV
        // arg3 equals tempMovieArrayV
        // code to return the users for "Batman Forever (1995)" movie ratings
        Rating.find({ title: "Batman Forever (1995)" }, function(err, ratings){
            if (err) callback(err);
            var users = ratings.map(function(r){ return r.user; });
            callback(null, arg1, arg2, arg3, users);
        });         
    },
    function(arg1, arg2, arg3, arg4, callback){      
        // arg1 is averageRatingV, 
        // arg2 equals tempRatingArrayV, 
        // arg3 equals tempMovieArrayVis ratings 
        // arg4 is the users array for "Batman Forever (1995)" movie ratings
        // code to return
        Rating.find({"user": { "$in": arg4 }}, function(err, result) {
            // do the necessary calculations here
            callback(null, result);
        });
    }], function (err, result) {
        // final result   
    }
)