Apache Solrのschema.xmlを読み解く | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

前回の「Apache Solrのデモ環境を作ってみる 」にて動く環境が作れたので、今回はもう少し突っ込んだApache Solrの設定ファイル周りについて書いていきたいと思います。



Apache Solrの設定ファイルの中身


Apache Solrの環境設定で触るファイルは大きく2つあります。

1つは、Solrの動作自体を定義するsolrconfig.xmlファイル。

そしてもう1つは、Solrに取り込むデータのスキーマ情報を定義するschema.xmlファイルです。

何れも、


/path/to/solr/example/solr/collection1/conf

に、存在します。

先のエントリでも書きましたがcollection1はSolrのコアディレクトリとなるため、コアを追加したらそのコアごとに設定ファイルが存在します。


順に説明したいところではありますが、solrconfig.xmlファイル(Solrの管理メニューのConfigメニューで見える内容もこのファイル)はSolrの動作を定義したもので、Solrが利用するjarファイルのパスやディレクトリの定義をしたり、検索クエリの細かな仕様やキャッシュ周りの設定などができるのですが、そのままでも取あえずは動くため一旦割愛します。


今回は、前回のデモ環境で作った住所録データベースのデータインポート作業をしたときに編集した、schema.xmlファイルについて少しだけ触れてみます。

下記が、前回のデモ環境用の作ったschema.xmlの全体です(一部コメントなどを割愛しています)。


<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.1">
    <types>
        <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
        <fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true" />
        <fieldType name="long" class="solr.LongField" omitNorms="true" />
        <fieldType name="textTight" class="solr.TextField" positionIncrementGap="100" >
            <analyzer>
                <tokenizer class="solr.WhitespaceTokenizerFactory" />
                <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false" />
                <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
                <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0" />
                <filter class="solr.LowerCaseFilterFactory" />
                <filter class="solr.RemoveDuplicatesTokenFilterFactory" />
            </analyzer>
        </fieldType>

        <!-- N-gram analyzed type using CJKAnalyzer -->
        <fieldType name="text_cjk" class="solr.TextField">
            <analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer" />
        </fieldType>

        <!-- Morphologically analyzed type using JapaneseAnalyzer -->
        <fieldType name="text_ja" class="solr.TextField">
            <analyzer class="org.apache.lucene.analysis.ja.JapaneseAnalyzer" />
        </fieldType>
    </types>

    <fields>
        <field name="id"           type="long"      indexed="true" stored="true" required="true" />
        <field name="organization" type="textTight" indexed="true" stored="true" />
        <field name="zip-old"      type="textTight" indexed="true" stored="true" />
        <field name="zip"          type="textTight" indexed="true" stored="true" />
        <field name="prefecture"   type="text_ja"   indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="district"     type="text_cjk"  indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="town"         type="text_ja"   indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="prefecture-undevided" type="string" indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="full_address" type="text_ja" indexed="true" stored="true" termVectors="true" termPositions="true" multiValued="true" />
        <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false" />
        <field name="_version_" type="long" indexed="true" stored="true"/>
    </fields>

    <uniqueKey>id</uniqueKey>

    <copyField source="prefecture" dest="prefecture-undevided" />
    <copyField source="prefecture" dest="full_address" />
    <copyField source="district"     dest="full_address" />
    <copyField source="town"             dest="full_address" />

    <defaultSearchField>full_address</defaultSearchField>

    <solrQueryParser defaultOperator="OR" />
</schema>


schmea.xmlは大きく3つの領域に分けて定義します。

1つ目がtypesタグで括られた利用するフィールドタイプやトークナイザーやフィルタを定義する箇所、2つ目がfieldsタグで括られた実際にSolrへ取り込む際のデータフィールドを定義する箇所、そして3つ目がその他のSolrの設定や取り込む際のデータ加工を指示する箇所です。



Solrで利用するfieldTypeを定義する


設定する1つ目の項目にあるtypesタグ内に定義するのは、フィールドタイプやトークナイザーなどを定義する箇所です。

ここでは、Solrへ取り込む際のデータの型を決めたり、取り込む際の解析に利用するフィルターや単語を解析するためのトークナイザーを定義します。

トークナイザーが何をするものなのかは、下記の記事が参考になるかと思います。


検索エンジンの常識をApache Solrで身につける (1/4) @IT


フィールドタイプは、後のfieldsタグの項目と連動します。

例えば、


<fieldType name="long" class="solr.LongField" omitNorms="true" />

という記述でlong型のフィールドを定義していますが、


<field name="id" type="long" indexed="true" stored="true" required="true" />

の中で、そのlong型を利用することを宣言しています。

要は、データをSolrに取り込んだり検索をする際にそのデータをどう取り込めば(扱えば)よいのかを決めているわけです。


トークナイザーは、データの中から意味ある単語をどう抽出するのかというものですが、形態素解析とN-gramの2種類があり、上記の設定ファイルの中では


<!-- N-gram analyzed type using CJKAnalyzer -->
<fieldType name="text_cjk" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer" />
</fieldType>

<!-- Morphologically analyzed type using JapaneseAnalyzer -->
<fieldType name="text_ja" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.ja.JapaneseAnalyzer" />
</fieldType>

のように、「text_cjk」(N-gram用)と「text_ja」(形態素解析用)にて定義しています。

そして、実際の住所録データの中では、


<field name="district" type="text_cjk" indexed="true" stored="true" termVectors="true" termPositions="true" />
<field name="town" type="text_ja" indexed="true" stored="true" termVectors="true" termPositions="true" />

の中で使われており、このデータの中では市や区のデータが入った「district」には「text_cjk」(N-gram)が、町村などのデータが入った「town」には「text_ja」(形態素解析)が利用されています(何故使い分けているのかはわかりませんが)。


その他に、Solrで用意されている幾つかのトークナイザやフィルタを組み合わせたオリジナルのフィールドタイプを作ることもできます。


<fieldType name="textTight" class="solr.TextField" positionIncrementGap="100" >
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory" />
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false" />
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0" />
<filter class="solr.LowerCaseFilterFactory" />
<filter class="solr.RemoveDuplicatesTokenFilterFactory" />
</analyzer>
</fieldType>

上記では、トークナイザーに「WhitespaceTokenizerFactory」が利用されています。

これは、単純にスペースの区切りで単語を分解するトークナイザです。

その他に、フィルターの中で定義している「SynonymFilterFactory」は、単語の別名を定義するものであったり、「StopFilterFactory」ではNGワードを定義したりもできます。

利用できるトークナイザーやフィルタは、マニュアル にも記載しているのでそちらもあわせて確認してください。



Solrで利用するfieldsを定義する


設定する2つ目の項目のfieldsタグでは、Solrに取り込む各フィールドのデータ型や取り込む際のオプションを定義します。

データ型の話は、先ほど書いたとおりでtypesの中で定義したデータ型を各フィールドにて定義していきます。

fieldsタグの中には幾つかの属性を設定でき、データの扱いを制御できます。

今回のschema.xmlで追加されている属性とその意味は下記の通りです。


属性 設定できる値 意味
indexed true | false フィールドをインデックス(検索対象)するかどうか
stored true | false フィールドを検索結果に含めるかどうか(インデックスはするけど検索結果には含めたくないというようなことができる)
multiValued true | false 複数の検索フィールドを追加することを許可するかどうか
required true | false 必須項目にするかどうか
omitNorms true | false 検索スコアに関連する項目でそのキーワードのスコアの平均点を保持するかどうか(検索スコアに関連しない項目などの場合、falseに設定することでメモリ使用量を抑えることができるらしい)
termVectors true | false 検索キーワードが含まれる数やキーワードの開始、終了位置などを結果に含めるかどうか(termPositionsとtermOffsetsをtrueに設定した場合、termVectorsもtrue扱いとなる)
termPositions true | false 検索キーワードの含まれる位置を返すかどうか
termOffsets true | false 検索キーワードが含まれるオフセットを返すかどうか
default デフォルトに設定したい文字列 そのフィールドのデフォルト値


また、この定義ファイルの中では書かれていませんが、ダイナミックフィールドといってアスタリスクを使って、フィールド名(name)に特定の文字が含まれるものを一括して特定のデータ型や属性を指定するというやり方もできたりします。

属性に設定できる値も含めて、マニュアル を参考にしてみてください。



schema.xmlで定義するその他の設定


3つ目の設定項目では、データフィールドを加工したり検索の挙動を定義するオプションを書いたりします。
今回の設定ファイルの中で、最初に登場するのが


<uniqueKey>id</uniqueKey>

というものですが、これは見ての通りデータの中のユニークキーが何なのかを定義しています。

SolrのRESTなAPIでは、データに対して更新や削除をする際にIDを指定して行うため一意なキーが必要となります。


次に、


<copyField source="prefecture" dest="prefecture-undevided" />
<copyField source="prefecture" dest="full_address" />
<copyField source="district"     dest="full_address" />
<copyField source="town"             dest="full_address" />

という設定ですが、こちらは既に定義済みのフィールドを別のフィールドにコピーするという設定です。

上記の場合、都道府県から市区町村までのデータをコピー(source)してfull_addressという新しいフィールドを作って(dest)います。

検索は通常どこか特定のフィールドに対して行うため、このfull_addressのように複数のデータを連結したフィールドを作っておけば検索にヒットさせやすくなったりします。


続いては、


<defaultSearchField>full_address</defaultSearchField>

という設定項目は、先ほど作ったfull_addressのフィールドをデフォルトの検索フィールドに指定しています。

そして、最後に


<solrQueryParser defaultOperator="OR" />

検索オプションとして、AND検索にするのかOR検索にするのかを指定しています。


このように、schema.xmlではデータの型を定義したり取り込みデータのフィールドやその加工、そして検索のオプションなどが指定できます。

schema.xmlはデータ取り込みの際に必ず必要となりますので、自分の環境に合わせて参考にしてみてください。