私はMongoDBが初めてですが、ここに行きます:
MongoDB で部分検索を行う方法はありますか?
たとえば、ドキュメントが { 名前: マイケル A. ジョンソン} のようなものである場合、名前: マイケル ジョンソンのクエリに対してそのドキュメントを返し、その逆も同様に検索をフォーマットする方法はありますか?
また、'Name: Johnson' を検索してドキュメント { Name: 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
(文字列) MongoDB がテキスト インデックスをクエリするために解析して使用する用語の文字列。MongoDB は、句として指定されていない限り、用語の論理 OR 検索を実行します。
関連性に基づいて結果を並べ替えたい場合 (上記の例で起こっていること) は、次のようにメタtextScore
プロパティを使用します$meta
(並べ替えで複製することを忘れないでください)。
演算子は
$text
、インデックス付きフィールドに検索語を含む各ドキュメントにスコアを割り当てます。スコアは、特定のテキスト検索クエリに対するドキュメントの関連性を表します。スコアsort()
は、射影式の一部であるだけでなく、メソッド仕様の一部でもあります。式は、操作の{ $meta: "textScore" }
処理に関する情報を提供します$text
。
$text
複数のフィールドで個別に機能しません。この場合、次を使用します$regex
。
{ field: { $regex: '\bWORD\b', $options: 'i' } }
正規表現の書き方は対象外です。SOで検索を行います。
$text
サブジェクト文字列のすべての「単語」がスペースで区切られた「用語」である場合の動作を模倣するには、' '
各用語を分割してオブジェクトにマッピングすることにより、正規表現オブジェクトの配列を作成でき$regex
ます。これがユーザー入力の場合、正規表現の一部と見なされる可能性のあるすべてのメタ文字もエスケープすることが重要です。最後に、$or
検索したいすべてのサブジェクトを特徴とする式を作成するか、代わり$and
に$not
, など...
$or
(logical 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}
]
});
注: 慣例により、フィールド名は通常キャメルケースで小文字で始まります。つまり、フィールドは「Alma Mater」でも「AlmaMater」でもなく「almaMater」です。しかし、元の質問に沿ったものにするために、最初の文字の上限を維持しています。