2016-11-24 1 views
1

Pour définir le scénario du code, la base de données stocke les documents et chaque document peut être associé à des images.Node.js requêtes mysql imbriquées asynchrones

J'ai essayé d'écrire un itinéraire qui interroge la base de données pour chaque document qui a des images liées à eux, en stockant ces données dans JSON qui est retourné à la requête ajax une fois rempli, afin que les données peuvent être consultées . Le plus proche que j'ai obtenu jusqu'ici est la tentative ci-dessous (voir le code).

router.post('/advanced_image_search', userAuthenticated, function(req, res, next) { 

    async.waterfall([ 
    // First function is to get each document which has an image related 
    function getDocuments(callback){ 
     connection.query(`SELECT DISTINCT(Document.document_id), Document.doc_name, Document.doc_version_no, Document.doc_date_added 
     FROM Document WHERE doc_type = 'image'`, function(err, results) { 
     if (err) { 
      callback(err, null); 
      return; 
     } 

     // The Object containing the array where the data from the db needs to be stored 
     var documents = { 
      'docs': [] 
     }; 

     // foreach to iterate through each result found from the first db query (getDocuments) 
     results.forEach(function(result) { 

      // New object to store each document 
      var document = {}; 

      document.entry = result; 

      // This is the array where each image assciated with a document will be stored 
      document.entry.images = []; 
      // Push each document to the array (above) 
      documents.docs.push(document); 

      var doc_id = result.document_id; 
     }) 
     // Returning the results as 'documents' to the next function 
     callback(null, documents); 
     }) 
    }, 
    function getImages(documents, callback){ 

     // Variable assignement to the array of documents 
     var doc_array = documents.docs; 

     // Foreach of the objects within document array 
     async.forEachOf(doc_array, function(doc, key, callback){ 
     // Foreach object do the following series of functions 
     async.waterfall([ 
      function handleImages(callback){ 

      // The id of the document to get the images for 
      var doc_id = doc.entry.document_id; 
      connection.query(`SELECT * FROM Image, Document WHERE Image.document_id = '${doc_id}' AND Image.document_id = Document.document_id`, function(err, rows) { 
       if (err) { 
       callback(err, null); 
       return; 
       } 

       callback(null, rows); 
      }) 
      }, 
      // Function below to push each image to the document.entry.images array 
      // 
      function pushImages(rows, callback){ 
      // If multiple images are found for that document, the loop iterates through each pushing to the images array 
      for (var j = 0; j < rows.length; j++) { 

       // Creating new object for each image found so the data can be stored within this object, then pushed into the images array 
       var image = { 
       'image_name': rows[j].image_name 
       }; 

       doc.entry.images.push(image); 
      } 
      callback(null, doc_array); 

      } 

     ], function(err, doc_array){ 

      if (err) { 
      console.log('Error in second waterfall callback:') 
      callback(err); 
      return; 
      } 
      console.log(doc.entry); 
      // callback(null, doc_array); 
     }) 
     }, function(err, doc_array){ 

     if (err) { 
      callback(err); 
      return; 
     } 

     callback(null, doc_array); 

     }); 
     callback(null, doc_array); 
    } 
    ], function(err, doc_array) { 

    if (err){ 
     console.log('Error is: '+err); 
     return; 
    } 

    // The response that should return each document with each related image in the JSON 
    res.send(doc_array); 
    }) 

}); 

Au moment où les résultats retournés sont:

1: 
{entry: {document_id: 1, doc_name: "DocumentNameHere", doc_version_no: 1,…}} 
entry: 
{document_id: 1, doc_name: "DocumentNameHere", doc_version_no: 1,…} 
doc_date_added:"2016-10-24" 
doc_name:"DocumentNameHere" 
doc_version_no:1 
document_id:1 
images:[] 

Comme on le voit ci-dessus, le tableau d'images reste vide même si des tests, les images sont trouvées (console.log).

J'espère que quelqu'un est en mesure de vous aider, car j'ai du mal à trouver le problème avec ce complexe.

Merci

+0

Où vous commentez '// Le retour des résultats comme 'documents' à la prochaine function' vous ne retourne pas vraiment quoi que ce soit de cette fonction mais vous devriez. – marekful

+0

@marekful Je viens de faire un console.log (documents); dans la fonction suivante (getImages()) et tous les documents y sont définitivement retournés. Je pense que le problème se situe à la fin de l'async.forEachOf().Il ne semble pas passer sur le JSON modifié – JRich5

Répondre

0

Il y a plusieurs opérations async en cours ici et chaque opération a besoin d'un rappel. Voir le code révisé:

router.post('/advanced_image_search', userAuthenticated, function(req, res, next) { 

    var getDocuments = function(next) { 
    // Function for getting documents from DB 
    var query = `SELECT DISTINCT(Document.document_id), Document.doc_name, Document.doc_version_no, Document.doc_date_added FROM Document WHERE doc_type = 'image'`; // Set the query 
    connection.query(query, function(err, results) { 
     // Run the query async 
     if(err) { 
      // If err end execution 
      next(err, null); 
      return; 
     } 

     var documentList = []; // Array for holding docs 
     for(var i=0; i<results.length; i++) { 
      // Loop over results, construct the document and push to an array 
      var documentEntry = results[i]; 
      var documentObject = {}; 
      documentObject.entry = documentEntry; 
      documentObject.entry.images = []; 
      documentObject.id = documentEntry.document_id; 
      documentList.push(documentObject); 
     } 
     next(null, documents); // Pass to next async operation 
     }); 
    }; 

    var getImages = function(documents, next) { 
    // Function for getting images from documents 
    var finalDocs = []; // Blank arry for final documents with images 
    for (var i=0; i<documents.length; i++) { 
     // Loop over each document and construct the query 
     var id = documents[i].id; 
     var query = `SELECT * FROM Image, Document WHERE Image.document_id = '${doc_id}' AND Image.document_id = Document.document_id`; 

     connection.query(query, function(err, images) { 
     // Execute the query async 
     if(err) { 
      // Throw error to callback 
      next(err, null); 
      return; 
     } 
     var processedDoc = processImages(documents[i], images); // Call a helper function to process all images into the document object 
     finalDocs.push(processedDoc); // Push the processed doc 

     if(i === documents.length) { 
      // If there are no more documents move onto next async 
      next(null, finalDocs); 
     } 
     }); 
    } 
    }; 

    var processImages = function(doc, images) { 
    for (var i=0; i< images.length; i++) { 
     // Loop over each document image - construct object 
     var image = { 
     'image_name': rows[j].image_name 
     }; 
     doc.entry.images.push(image); // Push image into document object 
    } 

    return doc; // Return processed doc 
    }; 

    getDocuments(function(err, docs) { 
    if(err) { 
     // Your error handler 
    } 

    if(docs) { 
     getImages(docs, function(err, finalDocs) { 
     if(err) { 
      // Your error handler 
     } 

     if(finalDocs) { 
      console.log(finalDocs); 
      res.status(200).json(finalDocs); // Send response 
     } 

     }); 
    } 

    }); 
}); 
  1. Tout d'abord, nous créons une fonction pour obtenir des documents - Cette fonction accepte un rappel comme paramètre. Nous exécutons notre requête et construisons notre liste de documents. Ensuite, nous retournons la liste des documents en exécutant notre rappel
  2. Ensuite, nous exécutons une fonction pour obtenir nos images pour chaque document. Cette fonction accepte notre liste de documents et un rappel comme paramètres. Il récupère les images pour chaque document et appelle une fonction d'assistance (sync)
  3. Notre fonction d'assistance traite les images dans chaque document et renvoie le document traité.
  4. Nous terminons ensuite nos opérations en renvoyant un tableau de documents traités via le second rappel.

Autres notes

  • On pourrait marée cela en structurant ce code de style procédural dans un univers confiné objet JSON
  • L'imbrication des exécutions de fonction à la fin du document pourrait être nettoyé plus loin
  • J'ai évité d'utiliser la librairie async car elle aide à mieux comprendre le modèle de rappel
  • Les émetteurs d'événements pouvaient être utilisés à plat dix callbacks - voir https://nodejs.org/dist/latest-v7.x/docs/api/events.html

Hope this helps

Dylan

+0

Juste une note supplémentaire sans votre code source complet, je n'ai pas pu tester donc cet exemple montre juste les principes –