无法迭代数组并保存到mongoose。回调问题?

时间:2013-06-05 21:38:06

标签: javascript node.js mongodb express mongoose

我正在学习节点,表达,mongo,以及在此过程中,javascript。我正在尝试使用rssparser获取一个功能,获取故事列表并将其保存到mongoose的mongo数据库中。

我已经完成了RSS工作,我正在重复这些故事,这是我遇到问题的拯救。我想1)检查数据库中是否存在故事,2)如果没有,请保存。我想我在处理回调的过程中迷失了方向。这是我当前的代码,带有注释。

rssparser.parseURL(url, options, function(err,out){
    // out.items is an array of the items pulled
    var items = out.items;
    var story;
    for (var i=0; i<items.length; i++){

        //create a mongoose story
        story = new schemas.Stories({
            title: items[i].title,
            url: items[i].url,
            summary: items[i].summary,
            published: items[i].published_at
        });

        //TODO: for testing - these show up correctly.  
        //If I pull 10 stories, I get 10 entries from here that match
        //So "story" is holding the current story
        console.log("items[i] is :" + items[i].title);
        console.log("story title is : " + story.title);

        // setup query to see if it's already in db
        var query = schemas.Stories.findOne({
            "title" : story.title,
            "url" : story.url
        });


        //execute the query
        query.exec( function(err, row){
            if(err) console.log("error-query: " + err);
            console.log("row: "+ row);
            if(!row) {
                // not there, so save
                console.log('about to save story.title: ' + story.title);
                story.save(function (err){
                    console.log("error in save: " + err);
                });
            }
        });

    }
});

当这个运行时,我看到的是很多控制台输出:

它开始显示所有故事(许多省略):

items[i] is :TSA Drops Plan to Let Passengers Carry Small Knives on Planes               
story title is : TSA Drops Plan to Let Passengers Carry Small Knives on Planes           
items[i] is :BUILDING COLLAPSE:1 Reportedly Dead, 13 Pulled From Philly Rubble           
story title is : BUILDING COLLAPSE:1 Reportedly Dead, 13 Pulled From Philly Rubble       
items[i] is :CONTROVERSIAL PAST: Obama's UN Nominee Once Likened US 'Sins' to Nazis'     
story title is : CONTROVERSIAL PAST: Obama's UN Nominee Once Likened US 'Sins' to Nazis' 
items[i] is :WRITING OUT WRIGHTS: Bill Gives First Powered Flight Nod to Whitehead       
story title is : WRITING OUT WRIGHTS: Bill Gives First Powered Flight Nod to Whitehead   
items[i] is :BREAKING NEWS: Rice Named to Top Security Post Despite Libya Fallout        
story title is : BREAKING NEWS: Rice Named to Top Security Post Despite Libya Fallout   

然后继续像(许多省略):

row: null                                                                     
about to save story.title: Best Ribs in America                               
row: null                                                                     
about to save story.title: Best Ribs in America                               
row: null                                                                     
about to save story.title: Best Ribs in America                               
row: null                                                                     
about to save story.title: Best Ribs in America                               
row: null                                                                     
about to save story.title: Best Ribs in America                               
row: null                                                                     
about to save story.title: Best Ribs in America                               
row: { title: 'Best Ribs in America',                                         
  url: 'http://www.foxnews.com/leisure/2013/06/05/10-best-ribs-in-america/',  
  published: 1370463800000,                                                   
  _id: 51af9f881995d40425000023,                                              
  __v: 0 }                                                                    

它重复了“即将保存”标题(这是Feed中的最后一个故事),它会像上一行一样保存故事。

console.log输出显示我把它放在顶部的所有故事标题输出,然后是底部的query.exec()调用内的所有内容。

感谢任何帮助...

2 个答案:

答案 0 :(得分:2)

这个问题是exec回调中引用的故事将被设置为for循环中迭代的最后一件事,一旦回调将被执行,因为所有执行的函数都引用相同的变量的实例。

解决这个问题的最简单方法是简单地将for循环中的每个东西包装在您使用参数立即执行的函数中,如:

rssparser.parseURL(url, options, function(err,out){
    // out.items is an array of the items pulled
    var items = out.items;
    for (var i=0; i<items.length; i++){
        (function(item) {

            //create a mongoose story
            var story = new schemas.Stories({
                title: item.title,
                url: item.url,
                summary: item.summary,
                published: item.published_at
            });

            // setup query to see if it's already in db
            var query = schemas.Stories.findOne({
                "title" : story.title,
                "url" : story.url
            });

            //execute the query
            query.exec( function(err, row){
                if(err) console.log("error-query: " + err);
                console.log("row: "+ row);
                if(!row) {
                    // not there, so save
                    console.log('about to save story.title: ' + story.title);
                    story.save(function (err){
                        console.log("error in save: " + err);
                    });
                }
            });

        })(items[i]);
    }
});

我没有测试过这个,但我相信你会发现它会解决你的问题

另一种更简单,更简洁,更好的方法是迭代数组上forEach循环中的项目,如果你的平台支持(node.js那样做) - 这个版本更漂亮:

rssparser.parseURL(url, options, function(err,out){
    // out.items is an array of the items pulled
    out.items.forEach(function(item) {

        //create a mongoose story
        var story = new schemas.Stories({
            title: item.title,
            url: item.url,
            summary: item.summary,
            published: item.published_at
        });

        // setup query to see if it's already in db
        var query = schemas.Stories.findOne({
            "title" : story.title,
            "url" : story.url
        });

        //execute the query
        query.exec( function(err, row){
            if(err) console.log("error-query: " + err);
            console.log("row: "+ row);
            if(!row) {
                // not there, so save
                console.log('about to save story.title: ' + story.title);
                story.save(function (err){
                    console.log("error in save: " + err);
                });
            }
        });

    });
});

答案 1 :(得分:2)

好吧,节点是事件驱动的服务器,javascript也是事件驱动的,所以你可以异步调用东西。

你需要使用一些异步模式来做你想做的事。

首先,如果您使用mongoose,您可以利用它的架构类来检查已存在的项目,而无需再次查询数据库:

var mongoose = require('mongoose');

var schema = new mongoose.Schema({
    title: String,
    url: { type: String, unique: true },
    summary: String,
    published: Date

})

var model = mongoose.model('stories', schema)

网址是唯一的,因此保存会导致重复错误,猫鼬不会保存查询。

现在要遍历项目并保存每个项目我们需要某种模式,幸运的是我们有async

var async = require('async');

rssparser.parseURL(url, options, function(err, out){
    async.each(out.items, function(item, callback){

        var m = new model({
            title: item.title,
            url: item.url,
            summary: item.summary,
            published: item.published_at
        })

        m.save(function(err, result){
            callback(null)
        });

    }, function(err){
        //we complete the saving we can do stuff here       
    });
}

我们在并行模式下使用async,因为我们不关心某些是否重复。 你也可以用一个数组跟踪它,你可以推送它错误的||结果如此 你可以看到你保存了多少项。