【问题标题】:MongoDB incomplete searchMongoDB不完全搜索
【发布时间】:2014-08-09 08:43:54
【问题描述】:

我是 MongoDB 新手,但这里是:

有没有办法在 MongoDB 中进行某种形式的部分搜索?

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

另外,有没有一种方法可以搜索“姓名:Johnson”并返回文档 { Name: Michael A. Johnson}?

谢谢!

【问题讨论】:

标签: node.js mongodb web mongojs database


【解决方案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 (string) 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 的行为,其中主题字符串中的所有“单词”都是以空格分隔的“术语”,您可以通过拆分' ' 并将每个术语映射到@ 来创建正则表达式对象数组987654346@ 对象。如果这是用户输入,那么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,这个替换sn-p可以工作:

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

注意:按照惯例,字段名称通常是驼峰式并以小写字母开头,即字段是“almaMater”而不是“Alma Mater”或“AlmaMater”。但为了与您最初的问题保持一致,我将保留第一个字母的上限。

【讨论】:

  • 非常感谢您的快速回复!您的答案是基于为名称创建索引,在这种情况下,对吗?如果有另一个字段(例如,{Name: 'Michael A. Johnson', Alma Mater: KU} 的文档)怎么办?有没有一种方法可以创建多个索引,以便您可以在一个时刻搜索名称,然后在另一个时刻搜索母校?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 2018-10-26
  • 2023-03-28
  • 1970-01-01
  • 2021-05-19
  • 2018-11-23
相关资源
最近更新 更多