7.1 スキーマ定義ファイル

スキーマ定義ファイルの配置場所と設定

managed-schema あるいは schema.xml は、通常はコアディレクトリは以下の conf/ に配置する。 スキーマ定義ファイルの格納場所は、schema.xml で設定できる。 以下はデフォルトの設定。

<schemaFactory class="ManagedIndexSchemaFactory">
	<!-- Schema API を使って編集可能にする場合は true にしておく -->
	<bool name="mutable">true</bool>
	<!-- スキーマ定義ファイル名 -->
	<str name="managedSchemaResourceName">managed-schema</str>
</schemaFactory>

従来の schema.xml を使う場合は以下のように設定する。

<schemaFactory class="ClassicIndexSchemaFactory" />

スキーマ定義の構成要素

要素 定義の省略の可否 Schema API での編集可否
フィールドタイプ x(必須) o
フィールド o o
ダイナミックフィールド o o
ユニークキーフィールド o x
コピーフィールド o o
Similarity o 一部 o

スキーマレスモード

  • スキーマにフィールドを定義しなくても検索・更新ができる仕組み
  • インデクシング時にスキーマ定義に設定されていないフィールドが存在した場合、フィールド値からフィールドタイプを推測して自動的にフィールドが定義される
  • 推測に際しては、フィールド値を Java のプリミティブ型のラッパークラスで解析し、パースできるフィールドタイプを単純に選択する
    • ex. true なら boolean, 1なら int
  • 整数値と文字列が混在するようなフィールドの場合に意図してフィールドタイプにならない可能性があるため、推奨されない
  • そのため、スキーマレスモードよりはダイナミックフィールドを利用するのが一般的

7.2 フィールドタイプ

フィールドタイプの設定オプション

オプション デフォルト 説明
name - 必須。フィールドの名称
class - 必須。フィールドタイプの実装クラス
indexed true 検索対象フィールドか否か
stored true フィールド値を保存するか否か
sortMissingLast false そのフィールドを使ってソートする際、フィールド値を持たないドキュメントを最後に持ってくる
sortMissingFirst false そのフィールドを使ってソートする際、フィールド値を持たないドキュメントを最初に持ってくる
multiValued false フィールドが複数の値を持つか
omitNorms 非テキスト系は true
テキスト系は false
norm 値を保存するなら false
omitTermFreqAndPositions 非テキスト系は true
テキスト系は false
tf 値とポジションを保存するなら false
omitPositions false true の場合、Position 情報は保存されないが、tf 値は保持する
positionIncrementGap 0 multiValued=true のフィールドにおいてフィールド値をまたがるフレーズ検索をする場合の調整パラメータ(整数)
postingsFormat なし フォーマット
required false フィールド値を持つことを必須とするか
termVectors
termPositions
termOffsets
termPayloads
false termVectors / termPositions / termOffsets / termPayloads を保存するか
autoGeneratePhraseQueries false 自動でフレーズ検索を行うかどうか
docValues 非テキスト系は true
テキスト系は false
ソートやファセット時、特定のフィールドの値だけを参照したいときに高速でサーチするための列指向フィールドを作成するかどうか(後述)
useDocValuesAsStored false docValues=true とした場合、stored=true が設定されていなくても検索結果にフィールド値を含めるかどうか
similarity BM25 フィールドタイプごとに similarity を指定する場合は設定する
  • sortMissingLast, sortMissingFirst
    • ともに false の場合、フィールド値を持たないドキュメントは昇順ソートなら最初、降順ソートなら最後に配置される
    • ともに true の場合、常に最後に配置される?(未確認。sortMissingLast が優先されるのか?)
  • autoGeneratePhraseQueries
    • 検索時にトークナイズされたトークンを自動的にフレーズとして扱い、フレーズ検索を行う設定
    • ex. デフォルト検索式 OR で「検索エンジン」というキーワードで検索した時(「検索」「エンジン」とトークナイズされる)
      • autoGeneratePhraseQueries=false(デフォルト):
        • 「検索」OR「エンジン」の検索
        • どちらかの単語を含んでいればヒット
      • autoGeneratePhraseQueries=true:
        • 「検索」の後に「エンジン」が出現するドキュメントを探すフレーズ検索
        • この順で(間になにも挟まず)両トークンが並んでいればヒット
  • omitNorms
    • norm 値:ドキュメントと検索クエリの「近さ」をスコア化する際、「複数文書が同じキーワードにヒットしても文書が長い(単語数が多い)ほどスコアを低くする」といった調整を行うため、あらかじめ Solr が計算する値
    • omitNorms=true を設定すると norm 値が省略され、インデックスが小さくなり、検索時のメモリ消費量も抑えられる
  • subFieldSuffix(point / location フィールドタイプ)
    • 内部で管理するサブフィールドに使用する suffix
    • point や location フィールドタイプは複数の値(ex. 経度・緯度)を持てるが、内部的にはそれらの値は別々のフィールドとして管理される
    • これらの内部的なフィールド管理用に suffix を定義する必要がある
  • dimension(point フィールドタイプ)
    • 複数の値を保持するフィールドの次元数を指定する
    • location のような経度・緯度なら2、xyz 3次元座標をもたせるなら3を指定する
  • docValues
    • ソートやファセット機能では、大量のドキュメントに対し、特定のフィールドの値だけを高速参照する必要がある
    • 通常のインデックス:
      • ドキュメント ID を第一のキーとして各フィールドの値を並べて持つ(行指向
      • 特定フィールドしか必要がなくても全データをサーチする必要がある
      • この負荷を軽減するためフィールド値キャッシュが利用される
    • docValues=true が指定された時:
      • フィールドを第一のキーとして持ち、各ドキュメントの該当フィールドの値を並べて持つ(列指向)フィールドが作られ、インデックスの一部として管理される
      • 特定フィールドのみを高速でロードできるため、フィールド値キャッシュは利用されなくなる
      • インデックスファイルのサイズは増加するが、Java ヒープ領域の利用サイズを減らす効果がある ```json

        通常のインデックス(行指向)

        { “doc1”: {“field1”: 1, “field2”: 2, “field3”: 3}, “doc2”: {“field1”: 4, “field2”: 3, “field3”: 6}, “doc3”: {“field1”: 7, “field2”: 2, “field3”: 7}, … }

docValues=true としたときに追加されるデータ形式(列指向)

{ “field1”: {“doc1”: 1, “doc2”: 4, “doc3”: 7, …}, “field2”: {“doc1”: 2, “doc2”: 3, “doc3”: 2, …}, “field3”: {“doc1”: 3, “doc2”: 6, “doc3”: 7, …}, }



## 非テキスト系フィールドタイプ

schema.xml の定義の書式については2章参照。

- 設定で任意のフィールドタイプ名をつけられるが、非テキスト系フィールドタイプでは慣習的に決まった名前が広く使用されているため、独自の名前をつけるのは推奨されない
- 以下は basic_configs をベースとしたスキーマに定義されているフィールドタイプの一覧

| フィールドタイプ名 | クラス | 説明 |
| :-- | :-- | :-- |
| string | solr.StrField | 文字列 |
| boolean | solr.BoolField | true / false |
| int | solr.TrieIntField | integer |
| long | solr.TrieLongField | long |
| float | solr.TrieFloatField | float |
| double | solr.TrieDoubleField | double |
| date | solr.TrieDateField | 日付(年月日時分秒ミリ秒) |
| binary | solr.BinaryField | 画像などのバイナリ |
| random | solr.RandomSortField | ランダムソートを行うフィールド |
| point | solr.PointType | 多次元の値の検索を行うフィールド |
| location | solr.LatLonType | 経度と緯度の検索を行うフィールド |
| location_rpt | solr.SpatialRecursivePrefixTreeFieldType | 経度と緯度の検索を行うフィールド |
| currency | solr.CurrencyField | レートを考慮した外貨計算を行うフィールド |

- 他にも以下のようなフィールドタイプクラスが用意されている

| クラス | 説明 |
| :-- | :-- |
| solr.UUIDField | ユニークな値を自動設定するフィールド |
| solr.CollectionField | Unicode に変換したソートや範囲検索を可能にするフィールド |
| solr.ICUCollationField | solr.CollationField と同様だが、ICU4J ライブラリを使用して高速化 |
| solr.EnumField | ユーザ定義された列挙値をフィールドに使用 |
| solr.PreAnalyzedField | すでにトークン化された内容をフィールドに使用 |


## テキスト系フィールドタイプ

schema.xml の定義の書式については2章参照。アナライザを内包する書式。
- テキスト系フィールドタイプでは、フィールドタイプ名の先頭に "text" をつける慣習がある

## アナライザ

以下のように1つだけアナライザを設定した場合、インデクシング時もクエリの際にも同じ設定が適用される。

```xml
<fieldType name="text_hoge" class="solr.TextField" positionIncrementGap="100" multiValued="true">
	<analyzer>...</analyzer>
</fieldType>

対して、以下のようにインデクシング時とクエリの際とで異なる設定を用いることもできる。

<fieldType name="text_hoge" class="solr.TextField" positionIncrementGap="100" multiValued="true">
	<analyzer type="index">...</analyzer>
	<analyzer type="query">...</analyzer>
</fieldType>

アナライザの設定では、文字フィルタ・トークナイザ・トークンフィルタをこの順序で記載する

  • 文字フィルタ(charFilter):省略可能、数の制限なし
  • トークナイザ(tokenizer):必須、1つだけ
  • トークンフィルタ(filter):省略可、数の制限なし
<analyzer>
	<charFilter>...</charFilter>
	<tokenizer>...</tokenizer>
	<filter>...</filter>
</analyzer>

文字フィルタ

できることの例:

  • 特殊文字を置換する
  • 日本語の半角・全角を一方に寄せる
  • ドイツ語などのリガチャー(合字)を処理する

MappingCharFilterFactory

指定されたマッピングファイルに基づき、文字単位で置換を行う →検索時の表記ゆれ処理を文字単位で行える

<charFilter class="solr.MappingCharFilterFactory" mapping="lang/mapping-hoge.txt"/>

マッピングファイルの書式:

"ザ" => "ザ"
"①" => "1"
"壱" => "一"
...

マッピングは1行ずつ個別に動作するため、以下のように定義しても A は C にならない。

"A" => "B"
"B" => "C"

ICUNormalizer2CharFilterFactory

Unicode 正規化にもとづき、半角英数字や機種依存文字を置き換える。 ex. 「㌔」→「キロ」

<charFilter class="solr.ICUNormalizer2CharFilterFactory"/>

PatternReplaceCharFilterFactory

指定した正規表現にマッチする文字を置換する。 以下は、小文字アルファベット以外の文字を空文字に置き換える設定。

<charFilter class="solr.PatternReplaceCharFilterFactory" pattern="([^a-z])" replacement=""/>

HTMLStringCharFilterFactory

テキストから HTML タグを取り除く。

<charFilter class="solr.HTMLStringCharFilterFactory"/>

代表的なトークナイザ

JapaneseTokenizerFactory

形態素解析器「Kuromoij」を利用したトークナイザ。

オプション一覧:

オプション 説明
mode search, normal, extended トークン分割モード。デフォルトは search
userDictionary ユーザ辞書のファイル名  
userDictionaryEncoding ユーザ辞書のエンコード Java の CharSet クラスに対応。デフォルトは UTF-8
discardPunctuation true, false 括弧などの特殊文字を残す場合は false。デフォルトは true
nBestCost
nBestExamples
任意の数値
任意の例文
N-best 機能を使用する場合の設定

mode による違い:

  • normal
    • 辞書にある単語単位で分割
    • 「羽田空港に行く」→「羽田空港 / に / 行く」
  • search
    • 辞書にある単語単位で分割し、更に辞書の単語を分割
    • 「羽田空港に行く」→「羽田 / 羽田空港 / 空港 / に / 行く」
  • extended
    • search mode の内容に加え、辞書未登録の単語を一文字単位で分割
    • 「アキバに行く」→「ア / キ / バ / に / 行く」
<tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_hoge.txt"/>

ユーザ辞書のフォーマット:登録単語,登録単語を更に分割した場合の単語群(スペース区切り),単語の読み(カタカナスペース区切り),品詞名

関西国際空港,関西 国際 空港,カンサイ コクサイ クウコウ,カスタム名詞

【注意】ユーザ定義辞書を反映させるために必要なこと

  • Solr コアのリロード or 再起動
  • インデックスの再生成

WhitespaceTokenizerFactory

英語のような、単語の間がホワイトスペースで区切られている言語に向くトークナイザ。

StandardTokenizerFactory

  • 連続した英数字やカタカナを1単語として切り出す
  • ハイフンや句読点は除去(小数点のピリオドは例外)
  • 英数字・カタカナ以外の全角文字は1文字単位で分割

ex.「本日, Solr-6.4がリリースされました」→「本日 / Solr / 6.4 / が / リリース / さ / れ / ま / し / た」

NGramTokenizerFactory

日本語や中国語など、文章を構成する単語がスペースで区切られていない言語に向くトークナイザ。 機械的に指定した文字数で文書から文字列を切り出す。

ex. 「Solrは検索エンジンです」→「So / ol / lr / rは / は検 / 検索 / 索エ / エン / ンジ / ジン / ンで / です / す。」

<tokenizer class="solr.NGramTokenizerFactory" minGramSize="2" maxGramSize="2"/>

代表的なトークンフィルタ

JapaneseBaseFormFilterFactory

動詞や形容詞など活用のある品詞を基本形に変換する。

JapanesePartOfSpeechStopFilterFactory

単語の品詞情報によって単語のフィルタリングを行う(助詞を取り除く、など)。 基本的に品詞情報を付与してくれる JapaneseTokenizerFactory と組み合わせて使う。

<filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_hoge.txt"/>

CJKWidthFilterFactory

全角と半角の文字を正規化する。

  • 日本語の半角カタカナを全角に置換
  • 全角英数字を半角に置換

StopFilterFactory

テキストファイルに列挙した単語(ストップワード)を取り除くトークンフィルタ。 大文字小文字を区別しない場合は ignoreCase=”true” を指定する。

<filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_hoge.txt"/>

JapaneseKatakanaStemFilterFactory

カタカナ語末尾にある長音記号「ー」の表記ゆれを吸収する。 ex. 「フィルター」,「コンピューター」→「フィルタ」,「コンピュータ」

minimumLength オプションを指定しておくと、(末尾の「ー」を含めて)指定した文字数以上のカタカナ語のみにフィルタが適用される。 ex. minimumLength=4,「マザー」→「マザー」

<filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>

LowerCaseFilterFactory

ASCII 文字のトークンにおける大文字を全て小文字に変換する。 ex. 「Java」「JAVA」「java」→「java」「java」「java」

SynonymFilterFactory

「首相」で検索したときに「内閣総理大臣」をヒットさせたいような場合に用いるトークンフィルタ。

<filter class="solr.SynonymFilterFactory" synonyms="lang/synonyms.txt" mode="normal" ignoreCase="true" expand="true" tokenizerFactory="solr.JapaneseTokenizerFactory" userDictionary="lang/userdict_hoge.txt"/>

シノニム辞書ファイルのフォーマット:

  • 片方向:検索語 => 同義語
  • 双方向:同義語1,同義語2,同義語3,...
トマト => 野菜
引っ越し,引越し,引越

7.3 フィールドの定義

フィールド定義の書式(schema.xml / managed-schema):

<field name="id" type="string" indexed="true" stored="true" required="true"/>
オプション デフォルト 説明
name - 必須
type - 必須
default - デフォルト値
indexed true 検索可能なフィールドにしたい場合は true
stored true オリジナルのテキスト情報をインデックスに保管したければ true
(これを false にすると、形態素解析などでばらばらになったトークンだけが保存され、もとの文書は保存されない:検索結果として取得したりハイライトしたりできない
sortMissingLast false フィールド値を持たない場合のソート設定
sortMissingFirst false フィールド値を持たない場合のソート設定
multiValued false 1つのフィールドに複数の値を保持したければ true
omitNorms 非テキスト系は true
テキスト系は false
norm 値を保存するなら false
omitTermFreqAndPositions 非テキスト系は true
テキスト系は false
tf 値とポジションを保存するなら false
omitPositions false true の場合、Position 情報は保存されないが、tf 値は保持する
required false 必須フィールドにしたければ true
termVectors
termPositions
termOffsets
termPayloads
false termVectors / termPositions / termOffsets / termPayloads を保存するか
docValues   7.2節参照
useDocValuesAsStored true 7.2節参照
simularity BM25 フィールドタイプごとに similarity を指定する場合は設定する

ユースケースと設定:

ユースケース indexed stored multiValued omitNorms termVectors termPositions docValues
検索対象フィールド true            
内容を結果として取得するフィールド   true(※)         true(※)
ユニークキーとして使うフィールド true   false        
ソート用のフィールド true   false true     true
クエリブースト利用フィールド       false      
ドキュメントブースト利用フィールド       false      
ハイライトフィールド true true     true true  
ファセットフィールド true           true
マルチバリューフィールド     true        
データ長を考慮せずスコア計算するフィールド       true      
MorLikeThis 検索フィールド         true    

※「内容を取得するフィールド」は、stored か docValues どちらかが true であれば良い

ダイナミックフィールドの定義

ダイナミックフィールド:フィールド名とフィールドタイプの組み合わせを動的に決定できるフィールド

<dynamicField name="*_dt" type="data" indexed="true" stored="true"/>

name="*_dt"はフィールド名のルールで、ワイルドカード(*)が使える。 この例の場合、インデックスに登録するときや検索する時にフィールド名が _dt で終わっていたらこの設定を適用する。

ユニークキーフィールドの定義

ユニークキーフィールド:Solr のドキュメントをユニークに決定するキーとなるフィールド

<fields>
	<field name="hoge" indexed="true" ... />
	...
</fields>
<uniqueKey>hoge</uniqueKey>

コピーフィールドの定義

コピーフィールド:ドキュメントをインデックスに登録する際、指定したフィールドにフィールド値をコピーするよう Solr に指示する機能

<field name="title" type="string" indexed="true" stored="false"/>
<field name="text" type="string" indexed="true" stored="false"/>
...
<copyField source="title" dest="text" maxChars="100"/>

maxChars で文字数を制限して、ドキュメントの先頭部分だけを格納するフィールド(ニュース記事の先頭一部とか)を作ったりできるのがメリット…??

複数のフィールドから同じフィールドへコピーを行っても良いが、その場合 dest にあたるフィールドは multiValued=”true” である必要がある。

その他のフィールドとオプション

_version_ フィールド

内部管理用のフィールド。インデックスを作成するとき自動で管理番号が登録される。 ユーザが意識して値を登録することはない。

_root_ フィールド

ネストされたドキュメントの親子関係を関連付けるフィールド。 ユーザが意識して値を登録することはない。

random フィールド

ランダムソート専用のフィールドであり、ユーザが意識して値を登録することはない。

default オプション

date タイプでは NOW が便利。 そのドキュメントの追加 or 更新時点のシステム時刻が設定される。

<field name="last_update" type="date" indexed="true" stored="true" default="NOW" />

7.4 Similarity の定義

スコア:ドキュメントとクエリの相関度合い Similarity:スコアの計算方式

Similarity は Solr 5 以前は TF-IDF がデフォルトだったが、Solr 6 からは BM25 がデフォルトになった。 TF-IDF(= ClassicSimilarity)を使わせたい場合は以下のように設定する。

<fieldType name="hoge" class="solr.TextField" ...>
	<analyzer>...</analyzer>
	<similarity class="solr.ClassicSimilarityFactory"/>
</fieldType>