2016-11-01 1 views
0

Ooof. Haben Sie jemals einen dieser Tage, an denen Sie wissen, dass Sie in der Nähe sind, aber Sie können es nicht ganz bekommen?Ausführen einer variablen Anzahl von Mongo-Abfragen im Knoten, Rückgabe eines einzelnen Ergebnisses

Ich schreibe einen Henker Puzzle-Löser. Dies wird in einem Dienst ausgeführt, der mit Knoten/hapi geschrieben wurde, die mit mongo db gesichert sind.

So habe ich eine Funktion:

solvePuzzle(puzzle, alreadyCalled); 

Die args sind das Rätsel selbst, mit gelösten Buchstaben als Literale und ungelöst wie s, etwa so:?

?O?N? ?O ?H? ?TO?? 

und alreadyCalled einfach sein eine Liste von Buchstaben genannt, aber falsch. Nach einiger Muckerei wird für jedes Wort ein RegEx erstellt, der dann an eine Funktion gesendet wird, die eine in mongo gespeicherte Wortliste nach Übereinstimmungen abfragt.

Alles funktioniert wie es sollte, und wenn ich eine Dummy-Wortliste als ein einfaches Array erstellen, funktioniert alles gut und ich bekomme eine Liste der Übereinstimmungen.

Die Rückkehr-Format ist ein Array von Objekten wie folgt: (I Array-Indizes verwenden Wortfolge zu erhalten, wenn mögliche Lösungen Anzeigen)

matches[0][?O?N?] = ['GOING', 'DOING', 'BOING']; 

So auf das eigentliche Problem. Ich teile das ganze Puzzle in Worte auf und führe eine for-Schleife über sie aus, indem ich die Funktion aufruft, die die Mongo-Abfrage für jeden durchführt. Problem ist, dass der Funktionsaufruf scheinbar zurückkehrt, bevor die Abfrage tatsächlich ausgeführt wurde. Die Konsolenprotokolle, die durchsetzt sind, scheinen diese Theorie auszudrücken.

Ich habe versucht, die Abfragefunktion eine Verheißung zurückgeben, aber das hat nur dazu gedient, die Gewässer weiter zu verdrücken. Ich fühle mich wie ich bin schließen aber ja - ich weiß nicht. Hier war mein ursprünglicher nicht-Versprechen Code:

function solvePuzzle(puzzle, called) { 
    // first build the exclusion match pattern 
    //console.log('solvePuzzle: building match pattern'); 
    var x = buildMatchPattern(puzzle, called); 
    // split the puzzle into words 
    //console.log('solvePuzzle: tokenizing puzzle'); 
    var words = tokenize(puzzle.toUpperCase()); 
    //console.log('solvePuzzle:', words); 
    var results = []; 

    for(var i = 0; i < words.length; i++) { 
     console.log('solvePuzzle: matching ' + words[i]); 
     results[i] = {}; 
     results[i][words[i]] = matchWord(words[i], x); 
    } 
    console.log('solvePuzzle: matches: ', results); 
    return results; 
} 

function matchWord(word, exclude) { 
    var pattern = '^'; 
    var letters = word.toUpperCase().split(''); 
    var matches = new Array(); 
    var query = {}; 

    //console.log('matchWord:', letters); 

    for(var i = 0; i < letters.length; i++) { 
     if(letters[i] !== '?') { 
      pattern += letters[i]; 
     } 
     else { 
      pattern += exclude; 
     } 
    } 

    pattern += '$'; 
    var re = new RegExp(pattern); 
    //console.log('matchWord:', re); 

    query.word = {"$regex" : re, "$options": "i"}; 
    //console.log("matchWord query:", JSON.stringify(query)); 
    db.wordlist.find(query, function (err, words) { 
     if(err) { 
      console.error('error:', err); 
     } 
     for(let i = 0; i < words.length; i++) { 
      if(words[i] !== null) { 
       console.log('loop:', words[i].word); 
       matches.push(words[i].word); 
      } 
     } 
     console.log('matchWord:', matches.length); 
     if(matches.length < 1) { 
      console.log('matchWord: found no matches'); 
      matches.push('No Matches Found'); 
     } 

     return matches; 
    }); 
} 

Also meine Konsolenausgabe war im Grunde:

solvePuzzle: matching ?O?N? 
solvePuzzle: matches: []  <---- problem 
loop: 'going' 
loop: 'doing' 
etc etc. 
. 
. 
matchWord: 5 (number of matches found); 

So wie Sie sehen können, wird der Anruf zu Matchwort zurückkehrt, bevor die eigentliche Abfrage ausgeführt wird. Also habe ich noch nie einen Hapi-Service gemacht, der von Mongo unterstützt wird. Wie kann ich diesen Code so strukturieren, dass er alle Wörter durchläuft, jedes einzelne Mongo abfragt und als Ergebnis ein einzelnes Array zurückgibt?

TIA.

+0

siehe mein Update! – felix

Antwort

0

Im Knoten sind Datenbankaufrufe asynchron, so dass Sie return nicht wie folgt verwenden können. Sie müssen Promise (nativer in node.js) verwenden

sollte dieser Code funktionieren:

function solvePuzzle(puzzle, called) { 
     var results = []; 
     // first build the exclusion match pattern 
     var x = buildMatchPattern(puzzle, called); 
     // split the puzzle into words 
     var words = tokenize(puzzle.toUpperCase()); 
     // an array to store the words index 
     var indexes = Array.apply(null, { 
      length: words.length 
     }).map(Number.call, Number); // looks like [1, 2, 3, 4, ...] 
     // create a Promise for each word in words 
     var promises = indexes.map(function(index) { 
      return new Promise(function(resolve, reject) { 
       console.log('solvePuzzle: matching ' + words[index]); 
       results[index] = {}; 
       var pattern = '^'; 
       var letters = words[index].toUpperCase().split(''); 
       var matches = new Array(); 
       var query = {}; 

       for (var i = 0; i < letters.length; i++) { 
        if (letters[i] !== '?') { 
         pattern += letters[i]; 
        } else { 
         pattern += exclude; 
        } 
       } 
       pattern += '$'; 
       var re = new RegExp(pattern); 

       query.word = { 
        "$regex": re, 
        "$options": "i" 
       }; 
       db.wordlist.find(query, function(err, wordsRes) { 
        if (err) { 
         console.error('error:', err); 
         reject(err); // if request failed, promise doesn't resolve 
        } 
        for (let i = 0; i < wordsRes.length; i++) { 
         if (wordsRes[i] !== null) { 
          console.log('loop:', wordsRes[i].word); 
          matches.push(wordsRes[i].word); 
         } 
        } 
        console.log('matchWord:', matches.length); 
        if (matches.length < 1) { 
         console.log('matchWord: found no matches'); 
         matches.push('No Matches Found'); 
        } 
        results[index][words[index]] = matches; 
        resolve(); // request successfull 
       }); 
      }); 
     }); 
     // when all promise has resolved, then return the results 
     Promise.all(promises).then(function() { 
      console.log('solvePuzzle: matches: ', results); 
      return results; 
     }); 
    } 
+0

Leider funktioniert das nicht. Mit dem Debugger im VS-Code setze ich einen Haltepunkt für die Rückgabeergebnisse; Linie in solvePuzzle, und es kehrt zurück, DANN sehe ich, dass alle Übereinstimmungen in der Konsole erscheinen. Also der Kunde (Postman in diesem Fall) mit "?? CK? Y? A ???" als Eingabe erhält ein Array von 3 leeren Objekten als Antwort. – MonkRocker

+0

Oh, ich habe die Rückkehr von SolvePuzzle vergessen, mein Schlechter. Sie müssen Promises hier verwenden, ich werde meine Antwort in einem minut aktualisieren – felix

+0

Nahm ein bisschen tun seit all das wurde von Hapi Routehandler aufgerufen, so dass ich eine Wiederholung des Problems dort war. Gelöst, dass man durch einen Callback übergeben. Jedenfalls - danke dafür.Ich wusste, dass Versprechungen die Lösung sein würden, ich konnte einfach nicht genau herausfinden, wie man es strukturiert. – MonkRocker

Verwandte Themen