MongoDB: Is it possible to make a case-insensitive query?
例子:
1 2 3 4 5 6 | > db.stuff.save({"foo":"bar"}); > db.stuff.find({"foo":"bar"}).count(); 1 > db.stuff.find({"foo":"BAR"}).count(); 0 |
你可以用正则表达式。
在您的示例中:
1 | db.stuff.find( { foo: /^bar$/i } ); |
不过,我必须说,也许你可以在进入的过程中降低(或提高)价值,而不是每次你发现它都要承担额外的成本。很明显,这对人名之类的人不起作用,但也可能是像标签这样的用例。
更新:
原来的答案现在已经过时了。MongoDB现在支持高级全文搜索,具有许多功能。
原始答案:
需要注意的是,使用regex的不区分大小写/i进行搜索意味着MongoDB无法按索引进行搜索,因此针对大型数据集的查询可能需要很长时间。
即使是小数据集,它也不是很有效。您的CPU命中率远远高于查询要求,如果您试图实现规模,这可能会成为一个问题。
作为一种替代方法,您可以存储一个大写的副本并对其进行搜索。例如,我有一个用户表,它有一个混合大小写的用户名,但ID是用户名的大写副本。这确保了不可能进行区分大小写的复制(不允许同时使用"foo"和"foo"),并且我可以通过id=username.touppercase()进行搜索,以获得对username不区分大小写的搜索。
如果您的字段很大,例如消息体,那么复制数据可能不是一个好的选择。我相信在这种情况下,使用像ApacheLucene这样的无关索引器是最好的选择。
请记住,前面的示例:
1 | db.stuff.find( { foo: /bar/i } ); |
将导致包含bar的每个条目与查询(bar1、barxyz、openbar)匹配,在auth函数上搜索用户名可能非常危险…
您可能需要使用适当的regexp语法使其仅与搜索词匹配,如下所示:
1 | db.stuff.find( { foo: /^bar$/i } ); |
有关正则表达式的语法帮助,请参阅http://www.regular-expressions.info/。
如果需要从变量创建regexp,这是一种更好的方法:https://stackoverflow.com/a/10728069/309514
然后您可以执行以下操作:
1 2 3 4 | var string ="SomeStringToFind"; var regex = new RegExp(["^", string,"$"].join(""),"i"); // Creates a regex of: /^SomeStringToFind$/i db.stuff.find( { foo: regex } ); |
这样做的好处是更具编程性,或者如果您经常重用它,可以通过提前编译来提高性能。
从MongoDB开始,执行快速不区分大小写搜索的推荐方法是使用不区分大小写的索引。
我亲自给其中一位创始人发了邮件,希望他能成功完成这项工作!自2009年以来,这是一个关于JIRA的问题,许多人都要求使用该功能。工作原理如下:
通过指定强度为1或2的排序规则来创建不区分大小写的索引。您可以这样创建不区分大小写的索引:
1 2 3 4 5 6 7 8 9 | db.cities.createIndex( { city: 1 }, { collation: { locale: 'en', strength: 2 } } ); |
创建集合时,还可以为每个集合指定默认排序规则:
1 | db.createCollection('cities', { collation: { locale: 'en', strength: 2 } } ); |
在这两种情况下,为了使用不区分大小写的索引,需要在创建索引或集合时使用的
1 2 3 4 5 | db.cities.find( { city: 'new york' } ).collation( { locale: 'en', strength: 2 } ); |
这将返回"纽约"、"纽约"、"纽约"等。
其他音符- 在这种情况下,建议使用全文搜索的答案是错误的(而且可能很危险)。问题是要进行不区分大小写的查询,例如,与
BILL 或BILL 匹配的username: 'bill' ,而不是与BILL 的词干匹配的全文搜索查询,如Bills 和billed 等。 建议使用正则表达式的答案很慢,因为即使使用索引,文档也说明:
"Case insensitive regular expression queries generally cannot use indexes effectively. The $regex implementation is not collation-aware and is unable to utilize case-insensitive indexes."
$regex 的答案也存在用户输入注入的风险。
1 2 | db.zipcodes.find({city :"NEW YORK"}); // Case-sensitive db.zipcodes.find({city : /NEW york/i}); // Note the 'i' flag for case-insensitivity |
DR
在蒙古人做这个的正确方法不使用regexp
自然使用MongoDB的内置索引,搜索
步骤1:1 2 3 4 5 6 7 8 9 10 11 12 | db.articles.insert( [ { _id: 1, subject:"coffee", author:"xyz", views: 50 }, { _id: 2, subject:"Coffee Shopping", author:"efg", views: 5 }, { _id: 3, subject:"Baking a cake", author:"abc", views: 90 }, { _id: 4, subject:"baking", author:"xyz", views: 100 }, { _id: 5, subject:"Café Con Leche", author:"abc", views: 200 }, { _id: 6, subject:"Сырники", author:"jkl", views: 80 }, { _id: 7, subject:"coffee and cream", author:"efg", views: 10 }, { _id: 8, subject:"Cafe con Leche", author:"xyz", views: 10 } ] ) |
步骤2:
需要在要搜索的任何文本字段上创建索引,而不创建索引查询将非常慢。
1 | db.articles.createIndex( { subject:"text" } ) |
步骤3:
1 2 | db.articles.find( { $text: { $search:"coffee",$caseSensitive :true } } ) //FOR SENSITIVITY db.articles.find( { $text: { $search:"coffee",$caseSensitive :false } } ) //FOR INSENSITIVITY |
Mongo(当前版本2.0.0)不允许对索引字段进行不区分大小写的搜索-请参阅其文档。对于非索引字段,其他答案中列出的正则表达式应该是好的。
1 | db.company_profile.find({"companyName" : {"$regex" :"Nilesh" ,"$options" :"i"}}); |
最好的方法是在您选择的语言中,当为对象创建模型包装器时,让save()方法迭代将要搜索的一组字段,这些字段也是索引的;这些字段组应该具有小写对应项,然后用于搜索。
每次再次保存对象时,都会检查小写属性,并使用对主属性的任何更改进行更新。这将使您能够有效地搜索,但隐藏每次更新LC字段所需的额外工作。
小写字段可以是key:value对象存储,也可以只是带有前缀lc_u的字段名。我使用第二种方法来简化查询(深度对象查询有时会令人困惑)。
注意:您要索引lc_u字段,而不是它们基于的主字段。
使用Mongoose,这对我很有用:
1 2 3 4 5 6 | var find = function(username, next){ User.find({'username': {$regex: new RegExp('^' + username, 'i')}}, function(err, res){ if(err) throw err; next(null, res); }); } |
假设您要搜索"table"中的"column",并且要进行大小写插入式搜索。最有效的方法如下:
1 2 3 4 5 6 7 8 | //create empty JSON Object mycolumn = {}; //check if column has valid value if(column) { mycolumn.column = {$regex: new RegExp(column), $options:"i"}; } Table.find(mycolumn); |
上面的代码只是将您的搜索值添加为regex,并使用以"i"为选项设置的无实体条件进行搜索。
祝你一切顺利。
在使用基于regex的查询时,需要记住一件非常重要的事情——当您为登录系统执行此操作时,请转义正在搜索的每个字符,不要忘记^和$运算符。如果您已经在使用它,那么lodash有一个很好的功能:
1 | db.stuff.find({$regex: new RegExp(_.escapeRegExp(bar), $options: 'i'}) |
为什么?想象一个用户输入
在MongoDB 2.2中引入了聚合框架。可以使用字符串运算符"$strcasecmp"在字符串之间进行不区分大小写的比较。它比使用regex更推荐也更容易。
下面是聚合命令操作符的官方文档:https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/exp.u s诳strcasecmp。
搜索并转义变量:
1 2 3 | const escapeStringRegexp = require('escape-string-regexp') const name = 'foo' db.stuff.find({name: new RegExp('^' + escapeStringRegexp(name) + '$', 'i')}) |
转义变量可防止查询受到".*"或其他regex的攻击。
转义字符串regexp
可以使用不区分大小写的索引:
下面的示例创建一个没有默认排序规则的集合,然后使用不区分大小写的排序规则在名称字段上添加索引。Unicode的国际组件
1 2 3 4 5 6 | /* strength: CollationStrength.Secondary * Secondary level of comparison. Collation performs comparisons up to secondary * differences, such as diacritics. That is, collation performs comparisons of * base characters (primary differences) and diacritics (secondary differences). * Differences between base characters takes precedence over secondary * differences. */ db.users.createIndex( { name: 1 }, collation: { locale: 'tr', strength: 2 } } ) |
若要使用索引,查询必须指定相同的排序规则。
1 2 3 4 5 6 7 8 9 10 11 12 | db.users.insert( [ { name:"O?uz" }, { name:"o?uz" }, { name:"O?UZ" } ] ) // does not use index, finds one result db.users.find( { name:"o?uz" } ) // uses the index, finds three results db.users.find( { name:"o?uz" } ).collation( { locale: 'tr', strength: 2 } ) // does not use the index, finds three results (different strength) db.users.find( { name:"o?uz" } ).collation( { locale: 'tr', strength: 1 } ) |
或者可以使用默认排序规则创建集合:
1 2 | db.createCollection("users", { collation: { locale: 'tr', strength: 2 } } ) db.users.createIndex( { name : 1 } ) // inherits the default collation |
我为不区分大小写的regex创建了一个简单的func,我在过滤器中使用它。
1 2 | private Func<string, BsonRegularExpression> CaseInsensitiveCompare = (field) => BsonRegularExpression.Create(new Regex(field, RegexOptions.IgnoreCase)); |
然后您只需按如下所示对字段进行筛选。
1 | db.stuff.find({"foo": CaseInsensitiveCompare("bar")}).count(); |
在C中使用过滤器对我很有用。
1 2 3 4 | string s ="searchTerm"; var filter = Builders<Model>.Filter.Where(p => p.Title.ToLower().Contains(s.ToLower())); var listSorted = collection.Find(filter).ToList(); var list = collection.Find(filter).ToList(); |
它甚至可以使用索引,因为我相信方法是在返回发生后调用的,但是我还没有测试过这个方法。
这也避免了
1 | var filter = Builders<Model>.Filter.Eq(p => p.Title.ToLower(), s.ToLower()); |
MongoDB会认为p.title.tolower()是一个属性,无法正确映射。
对于任何使用golang的用户,希望使用mongodb和mgo godoc globalsign库进行区分大小写的全文搜索。
1 2 3 4 5 6 7 | collation := &mgo.Collation{ Locale: "en", Strength: 2, } err := collection.Find(query).Collation(collation) |
使用ReGEXP,如果其他选项对您不起作用,那么regexp是一个很好的选项。它使字符串区分大小写。
1 | var username = new RegExp("John","i"); |
在查询中使用用户名,然后完成。
我希望它也会对你有用。祝你一切顺利。
正如您在Mongo Docs中看到的,从3.2版开始,
创建文本索引并在查询中使用$TEXT运算符。
这些已经过字符串搜索测试
1 2 3 4 5 6 7 | {'_id': /.*CM.*/} ||find _id where _id contains ->CM {'_id': /^CM/} ||find _id where _id starts ->CM {'_id': /CM$/} ||find _id where _id ends ->CM {'_id': /.*UcM075237.*/i} ||find _id where _id contains ->UcM075237, ignore upper/lower case {'_id': /^UcM075237/i} ||find _id where _id starts ->UcM075237, ignore upper/lower case {'_id': /UcM075237$/i} ||find _id where _id ends ->UcM075237, ignore upper/lower case |
我也遇到过类似的问题,这对我很有帮助:
1 2 3 | const flavorExists = await Flavors.findOne({ 'flavor.name': { $regex: flavorName, $options: 'i' }, }); |