MongoDB不完整的搜索

时间:2014-06-18 22:24:23

标签: node.js mongodb web mongojs database

我是MongoDB的新手,但这里有:

有没有办法在MongoDB中进行部分搜索?

例如,如果文档类似于{Name:Michael A. Johnson},是否有一种格式化搜索的方法,以便它返回该文档以查询Name:Michael Johnson,反之亦然?

此外,是否有一种方法可以搜索姓名:Johnson'并返回文件{姓名:Michael A. Johnson}?

谢谢!

1 个答案:

答案 0 :(得分:1)

从MongoDB 2.6开始,$text(与$search$meta一起提供)可以提供您描述的搜索字词功能。

请考虑以下事项:

db.collection.ensureIndex({Name:'text'});
db.collection.find({
        $text: { $search: 'Michael Johnson' }
    },{
        score: { $meta: 'textScore' }
}).sort({
        score: {  $meta: 'textScore' }
});

请注意,您无需始终ensureIndex,索引将根据需要进行更新。此外,将使用所有相关指数,因此如果您有多个text-type indices,也会考虑这些指数。

根据documentation for $text

  

$text对索引字段的内容执行文本搜索   带有文本索引。   $search(字符串)MongoDB解析并用于查询文本索引的一串术语。 MongoDB执行术语的逻辑OR搜索,除非指定为短语。

如果您想根据相关性(上面示例中发生的情况)对结果进行排序,请使用meta textScore property通过$meta (don't forget to duplicate in sort)

  

$text运算符为包含的每个文档指定分数   索引字段中的搜索词。分数代表了   文档与给定文本搜索查询的相关性。得分可以是   sort()方法规范的一部分以及部分   投影表达。 { $meta: "textScore" }表达式   提供有关$text操作处理的信息。


$text分别在多个字段上工作。在这种情况下,请使用$regex

{ field: { $regex: '\bWORD\b', $options: 'i' } }

如何编写正则表达式超出范围。在SO上做一些搜索。

要模仿$text的行为,其中主题字符串中的所有“单词”都是以空格分隔的“术语”,您可以通过拆分' '并映射每个“正则表达式”来创建一个正则表达式对象的数组术语到$regex对象。如果这是用户输入,那么escape all meta characters that could be considered part of the regular expression也很重要。最后,构建一个$or表达式,其中包含您要搜索的所有主题,或者$and$not等等。

以下是$or(逻辑OR)的完整示例实现:

var nameMongoSearch = strToMongoRegexArray('Michael Johnson','Name');
var almaMaterMongoSearch = strToMongoRegexArray('KU','AlmaMater');

// OR matching for both Name and AlmaMater terms
db.collection.find({
    $or: [].concat(nameMongoSearch).concat(almaMaterMongoSearch)
});

/*
 * When str = "Michael Johnson" and key = "Name"
 * convert to something like
 * [
 *   { Name: { $regex: '\\bMichael\\b', $options: 'i' } },
 *   { Name: { $regex: '\\bJohnson\\b', $options: 'i' } }
 * ]
 */
function strToMongoRegexArray(str,key) {
    // 
    return str
    .split(' ') // translate String to Array, split into "terms"
    .filter(Boolean) // filter empty strings (in the case of adjecent spaces)
    .map(function(str){ // translate each term into a mongodb regex
        var o = {};
        o[key] = {
            $regex: '\\b'+escapeRegExp(str)+'\\b', // the '\\b' encapsulation is for word boundaries
            $options: 'i' // the 'i' flag is for case insensitive matching
        };
        return o;
    });
}

/*
 * from https://stackoverflow.com/a/6969486/1481489
 * this will escape regex metacharacters for javascript for user input sanitation
 */
function escapeRegExp(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

如果您想使用逻辑AND,则此替换代码段可以正常工作:

db.collection.find({
    $and: [
        { $or: nameMongoSearch},
        { $or: almaMaterMongoSearch}
    ]
});

注意:按照惯例,字段名称通常是camelcase并以小写字母开头,即字段为“almaMater”而不是“Alma Mater”或“AlmaMater”。但为了与原来的问题保持一致,我在第一封信上保留了上限。