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

==========

何を検証する？
----

* 全文検索の内容/設定
* 日本語の全文検索に対しては？ → 未対応
* 性能検証 → 未検証

参考
----
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などを使用する)


コマンド系
----

設定
----

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

もしくは

```bash
mongod --setParameter textSearchEnabled=true
```

Index作成
----

* content, subject インデックス対象 
	* 別コレクションも対象にできる。
    * field名 : 1 もしくは-1と書くと昇順/降順の作成も付与できる。
* name 索引名
* weights　重み

また、ワイルドカード指定もできる。
更に昇順/降順の索引も付与できる。

```javascript:ワイルドカード指定のindex作成
 db.contents.ensureIndex({ "$**":"text"}, {name:"ContentsIndex"});
```


```javascript: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
	}
]
```

検索方法
----

```javascript:検索コマンド
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 が返却される |

```javascript
> 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のみ出力を絞る

```javascript
> 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
}
```