2013-06-05 6 views
5

Sto imparando nodo, express, mongo e, nel processo, javascript. Sto cercando di ottenere una funzione in cui utilizzare rssparser, ottenere un elenco di storie e salvarle in un database mongo con mangusta.Problemi durante l'iterazione della matrice e salvataggio in mangusta. Problema di richiamata?

Ho la funzione RSS che funziona, e sto scorrendo le storie, è il risparmio con cui sto avendo problemi. Voglio 1) controllare che la storia non sia già presente nel database, e 2) se no, salvarla. Penso di essermi perso nel modo in cui vengono gestite le callback. Ecco il mio codice attuale, con commenti.

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); 
       }); 
      } 
     }); 

    } 
}); 

Quando questo viene eseguito, quello che vedo è un sacco di output della console:

E 'inizia a mostrare tutte le storie (molti omesso):

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 

Poi continua così (molti omesso):

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 }                  

Ripete il titolo "about to save" (che è l'ultimo articolo nel feed) e salva il storia una volta, come mostra l'ultima riga.

L'output console.log mostra solo che l'ho messo, tutto il titolo del titolo in uscita in alto, poi tutte le cose all'interno della query.exec() chiamata in basso.

Ogni aiuto è apprezzato ...

risposta

1

Il problema di questo è che la storia si fa riferimento nella richiamata exec sarà stato impostato a tutto ciò che è l'ultima cosa sulla iterato nel ciclo for, una volta che il callback otterrà eseguito, poiché tutte le funzioni eseguite fanno riferimento alla stessa istanza della variabile.

Il modo più semplice per risolvere questo problema è quello di avvolgere semplicemente ogni cosa nel ciclo for in una funzione che si esegue subito con i parametri, come in:

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]); 
    } 
}); 

non ho ancora testato questo, ma io Sono sicuro che scoprirete che risolverà il problema

Un altro modo ancora più semplice, più pulito, migliore sarebbe quello di scorrere gli elementi in un ciclo forEach sull'array, se la piattaforma lo supporta (quale nodo.js fa) - questa versione è ancora più bella:

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); 
       }); 
      } 
     }); 

    }); 
}); 
+0

Ho implementato il metodo di pulizia e ha funzionato benissimo, grazie. Quindi il mio problema era solo un problema di temporizzazione? cioè, tutti gli elementi di out.items sono stati ripetuti prima che il callback fosse eseguito (quindi la trama è stata impostata sull'ultimo out.item)? – Mike

+0

Il tuo problema ha avuto a che fare con l'ambito, che ha provocato una condizione di competizione. quando il ciclo for sta iterando, c'è solo un riferimento al 'story', ed è sovrascritto ogni volta che si itera su un array. Se la funzione exec venisse eseguita immediatamente, in modo sincrono, il tuo codice avrebbe funzionato bene, ma poiché tutte le chiamate al callback in exec si verificano dopo che il ciclo for è terminato, si riferiscono tutte all'ultima istanza della storia assegnata alla variabile storia. quando sono "chiusi" con una funzione, hanno tutti una propria istanza di "storia". questo è un errore molto comune – arnorhs

2

beh, il nodo è server basato su eventi e javascript è anche basato su eventi, quindi è possibile chiamare roba in modo asincrono.

è necessario utilizzare alcuni schemi asincroni per fare ciò che si desidera.

prima, se si utilizza mongoose si può utilizzare è di classe schema per verificare la presenza di elementi che sono già esiste, senza interrogare nuovamente il db:

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) 

l'url è unico, in modo che il salvataggio causerà un duplicato errore e mangusta non salveranno la query.

ora per scorrere le voci e salvare ognuno abbiamo bisogno di una sorta di un modello per essa, per fortuna abbiamo async per esso:

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  
    }); 
} 

abbiamo utilizzato asincrona in un modo parallelo, come non lo facciamo cura se alcuni sono duplicati o meno. puoi anche tracciarlo con un array che puoi spingere ad esso err || risultato così puoi vedere quanti oggetti hai salvato.

+0

Hey, grazie, potrebbe essere un modo migliore per raggiungere l'obiettivo. Molto apprezzato! – Mike

Problemi correlati