2014-04-18 8 views
1

Je rencontre un problème lorsque j'essaie d'effectuer une requête en fonction du résultat d'une autre requête sur mongodb.Plusieurs requêtes dans la requête mongodb

J'essaie de faire une requête initiale et ensuite faire une autre requête pour chacun des résultats de la première requête. La raison pour laquelle je le fais est parce que j'ai deux collections différentes et j'ai besoin de joindre des données d'une collection avec les données de l'autre collection. Dans un monde SQL, je pourrais facilement faire cela avec un JOIN, mais comme j'utilise mongodb dans celui-ci, je ne peux pas vraiment utiliser JOINs, donc j'ai deviné faire une boucle for dans la fonction de rappel de la première requête. .

Voici le code que je utilise ...

var resultSet = []; 
db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){ 
    if(e || docs.length === 0) { 
     console.log("Sorry, wrong id."); 
     return e; 
    } 
    for(var m=0; m<docs.length; m++){ 

     var auxRes = {}; 
     auxRes.id_bus = docs[m].id_bus; 
     auxRes.id_bus_stop = docs[m].id_bus_stop; 
     auxRes.coord_x = docs[m].coord_x; 
     auxRes.coord_y = docs[m].coord_y; 
     auxRes.id_bus_variation = docs[m].id_bus_variation; 
     db.get('buscollection').find({id_bus: parseInt(docs[m].id_bus)}, function(e, busDocs){ 
      auxRes.s_origin_description = busDocs[0].s_origin_description; 
      auxRes.s_destination_description = busDocs[0].id_destination_description; 
      resultSet.push(auxRes); 
     }); 
     res.send(JSON.stringify(resultSet)); 
    } 
}); 

Je dois res.send le tableau de resultSet après toutes les valeurs ont été ajoutées.

J'ai essayé d'autres façons de faire cela, mais le fait est que lorsque la ligne res.send est atteinte, la deuxième requête n'est pas terminée du tout. J'ai aussi essayé de le faire à l'intérieur du callback de la requête interne, mais je dois vérifier si c'est la dernière dans la boucle for, et vérifier la valeur o m ne le fera pas car il est toujours équivalent à docs.length. Pour autant que je sache, il n'y a pas de requête synchrone dans mongodb, mais peut-être que je me trompe.

Quelle est la bonne façon de faire cela?

EDIT

J'ai trouvé un moyen de contourner cela, mais je suis sûr qu'il doit y avoir une meilleure façon. Voici comment je le fais ...

db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){ 
    if(e || docs.length === 0) { 
     console.log("Ha ocurrido un error, no existe esa parada"); 
     return e; 
    } 
    var busIDs = []; 
    for(var m=0; m<docs.length; m++){ 
     busIDs.push(parseInt(docs[m].id_bus)); 
     var auxRes = {}; 
     auxRes.id_bus = docs[m].id_bus; 
     auxRes.id_bus_stop = docs[m].id_bus_stop; 
     auxRes.coord_x = docs[m].coord_x; 
     auxRes.coord_y = docs[m].coord_y; 
     auxRes.id_bus_variation = docs[m].id_bus_variation; 
     resultSet.push(auxRes); 
    } 
    db.get('buscollection').find({id_bus: {$in: busIDs}}, function(e, busDocs){ 
     for(var n = 0; n<busDocs.length; n++){ 
      for(var k=0; k<resultSet.length; k++){ 
       if(resultSet[k].id_bus == busDocs[n].id_bus){ 
        resultSet[k].s_origin_description = busDocs[n].s_origin_description; 
        resultSet[k].s_destination_description = busDocs[n].id_destination_description; 
       } 
      } 
     } 
     res.send(JSON.stringify(resultSet)); 
    }); 
}); 

Répondre

1

Votre solution mise à jour dans votre question est généralement très bien, que l'utilisation $in est un excellent moyen d'aller chercher un ensemble de résultats (vous aurez envie de vous assurer que vous avez indexé la propriété id_bus).

Voici quelques tweaks (avec un peu de nettoyage et d'optimisation):

db.get('busstopcollection') 
    .find({id_bus_stop: parseInt(req.body.busstopid)}).toArray(function(e, docs){ 
    var auxById = {}; // store a dictionary of all the results for later 
    if(e || docs === null || docs.length === 0) { 
     console.log("Ha ocurrido un error, no existe esa parada"); 
     return e; 
    } 
    var busIDs = []; 
    docs.forEach(function(doc) { 
     busIDs.push(parseInt(doc.id_bus)); 
     // consider just using the doc directly rather than copying each property 
     // especially if you're not manipulating any of the data as it passes 
     var auxRes = {   
      id_bus : doc.id_bus, 
      id_bus_stop : doc.id_bus_stop, 
      coord_x : doc.coord_x, 
      coord_y : doc.coord_y, 
      id_bus_variation : doc.id_bus_variation 
     }; 
     // you could just use each doc directly ... 
     // var auxRes = doc; ?? 
     // ** importantly, store off the id_bus for each one so you can 
     // ** avoid a costly loop trying to match an id below. 
     auxById[doc.id_bus] = auxRes; 
     resultSet.push(auxRes); 
    }); 
    // might want to consider using a cursor ... here's an example 
    db.get('buscollection') 
     .find({id_bus: {$in: busIDs}}).each(function(e, busDoc){ 
     // the last item in the cursor will be null 
     if (busDoc === null) { 
      res.send(JSON.stringify(resultSet)); 
      return; 
     } 
     var res = auxById[busDoc.id_bus]; 
     if (res) { // did we find it in our dictionary of results? 
      // yes, we did == copy the necessary field data 
      res.s_origin_description = busDoc.s_origin_description; 
      res.s_destination_description = busDoc.id_destination_description; 
     }    
    }); 
}); 
2

comportement Node.js est asynchrone, programmeur doit coder en prenant compte de ce comportement. Utilisez des rappels ou des promesses ou une bibliothèque de contrôle de flux. Dans votre programme, vous avez mis la requête mongo à l'intérieur de la boucle, ce qui est une mauvaise approche de l'interrogation. Si vous interrogez plusieurs fois, utilisez plutôt $ dans l'opérateur. Il va optimiser les performances de votre code et résout votre problème d'envoi de réponse aussi.

var resultSet = []; 
     db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){ 
      if(e || docs.length === 0) { 
       console.log("Sorry, wrong id."); 
       return e; 
      } 
      var bus_ids = []; 
      for(var m=0; m<docs.length; m++){ 
       var auxRes = {}; 
       auxRes.id_bus = docs[m].id_bus; 
       bus_ids.push(parseInt(docs[m].id_bus)); // collect all ids 
       auxRes.id_bus_stop = docs[m].id_bus_stop; 
       auxRes.coord_x = docs[m].coord_x; 
       auxRes.coord_y = docs[m].coord_y;    
       auxRes.id_bus_variation = docs[m].id_bus_variation; 
       resultSet.push(auxRes);   

      } 
      // Query at one time for all document 
      db.get('buscollection').find({id_bus: {$in : bus_ids}}).toArray(function(e, busDocs){ 
        // Now find and merge in one go 
        busDocs.forEach(function(eachBusDoc){ 
         for(var i=0,len = resultSet.length;i< len;i++){ 
          if(resultSet[i].id_bus == busDocs.id_bus){ 
           resultSet[i].s_origin_description = eachBusDoc.s_origin_description; 
           resultSet[i].s_destination_description = eachBusDoc.id_destination_description; 
          } 
         } 
        });    
         res.send(JSON.stringify(resultSet)); 
       }); 
     }); 
+0

Ouais, c'est en fait ce que je pensais trop. Je suppose qu'il n'y a pas d'autre moyen de le faire alors. Merci pour votre réponse! – lascort

+0

vous pouvez utiliser la récursivité, mais encore une fois, vous interrogerez mongodb plusieurs fois. – Sumeet

Questions connexes