我是 MongoDB 的新手,但这里有:
有没有办法在 MongoDB 中进行某种形式的部分搜索?
例如,如果文档类似于 { Name: Michael A. Johnson},有没有办法格式化搜索,以便它返回该文档以进行 Name: Michael Johnson 的查询,反之亦然?
此外,是否有一种方法可以搜索“姓名:约翰逊”并返回文档 { 姓名:Michael A. Johnson}?
谢谢!
从 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
类型的索引,也会考虑这些索引。
根据以下文档$text
:
$text
对使用文本索引编制索引的字段的内容执行文本搜索。$search
(string) MongoDB 解析并用于查询文本索引的术语字符串。MongoDB 对术语执行逻辑 OR 搜索,除非指定为短语。
如果您想根据相关性对结果进行排序(这是上面示例中发生的情况),请使用元textScore
属性via $meta
(不要忘记在排序中复制):
$text
操作员为索引字段中包含搜索词的每个文档分配一个分数。分数表示文档与给定文本搜索查询的相关性。分数可以是sort()
方法规范的一部分,也可以是投影表达式的一部分。该{ $meta: "textScore" }
表达式提供有关$text
操作处理的信息。
$text
不会单独在多个字段上工作。在这种情况下,使用$regex
:
{ field: { $regex: '\bWORD\b', $options: 'i' } }
如何编写正则表达式超出了范围。对 SO 进行一些搜索。
为了模仿主题字符串中的所有“单词”都是以空格分隔的“术语”的行为,您可以通过拆分每个术语并将其映射到对象$text
来创建正则表达式对象数组。如果这是用户输入,那么转义所有可能被视为正则表达式一部分的元字符也很重要。最后,构建一个包含您要搜索的所有主题的表达式,或者一个,等...' '
$regex
$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}
]
});
注意:按照惯例,字段名称通常是驼峰式并以小写字母开头,即字段是“almaMater”而不是“Alma Mater”或“AlmaMater”。但为了与您最初的问题保持一致,我将保持第一个字母的上限。