MongoDB:大文字と小文字を区別しないクエリを作成することは可能ですか?


304

例:

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

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

3
MongoDB 3.2以降では、大文字と小文字を区別しない検索をで実行できます$caseSensitive: false。参照: docs.mongodb.org/manual/reference/operator/query/text/...
マーティン

4
これはテキストインデックスのみに適用されることに注意してください。
Willem D'Haeseleer

1
@martin:$caseSensitiveはデフォルトですでにfalseになっていますが、インデックス付きフィールドでのみ機能するため、質問には答えられません。OPは、大文字と小文字を区別しない文字列比較を探していました。
Dan Dascalescu、

回答:


343

あなたは正規表現を使うことができます。

あなたの例では次のようになります:

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

ただし、値が見つかるたびに追加のコストが発生するのではなく、途中で値を小文字(または大文字)にすることもできます。明らかにこれは人の名前などでは機能しませんが、タグのようなユースケースかもしれません。


27
これは完全に機能します。$ collection-> find(array( 'key' => new MongoRegex( '/'.$ val。' / i '))));
ルークデニス

2
特に、疑問符が含まれる可能性がある文字列({foo:/#{x} / i})を補間している場合は、..
Peter Ehrlich

17
^ and $も忘れないでください:MongoRegex( '/ ^'。preg_quote($ val)。 '$ / i')
Julien

20
これはインデックスを使用する代わりにフルスキャンを実行することに注意してください。
Martin Konicek 2013

12
最初に^アンカーを使用した場合、フルスキャンは実行されないため、Julienのアドバイスが重要です。
Pax

198

更新:

元の答えは廃止されました。Mongodbは、多くの機能を備えた高度な全文検索をサポートするようになりました。

元の回答:

正規表現の大文字と小文字を区別しない/ iで検索すると、mongodbはインデックスで検索できないため、大きなデータセットに対するクエリには時間がかかる可能性があることに注意してください。

データセットが小さい場合でも、それほど効率的ではありません。クエリが保証するよりもはるかに大きなCPUヒットを取得します。これは、スケールを達成しようとしている場合に問題になる可能性があります。

別の方法として、大文字のコピーを保存して検索することもできます。たとえば、大文字と小文字が混在するユーザー名を持つユーザーテーブルがありますが、IDはユーザー名の大文字のコピーです。これにより、大文字と小文字が区別される複製が不可能になり(「Foo」と「foo」の両方を使用することはできなくなります)、id = username.toUpperCase()で検索して、大文字と小文字を区別しないユーザー名の検索を取得できます。

メッセージ本文のようにフィールドが大きい場合、データの複製はおそらく適切なオプションではありません。その場合、Apache Luceneのような無関係なインデクサーを使用するのが最良のオプションだと思います。


1
@Dan、ちょうど最新のMongoDBでは、「フィールドにインデックスが存在する場合、MongoDBは正規表現をインデックス内の値と照合します。これは、コレクションスキャンよりも高速です。」- docs.mongodb.org/manual/reference/operator/query/regex/...
Sergiy Sokolenko

1
ドキュメントが更新された可能性があります。「大文字と小文字を区別する正規表現クエリの場合、フィールドにインデックスが存在する場合、MongoDBは正規表現をインデックス内の値と照合します。これは、コレクションスキャンよりも高速です。」
ジェフルイス

1
テキストインデックスのもう1つの制限は、コレクションごとに1つ(複数の列)しか持てないことです。そのため、さまざまなケースでさまざまなフィールドの検索を分離する必要がある場合は適していません。
Paul Grimshaw 2017

2
@SergiySokolenko:ドキュメントでは次のようになっています(セクションの最後の段落):「通常、大文字と小文字を区別しない正規表現クエリはインデックスを効果的に使用できません。$ regexの実装は照合に対応しておらず、大文字と小文字を区別しないインデックスを利用できません。」
Dan Dascalescu、

1
この場合、全文検索の使用は間違っています(そして潜在的に 危険な質問は、大文字と小文字を区別しないクエリ、たとえばことについてだったので、)username: 'bill'マッチングBILLBill、ないにもマッチがなり、フルテキスト検索クエリ、言葉茎billように、Billsbilledなど
ダンDascalescu

70

変数から正規表現を作成する必要がある場合、これははるかに優れた方法です: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 } );

これには、よりプログラマティックであるという利点があります。または、それを頻繁に再利用している場合は、事前にコンパイルすることでパフォーマンスを向上させることができます。


1
new RegExp("^" + req.params.term.toLowerCase(), "i") また、正常に機能します
Tahir Yasin 2017年

2
変数がリクエストからのものである場合は、セキュリティを高めるために文字列をエスケープすることを検討する必要があります:stackoverflow.com/a/50633536/5195127
davidivad

MongoDB 3.4以降では、大文字と小文字
Dan Dascalescu

64

前の例は次のとおりです。

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

バーを含むすべてのエントリが発生しますをクエリ(bar1、barxyz、openbar)と一致するようになります。認証関数でのユーザー名検索では非常に危険です...

次のように適切な正規表現構文を使用して、検索用語のみに一致させる必要がある場合があります。

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

正規表現の構文ヘルプについては、http://www.regular-expressions.info/を参照してください


この回答はコメントのようです。
Dan Dascalescu、

62

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

これは、「ニューヨーク」、「ニューヨーク」、「ニューヨーク」などを返します。

その他の注意事項

  • この場合、全文検索の使用を提案する答えは間違っています(そして、潜在的に危険です)。質問は大文字と小文字を区別しないクエリ、たとえばことについてだったusername: 'bill'マッチングBILLBillもマッチします、ないフルテキスト検索クエリ、の単語のbillような、Billsbilledなど
  • 正規表現の使用を提案する答えは遅くなります。これは、インデックスがあっても、ドキュメントには次のように記載されているためです。

    「大文字と小文字を区別しない正規表現クエリでは、通常、インデックスを効果的に使用できません。$ regexの実装は照合に対応しておらず、大文字と小文字を区別しないインデックスを利用できません。」

    $regex答えはまた、ユーザー入力インジェクションのリスクを冒します


集約パイプラインを使用しても、私にとってはうまくいきました。
モリオ

データの読み取り速度が重要であるため、これは正しい答えだと思います
Rndmax

コレクションが作成された後、コレクションにデフォルトの照合を追加する方法を見つけることができないようです。そうする方法はありますか?
IncrediblePony

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

1
@ OlegV.Volkovには、回答がどのように適切で、質問者コードのどこが間違っているかについての説明が必要です。
Parth Trivedi

1
このコードのみの回答は、6年前に投稿された承認済みの回答には何も追加しません。
Dan Dascalescu、

19

TL; DR

mongoでこれを行う正しい方法

RegExpを使用しない

自然に進み、mongodbの組み込みインデックスを使用して検索

ステップ1 :

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 :

検索したいTEXTフィールドにインデックスを作成する必要がありますが、クエリのインデックスを作成しないと非常に遅くなります

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

ステップ3:

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

1
良いオプションですが、テキストインデックスと正規表現を使用することについて「正しい」ものは何もありません。これは単なる別のオプションです。OPの場合は過剰です。
JohnnyHK 2016

2
正規表現を除いて、かなり遅いです。全文検索も遅いですが、それほど遅くはありません。最速の(しかし、より肥大化した)方法は、常に小文字に設定される別個のフィールドです。
トムMettam

4
フルテキスト検索を使用すると、この場合、間違った(および潜在的にある危険な質問は、大文字と小文字を区別しないクエリ、たとえばことについてだったので、)username: 'bill'マッチングBILLBill、ないにもマッチがなり、フルテキスト検索クエリ、言葉茎billように、Billsbilledなど
ダンDascalescu

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

2
これを投稿する前に既存の回答を見ましたか?準重複コードのみの回答の代わりに、以前の回答と比較して価値のあるものを追加する方法を説明することができます。
Dan Dascalescu、

1
この答えが私を解決に導いた理由であることを付け加えたいだけです。私はPHPフレームワークを使用していますが、これはORM構文にうまく適合しますが、他のソリューションではうまくいきません。$existing = Users::masterFind('all', ['conditions' => ['traits.0.email' => ['$regex' => "^$value$", '$options' => 'i']]]);
Don Rzeszut

9

Mongo(現在のバージョン2.0.0)では、インデックス付きフィールドに対する大文字と小文字を区別しない検索は許可されていません。ドキュメントを参照しください。インデックスのないフィールドの場合、他の回答にリストされている正規表現で問題ありません。


19
これを明確にするために、大文字と小文字を区別しない検索がインデックス付きフィールドで許可されている場合、インデックスは使用されず、フィールドにインデックスが付けられていない場合と同じくらい遅くなります。
heavi5ide 2011

@ heavi5ideこの質問は重複をマークするために使用されているので、正規表現(大文字と小文字を区別しない検索に必要)はインデックスを使用することを明確にすると思いましたが、フルインデックススキャンを実行する必要があります。つまり、インデックスを効率的に使用できません。幸い、ドキュメントは2011年から更新されていますが、ここでも注意してください。
Sammaye 2014

7

正規表現ベースのクエリを使用する際の重要な注意点の1つ-ログインシステムでこれを行う場合は、検索するすべての文字エスケープし、^および$演算子を忘れないでください。Lodashはこれのための素晴らしい関数を持っています。

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

どうして?.*ユーザーがユーザー名として入力したとします。これはすべてのユーザー名と一致し、ユーザーのパスワードを推測するだけでログインできるようになります。


6

最良の方法は、選択した言語にあります。オブジェクトのモデルラッパーを作成するときに、save()メソッドで、検索対象となるインデックス付きのフィールドセットを反復処理します。それらのフィールドのセットには、対応する小文字があり、それらは検索に使用されます。

オブジェクトが再度保存されるたびに、小文字のプロパティがチェックされ、メインプロパティへの変更で更新されます。これにより効率的に検索できるようになりますが、lcフィールドを毎回更新するために必要な余分な作業は非表示になります。

小文字のフィールドは、key:valueオブジェクトストアか、接頭辞lc_が付いたフィールド名だけです。私は2番目のものを使用してクエリを簡略化します(深いオブジェクトのクエリは時々混乱する可能性があります)。

注:lc_フィールドのインデックスを作成する必要がありますが、それらのベースとなっているメインフィールドではありません。


素晴らしい解決策ですが、幸運なことにMongoDB 3.4以降では、大文字と小文字区別しないインデックスがネイティブでサポートされています。
Dan Dascalescu、

6

「テーブル」の「列」を検索し、大文字小文字を区別しない検索が必要だとします。最良かつ効率的な方法は以下のとおりです。

//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"を設定した鈍感な基準で検索します。

ではごきげんよう。


5

Mongooseを使用すると、これは私のために働きました:

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

8
.toLowerCase()の大文字と小文字を区別しないフラグを指定している場合、冗長ではありませんiか?
k00k 2015

はい、そうです。.toLowerCase()は必要ありません。回答から削除しました。
ChrisRich

うーん、これはそのように機能する必要がありますか?「マーク」を検索すると、「マーク」が付いたすべてのレコードも取得されます。大文字と小文字の区別を無視する方法はありますか?
スイス

わかりました。正しい正規表現は次のようになります: '^' + serach_name + '$'、 "i"
スイス

3
これは危険です。ユーザー名をエスケープしないため、任意の正規表現を挿入できます。
トムMettam

3

集約フレームワークはmongodb 2.2で導入されました。文字列演算子 "$ strcasecmp"を使用して、文字列間で大文字と小文字を区別しない比較を行うことができます。正規表現を使用するよりも推奨され、簡単です。

これは、集約コマンドオペレーターに関する公式ドキュメントです:https : //docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/#exp._S_strcasecmp


4
これをfind()クエリでどのように使用しますか?db.stuff.find({name:$ strcasecmp(name)})?
スイス

3

大文字と小文字を区別しないインデックスを使用できます。

次の例では、デフォルトの照合順序のないコレクションを作成し、大文字と小文字を区別しない照合順序で名前フィールドにインデックスを追加します。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

構文上の小さな問題があるようです(ブレースがありません)。クエリを更新してください: db.users.createIndex( { name: 1 }, {collation: { locale: 'tr', strength: 2 } } )
Mohd Belal

3

変数を検索してエスケープする場合:

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

変数をエスケープすると、 '。*'またはその他の正規表現による攻撃からクエリが保護されます。

エスケープ文字列正規表現


1

RegExpを使用します。他のオプションが機能しない場合は、RegExpが適切なオプションです。文字列の大文字と小文字を区別しません。

var username = new RegExp("^" + "John" + "$", "i");;

クエリでユーザー名を使用し、それを実行します。

私もそれがあなたのために働くことを望みます。ではごきげんよう。


0

フィルターで使用する、大文字と小文字を区別しない正規表現用の単純なFuncを作成しました。

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

次に、次のようにフィールドをフィルターするだけです。

db.stuff.find({"foo": CaseInsensitiveCompare("bar")}).count();

0

フィルターを使用すると、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.Title.ToLower()はプロパティであると見なし、適切にマップされません。


ありがとう、それは私のために働きます。ここでは、変数でフィルターを取得し、Find()メソッドで渡す必要があります。
Nilay

0

Golangを使用していて、mongodbとmgo godoc globalsignライブラリを使用して、大文字と小文字を区別する全文検索をしたい人。

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


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

-1

mongo docsでわかるように、バージョン3.2以降、$textインデックスはデフォルトで大文字と小文字を区別しません:https : //docs.mongodb.com/manual/core/index-text/#text-index-case-insensitiveivity

テキストインデックス作成し、クエリで$ text演算子使用します


フルテキスト検索を使用すると、この場合、間違った(および潜在的にある危険な質問は、大文字と小文字を区別しないクエリ、たとえばことについてだったので、)username: 'bill'マッチングBILLBill、ないにもマッチがなり、フルテキスト検索クエリ、言葉茎billように、Billsbilledなど
ダンDascalescu

-1

これらは文字列検索用にテストされています

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

私は同様の問題に直面していましたが、これは私にとってうまくいったものです:

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

この解決策はすでに2回提供されていました。新しい回答を投稿する前に、既存の回答を確認してください。
Dan Dascalescu、

@DanDascalescuは何を話しているのかわからない、CTRL + Fを押すと、多くの賛成票を持つ同様のソリューションが2018年9月に投稿されました。私は2018年4月に回答を投稿しました。また、本当に助けようとする人に警告する前に、いつ投稿されたかを確認してください。
Woppi

私はこの回答について2016年4月から、この回答は2016年5月から話しています。との両方を使用$regexしてい$optionsます。Ctrl + Fで何をしましたか?
Dan Dascalescu、

また、この他の2016年の回答に対する$regex私の編集で説明したように、使用は非効率的で潜在的に安全ではありません。回答がコミュニティに提供されなくなった場合でも、回答を削除してもかまいません。
Dan Dascalescu、

非効率的な$ regexについて言及しました。ありがとうございます。Ctrl + F $ options。2018年4月と2018年9月の$ regexコードに新しいRegexpがない2つだけです。新しいRegexpを回答に使用しませんでした。新しいRegexpで発生した特定の問題を忘れましたが、それを削除すると解決し、代わりに投稿したこのソリューションを使用しました。
Woppi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.