使用AND运算符进行Mongoose文本搜索

时间:2015-12-09 15:13:17

标签: javascript node.js mongodb mongoose

所以我要做的是用mongoose和mongoDb进行“AND”搜索,但结果来自“OR”搜索。这有什么设置吗?这就是查询的样子:

exampleModel.find({
    $text: {
        $search: searchParams
    }
}, {
    score: {
        $meta: "textScore"
    }
}, { lean: true }).select(exampleViewModel).limit(1000).sort({
    score: {
        $meta: 'textScore'
    }
}).exec(function (err, results) {
   next(null, results); 
});

让我们假设searchParams = restaurant london;

这将在包含“restaurant”或“london”字样的文档中产生结果。 我希望它在包含“restaurant”和“london”字样的文档中产生结果。

2 个答案:

答案 0 :(得分:1)

在搜索字词周围加上引号,将默认行为更改为AND。

https://docs.mongodb.org/manual/reference/operator/query/text/#phrases

exampleModel.find({
    $text: {
        $search: "\"restaurant\" \"london\""
    }
}, {
    score: {
        $meta: "textScore"
    }
}, { lean: true }).select(exampleViewModel).limit(1000).sort({
    score: {
        $meta: 'textScore'
    }
}).exec(function (err, results) {
   next(null, results); 
});

答案 1 :(得分:1)

正如卡洛斯所提到的,一个可接受的解决方法是在单字搜索术语中加上双引号,使其成为强制性的:

apple banana       =>      "apple" "banana"

这会产生与Elasticsearch等高级搜索引擎的AND运算符类似的行为。

但是,如果您打算通过前面的连字符(-exclude)或精确短语("several words wrapped in double quotes")来支持单词排除,那么在动态的,用户生成的查询中实现此操作会变得混乱是$text支持的有效语法。

所以,这里是我编写的一个函数,如果查询中的每个单独的单词前面没有连字符或包含在一个精确的短语中,则用查询包装每个单独的单词。

<强>输入:

apple banana "huge orange trees" -pineapple "lemon"

<强>输出:

"apple" "banana" "huge orange trees" -pineapple "lemon"

确保在每个双引号之前使用前面的反斜杠转义此函数的输出。然后,将其作为MongoDB $search查询的find()参数传递。

function wrapSingleTermsWithDoubleQuotes(query) {
    // Output variable
    var output = "";

    // Keep track of whether to ignore the current word due to the negation operator (-minus)
    var ignoreCurrentWord = false;

    // Keep track of whether we're inside a custom phrase in the query ("exact match")
    var withinCustomPhrase = false;

    // Keep track of whether we need to close a double quote that we opened for the current word
    var openedDoubleQuote = false;

    // Remove all double spacing from the query (we may need a few iterations for this to work)
    while (query.indexOf('  ') != -1) {
        // Replace all occurrences of double spacing with single spacing
        query = query.replace(/  /g, ' ');
    }

    // Trim leading and trailing spaces from the query to normalize the input
    query = query.trim();

    // Start traversing query characters
    for (var i = 0; i < query.length; i++) {
        // Comfort variable
        var char = query[i];

        // Not yet wrapping a term with double quotes?
        if (!openedDoubleQuote) {
            // Not ignoring the current word already (due to an operator) and not within a custom phrase?
            if (!ignoreCurrentWord && !withinCustomPhrase) {
                // Char is not a quote or negation operator?
                if (char != '"' && char != '-') {
                    // This is most likely a single term, let's insert an opening double quote
                    output += '"';

                    // Mark this as true so we remember to close the double-quote when the word's done
                    openedDoubleQuote = true;
                }
                else {
                    // Char is a quote
                    if (char == '"') {
                        // Avoid adding quotes until we encounter the phrase's closing quote
                        withinCustomPhrase = !withinCustomPhrase;
                    }
                    // Char is a negation operator
                    else if (char == '-') {                    
                        // Ignore the current word (don't try to wrap it with double quotes)
                        ignoreCurrentWord = true;
                    }
                }
            }
            else {
                // Ignoring the current word or phrase -- check if we reached the end of the current word (space)
                if (char == ' ') {
                    // In case this was a negative word, it's over now
                    ignoreCurrentWord = false;

                    // Were we inside a custom phrase, the current char is a space, and the previous char was a double quote?
                    if (withinCustomPhrase && i > 0 && query[i - 1] == '"') {
                        // No longer inside a the custom phrase (since we encountered a closing double-quote)
                        withinCustomPhrase = false;
                    }
                }
            }
        }
        else {
            // Within a single term currently -- is current char a space, indicating the end of the single term?
            if (char == ' ') {
                // Add a closing double quote to finish wrapping the single term
                output += '"';

                // We closed our own double-quote
                openedDoubleQuote = false;
            }
        }

        // Add current char to output (we never omit any query chars, only append double-quotes where necessary)
        output += char;
    }

    // Reached the end of the string but still got a quote to close?
    if (openedDoubleQuote) {
        // Add the final double quote
        output += '"';
    }

    // Return algorithm output
    return output;
}