Edited at

MongoDB2.4から導入された全文検索

More than 5 years have passed since last update.

==========


何を検証する?


  • 全文検索の内容/設定

  • 日本語の全文検索に対しては? → 未対応

  • 性能検証 → 未検証


参考

http://docs.mongodb.org/manual/applications/text-search/


機能特徴


  • 複数の文字列フィールドでテキストインデックス作成できる

  • 1つのコレクションに対して、1つの索引を作成できる

  • テキストインデックスは、アプリケーションがデータベースからドキュメントを

  • insert,update,deleteしたらリアルタイムで更新される。

  • テキストインデックスとクエリは言語固有のステミングとストップワードをサポートしている。


    • 例) the, an, and などをストップワードとしてdropする。(まだ単純な単語しかないみたい)

    • MongoDBは索引を挿入する間、複数言語をサポートした単純なステミングを使っている.MongoDBはクエリ実行前に自動的にテキストクエリを始める。



  • 対応言語は以下のとおり


    • danish

    • dutch

    • english

    • finnish

    • french

    • german

    • hungarian

    • italian

    • norwegian

    • portuguese

    • romanian

    • russian

    • spanish

    • swedish

    • turkish




デメリット


  • テキストインデックスは肥大化する。(各ドキュメントの各インデックス内に、ユニークな次のステムワードを持ってる)

  • インデックス作成する時間は通常のインデックスを作成するよりも時間がかかる。

  • データ挿入スループットは低下する。(MongoDBは各ドキュメントの各インデックス内に、ユニークな次のステムワードを追加しなければならないため)

  • テキスト検索はmongodの性能に影響があるだろう。特にNOT検索やフレーズマッチは
    効果的にインデックスを有効活用できない。


現時点での制約


  • テキストインデックスはドキュメント内の単語のち情報を格納してはならない。
    コレクション全体がRAMに収まると、効果的な検索ができる。

  • MongoDBは否定語やフレーズを軸にしない

  • インデックスは大文字/小文字を区別しない

  • コレクションは一度に一つのテキストインデックスしか持てない。

  • 結果はBSON Document Size(16MB)に収める必要がある。(後述するproject,filterなどを使用する)


コマンド系


設定

db.adminCommand( { setParameter: 1, textSearchEnabled: true } )

もしくは

mongod --setParameter textSearchEnabled=true


Index作成


  • content, subject インデックス対象


    • 別コレクションも対象にできる。

    • field名 : 1 もしくは-1と書くと昇順/降順の作成も付与できる。



  • name 索引名

  • weights 重み

また、ワイルドカード指定もできる。

更に昇順/降順の索引も付与できる。


ワイルドカード指定のindex作成

 db.contents.ensureIndex({ "$**":"text"}, {name:"ContentsIndex"});



index作成

db.contents.ensureIndex(

{content: "text", subject: "text", <field>:1 or (-1)},
{name:"ContentsIndex", weights:{content:10, subject:5}}
)
db.contents.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "blogs.contents",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"_fts" : "text",
"_ftsx" : 1
},
"ns" : "blogs.contents",
"name" : "ContentsIndex",
"weights" : {
"content" : 10,
"subject" : 5
},
"default_language" : "english",
"language_override" : "language",
"textIndexVersion" : 1
}
]


検索方法


検索コマンド

db.<コレクション名>.runCommand("text", 

{ search: <string>,
filter: <document>,
project: <document>,
limit: <number>,
language: <string> } );

各パラメータの内容は以下のとおり


  • search

    検索対象の文字列.半角スペースで区切るとorで検索を行う。Not検索はワードの前に"-"をつける。(例:A not B -> A -B)
    また、A B Cという単語を検索する場合には "\"A B C \""とダブルクオーテーションを使用する。

  • filter

    指定フィールドに指定文字列が含まれているデータを検索対象にする.
    MongoDBで有効なドキュメントを使用できる。
    filterの条件に一致するデータを出力する。
    例えば fliter : { createAt: { $gt : new Date()}} という指定もできる
    先頭にasc or descとしてindexをつけた場合はfilter設定は必須かつ、equalの条件にする必要がある。
    例えば、

db.contents.ensureIndex({ blogId:1, content: "text", subject: "text"})

というindexを作成した場合は、以下のようなコマンドしか受け付けない。

db.contents.runCommand("text" , { search: "indonesia", filter:{blogId : 100}});

db.contents.runCommand("text" , { search: "indonesia", filter:{blogId : {$gt:100}}});

{ "ok" : 0, "errmsg" : "BadValue need have an equality filter on: blogId" }


  • project

    戻りフィールドを限定する.

  • limit

    検索結果件数

  • language

    トークンなどに関わってくる検索言語を指定.デフォルトは英語
    今のところ日本語は対応していない。


出力内容

パラメータ名
内容

language
全文検索を行った言語

results
検索結果やスコアを格納した配列

results.score
検索結果のスコア

results.obj
検索結果のオブジェクト

stats
ステータスを格納したオブジェクト

stats.nscanned
検索したindex数

stats.nscannedObjects
検索したドキュメント数(filterを設定するとカウントアップされる)

stats.n
resultsの要素数. BSON Document Sizeを越えた場合、resultsの要素数より少なくなる場合がある

stats.nfound
ドキュメントが一致している総数を返却する。BSON Document Sizeを越えた場合はstats.nより大きくなる場合がある

stats.timeMicro
検索にかかった時間(マイクロ秒)

ok
1 が返却される

> db.contents.runCommand("text", { search: "america"});

{
"queryDebugString" : "america||||||",
"language" : "english",
"results" : [
{
"score" : 7.5,
"obj" : {
"_id" : ObjectId("5151abf80e5ebfcf71b9f9f9"),
"contentsId" : 10000,
"blogId" : 100,
"subject" : "アメリカ訪問",
"content" : "Let's go America",
"createAt" : 1364306936770
}
},
{
"score" : 7.5,
"obj" : {
"_id" : ObjectId("5151abf80e5ebfcf71b9f9fa"),
"contentsId" : 11000,
"blogId" : 100,
"subject" : "アメリカ訪問",
"content" : "遊びにいった i have been to Amarica",
"createAt" : 1364306936775
}
},
{
"score" : 7.5,
"obj" : {
"_id" : ObjectId("5151abf80e5ebfcf71b9f9fb"),
"contentsId" : 12000,
"blogId" : 100,
"subject" : "America訪問",
"content" : "Amaricaへ遊びにいった i have been to Amarica",
"createAt" : 1364306936779
}
}
],
"stats" : {
"nscanned" : 3,
"nscannedObjects" : 0,
"n" : 3,
"nfound" : 3,
"timeMicros" : 96
},
"ok" : 1
}

contentsIdとcontentのみ出力を絞る

> db.contents.runCommand("text", { search: "america", project: {contentsId:1, content:1}});

{
"queryDebugString" : "america||||||",
"language" : "english",
"results" : [
{
"score" : 11.25,
"obj" : {
"_id" : ObjectId("5151abf80e5ebfcf71b9f9fc"),
"contentsId" : 20000,
"content" : "i have been to america. アメリカ言ってみた"
}
}
],
"stats" : {
"nscanned" : 1,
"nscannedObjects" : 0,
"n" : 1,
"nfound" : 1,
"timeMicros" : 76
},
"ok" : 1
}