在节点中执行可变数量的mongo查询,返回单个结果

时间:2016-11-01 02:59:39

标签: node.js mongodb

Ooof。曾经有过这样的日子,你知道自己已经关闭了,但是你却无法得到它吗?

我正在写一个刽子手拼图解算器。这是在使用mongo db进行了node / hapi编写的服务中运行的。

所以我有一个功能:

solvePuzzle(puzzle, alreadyCalled);

args是拼图本身,解码后的字母为文字,未解决为?s,如下:

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

and alreadyCalled只是一个被称为但不正确的字母列表。经过一些捣乱之后,会为每个单词创建一个RegEx,然后将其发送到一个函数,该函数查询存储在mongo中的单词列表以进行匹配。

一切都在运作,如果我创建一个虚拟的单词列表作为一个简单的数组,一切正常,我得到一个匹配列表。

返回格式是一个对象数组,如下所示:(我在显示可能的解决方案时使用数组索引来保留单词顺序)

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

所以问到实际的问题。我将整个拼图分成单词,并在它们上面运行for循环,调用为每个拼图执行mongo查询的函数。问题是,在查询实际运行之前,函数调用似乎正在返回。散布在各处的控制台日志似乎承认了这一理论。

我尝试让查询函数返回一个承诺,但这只会让水更加混乱。我觉得我关闭但是 - 我不知道。这是我最初的非承诺代码:

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

所以我的控制台输出基本上是:

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

正如您所看到的,对matchWord的调用是在实际查询运行之前返回的。所以我从来没有做过mongo支持的hapi服务。我如何构造这个代码,使它循环遍历所有单词,为每个单词查询mongo,并返回单个数组作为结果?

TIA。

1 个答案:

答案 0 :(得分:0)

在节点中,数据库调用是异步的,因此您不能像这样使用return。 您需要使用Promise(node.js中的native)

此代码应该有效:

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