MongoDB:是否可以进行不区分大小写的查询?

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

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

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

这将返回"纽约"、"纽约"、"纽约"等。

其他音符

  • 在这种情况下,建议使用全文搜索的答案是错误的(而且可能很危险)。问题是要进行不区分大小写的查询,例如,与BILLBILL匹配的username: 'bill',而不是与BILL的词干匹配的全文搜索查询,如Billsbilled等。
  • 建议使用正则表达式的答案很慢,因为即使使用索引,文档也说明:


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

username的值与/John/i的值相同。

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

我希望它也会对你有用。祝你一切顺利。


正如您在Mongo Docs中看到的,从3.2版开始,$text索引默认情况下不区分大小写:https://docs.mongodb.com/manual/core/index text/text index case-insensitivity

创建文本索引并在查询中使用$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' },
  });