MongoDB:有可能进行不区分大小写的查询吗?

例子:

> db.stuff.save({"foo":"bar"});


> db.stuff.find({"foo":"bar"}).count();
1
> db.stuff.find({"foo":"BAR"}).count();
0
402427 次浏览

你可以使用正则表达式

在你的例子中,这将是:

db.stuff.find( { foo: /^bar$/i } );

不过,我必须说,也许你可以降低(或提高)价值的过程中,而不是承担额外的成本,每次你找到它。显然,这对人名之类的东西不起作用,但可能用在像标签这样的用例上。

更新:

原来的答案现在已经过时了。Mongodb现在支持高级全文搜索,有很多特性。

最初的回答:

需要注意的是,使用regex的大小写不敏感/i进行搜索意味着mongodb不能通过索引进行搜索,因此针对大型数据集的查询可能需要很长时间。

即使数据集很小,效率也不是很高。cpu消耗比查询所保证的要大得多,如果您试图实现规模化,这可能会成为一个问题。

作为一种替代方法,您可以存储一个大写的副本并根据它进行搜索。例如,我有一个用户表,其中有一个混合大小写的用户名,但id是用户名的大写副本。这确保了区分大小写的复制是不可能的(同时拥有“Foo”和“Foo”是不允许的),并且我可以通过id = username. touppercase()进行搜索,以获得对用户名不区分大小写的搜索。

如果字段很大,比如消息体,复制数据可能不是一个好选择。我相信在这种情况下,使用像Apache Lucene这样的无关索引器是最好的选择。

最好的方法是在你选择的语言中,当为你的对象创建模型包装器时,让你的save()方法遍历一组你将搜索的字段,这些字段也被索引了;这组字段应该有对应的小写字母,然后用于搜索。

每次再次保存对象时,都会检查小写属性并更新主属性的任何更改。这将使您可以有效地搜索,但隐藏了每次更新lc字段所需的额外工作。

小写字段可以是一个键值对象存储,或者只是字段名加上前缀lc_。我使用第二种方法来简化查询(深度对象查询有时会令人困惑)。

注意:您希望索引lc_字段,而不是它们所基于的主字段。

记住前面的例子:

db.stuff.find( { foo: /bar/i } );

将导致每个包含酒吧的条目都匹配查询(bar1, barxyz, openbar),这对于用户名搜索认证函数来说是非常危险的…

您可能需要使用适当的regexp语法使其仅匹配搜索项,如下:

db.stuff.find( { foo: /^bar$/i } );

有关正则表达式的语法帮助,请参阅http://www.regular-expressions.info/

Mongo(当前版本2.0.0)不允许对索引字段进行不区分大小写的搜索——参见他们的文档。对于非索引字段,其他答案中列出的正则表达式应该是可以的。

如果你需要从一个变量中创建regexp,这是一个更好的方法:https://stackoverflow.com/a/10728069/309514

然后你可以这样做:

var string = "SomeStringToFind";
var regex = new RegExp(["^", string, "$"].join(""), "i");
// Creates a regex of: /^SomeStringToFind$/i
db.stuff.find( { foo: regex } );

这样做的好处是更加程序化,或者如果您经常重用它,则可以通过提前编译它来提高性能。

使用Mongoose对我来说很管用:

var find = function(username, next){
User.find({'username': {$regex: new RegExp('^' + username, 'i')}}, function(err, res){
if(err) throw err;
next(null, res);
});
}

我为不区分大小写的正则表达式创建了一个简单的Func,我在过滤器中使用它。

private Func<string, BsonRegularExpression> CaseInsensitiveCompare = (field) =>
BsonRegularExpression.Create(new Regex(field, RegexOptions.IgnoreCase));

然后,只需按如下方式筛选一个字段。

db.stuff.find({"foo": CaseInsensitiveCompare("bar")}).count();
db.zipcodes.find({city : "NEW YORK"}); // Case-sensitive
db.zipcodes.find({city : /NEW york/i}); // Note the 'i' flag for case-insensitivity

假设你想搜索“列”;在“Table"你需要不区分大小写的搜索。最有效的方法是:

//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有一个很好的函数,如果你已经在使用它:

db.stuff.find({$regex: new RegExp(_.escapeRegExp(bar), $options: 'i'})

为什么?假设一个用户输入.*作为他的用户名。这将匹配所有用户名,只需猜测任何用户的密码就可以登录。

博士TL;

正确的方法做到这一点在mongo

不使用正则表达式

Go natural和使用mongodb的内置索引,搜索

第一步:

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 }
]
)
 

第二步:

需要在你想要搜索的文本字段上创建索引,没有索引的查询将会非常慢

db.articles.createIndex( { subject: "text" } )

第三步:

db.articles.find( { $text: { $search: "coffee",$caseSensitive :true } } )  //FOR SENSITIVITY
db.articles.find( { $text: { $search: "coffee",$caseSensitive :false } } ) //FOR INSENSITIVITY




 

从MongoDB 3.4开始,执行快速不区分大小写搜索的推荐方法是使用大小写不区分索引

我亲自给其中一位创始人发了邮件,请他把这个工作做好,他做到了!它是一个自2009年起在JIRA上发行,许多人都要求这个功能。下面是它的工作原理:

不区分大小写的索引是通过指定强度为1或2的排序来创建的。你可以像这样创建一个不区分大小写的索引:

db.cities.createIndex(
{ city: 1 },
{
collation: {
locale: 'en',
strength: 2
}
}
);

你也可以在创建集合时指定一个默认的排序规则:

db.createCollection('cities', { collation: { locale: 'en', strength: 2 } } );

在这两种情况下,为了使用不区分大小写的索引,你需要在find操作中指定与创建索引或集合时使用的相同的排序规则:

db.cities.find(
{ city: 'new york' }
).collation(
{ locale: 'en', strength: 2 }
);

这将返回&;New york&;, &;New york&;等。

其他的笔记

  • 答案建议在这种情况下使用全文搜索是错误的(和潜在的危险的)。这个问题是关于进行一个不区分大小写的查询,例如username: 'bill'匹配BILLBill,而不是一个全文搜索查询,它也会匹配bill个单词,例如Billsbilled等。

  • 建议使用正则表达式的答案是缓慢的,因为即使有索引,文档状态:

    不区分大小写的正则表达式查询通常不能有效地使用索引。$regex实现不支持排序规则,也无法使用不区分大小写的索引。

    $regex回答也有用户输入注入的风险。

正如你在mongo docs中看到的那样——自3.2版以来,$text索引默认情况下是不区分大小写的:https://docs.mongodb.com/manual/core/index-text/#text-index-case-insensitivity

创建文本索引在查询中使用$text操作符

这些已经用于字符串搜索进行了测试

{'_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

聚合框架在mongodb 2.2中引入。您可以使用字符串操作符"$strcasecmp"在字符串之间进行不区分大小写的比较。它比使用regex更值得推荐,也更简单。

下面是关于聚合命令运算符的官方文档:https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/#exp._S_strcasecmp

在c#中使用过滤器对我来说是有效的。

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();

它甚至可以使用索引,因为我相信方法是在返回发生后调用的,但我还没有测试出来。

这也避免了一个问题

var filter = Builders<Model>.Filter.Eq(p => p.Title.ToLower(), s.ToLower());

mongodb会认为p.t el . tolower()是一个属性,不会正确映射。

你可以使用不区分大小写索引:

下面的示例创建一个没有默认排序规则的集合,然后在名称字段上添加一个索引,排序规则不区分大小写。Unicode国际组件

/* 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 } } )

要使用索引,查询必须指定相同的排序规则。

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 } )

或者你可以创建一个默认排序规则的集合:

db.createCollection("users", { collation: { locale: 'tr', strength: 2 } } )
db.users.createIndex( { name : 1 } ) // inherits the default collation

我也遇到过类似的问题,这对我很有帮助:

  const flavorExists = await Flavors.findOne({
'flavor.name': { $regex: flavorName, $options: 'i' },
});

搜索变量并转义:

const escapeStringRegexp = require('escape-string-regexp')
const name = 'foo'
db.stuff.find({name: new RegExp('^' + escapeStringRegexp(name) + '$', 'i')})

转义变量可以保护查询不受'攻击。*'或其他正则表达式。

escape-string-regexp

< p >使用正则表达式, 如果任何其他选项都不适合您,RegExp是一个不错的选择。它使字符串不区分大小写
var username = new RegExp("^" + "John" + "$", "i");;

使用用户名在查询,然后它完成。

我希望这对你也有用。愿一切都好!

db.company_profile.find({ "companyName" : { "$regex" : "Nilesh" , "$options" : "i"}});

对于任何使用Golang并希望使用mongodb和mgo Godoc globalsign库进行区分大小写全文搜索的人。

collation := &mgo.Collation{
Locale:   "en",
Strength: 2,
}




err := collection.Find(query).Collation(collation)

如果酒吧是密码或帐户id搜索,我很惊讶没有人警告使用/^bar$/i进行正则表达式注入的风险。(例如,bar => .*@myhackeddomain.com,所以这里是我的赌注:PERL提供的使用__ABC2 \E正则表达式特殊字符!

db.stuff.find( { foo: /^\Qbar\E$/i } );

你应该用\\来转义酒吧变量\字符,以避免在例如bar = '\E.*@myhackeddomain.com\Q'时再次利用\E

另一种选择是使用正则表达式转义字符策略,如这里描述的Javascript等价的Perl's \Q…\E或quotemeta()

如果你正在使用MongoDB Compass:

转到集合,在filter类型中->{字符串字段名:/ /我}

对于使用Mongoose的Node.js:

模型。find({FieldName: {$regex: "stringToSearch", $options: "i"}})

如果查询中有一些特殊字符,则regex simple将不起作用。您需要转义这些特殊字符。

以下helper函数可以在不安装任何第三方库的情况下提供帮助:

const escapeSpecialChars = (str) => {
return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}

你的问题会是这样的:

db.collection.find({ field: { $regex: escapeSpecialChars(query), $options: "i" }})

希望对大家有所帮助!