ご利用にはKandaSearchへの
ユーザー登録(無料)が必要です
最新バージョン: 1.1.0
開発者: RONDHUIT
ダウンロード数: 39
最終更新日: 2022-07-14
Copyright: RONDHUIT Co.,LTD
最新バージョン: 1.1.0
開発者: RONDHUIT
ダウンロード数: 39
最終更新日: 2022-07-14
Copyright: RONDHUIT Co.,LTD
Apache Solr ハンズオンセミナー「第2回 スキーマ設定の基本」のスライドです。
第2回 スキーマ設定の基本 Apache Solr ハンズオンセミナー
株式会社ロンウイット
関口宏司 @kojisays
2022年5月17日 | 初版 |
---|---|
2022年5月18日 | ECサンプルデータの taxKbn の具体例を現状に合わせて “1” から false に修正。 |
2022年5月20日 | エクステンション利用に関する注意事項を追加。 |
2022年7月14日 | アンケート経由の類義語に関する質問と回答を追加。 |
これから Apache Solr のいろいろな機能を使っていきます。本セミナーでは Apache Solr 環境を簡単に準備できる KandaSearch を通じて Apache Solr を使っていきます。
そのためには、 https://kandasearch.com/ にログイン後、プロジェクトを作成します。
(お試しになりたい方のみお試しください。このステップを飛ばしても以降の[演習]には差し支えございません)
作成したプロジェクト内に検索エンジンインスタンス、つまり Apache Solr を作成してみましょう。
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.6">
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false"/>
<field name="sku" type="text_ws" indexed="true" stored="true" omitNorms="true"/>
<field name="name" type="text_ws" indexed="true" stored="true"/>
<field name="cat" type="string" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_i" type="pint" indexed="true" stored="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
<uniqueKey>id</uniqueKey>
<copyField source="cat" dest="text"/>
<copyField source="name" dest="text"/>
<fieldType name="string" class="solr.StrField" sortMissingLast="true"/>
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
<fieldType name="pint" class="solr.IntPointField"/>
<fieldType name="pdate" class="solr.DatePointField"/>
<fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
</analyzer>
</fieldType>
</schema>
<fieldType />
でフィールド型を設定<field />
でフィールド型使ったフィールドを設定{
"id" : "6",
"productCode" : "RKK0000003620",
"productName" : "付箋(ピンク)100枚",
"productNameKana" : "ふせん(ぴんく)100まい",
"price" : 105,
"taxKbn" : false,
"makerName" : "KOKOYO",
"makerNameKana" : "ここよ",
"makerProduCode" : "A0BD5021436633",
"categoryL" : "オフィス用品",
"categoryM" : "事務用品",
"categoryS" : "ラベル",
"categoryFacetInfo" : "オフィス用品/事務用品/ラベル",
"stock" : 217,
"size" : "72mm×72mm",
"weight" : "45",
"unitName" : "袋",
"salesUnit" : "1",
"productInfo" : ["付箋、いわゆるポストイットです。", "のりの強さは製造工場によりばらつきがあります。ご了承ください。"],
"standardDeliveryDate" : "3",
"campaignPrice" : 0,
"dateSince" : "2006-05-02T00:00:00Z"
}
フィールドとは、検索対象文書の構成要素で、idやproductCode, productName, … がフィールドです。
productInfo は multiValued フィールドです。
フィールド型は<fieldType />
で設定します。
<fieldType name="string" class="solr.StrField" sortMissingLast="true"/>
フィールド型には以下のように<analyzer />
子要素を持つものがあります。
<analyzer />
子要素を持つフィールド型を本書では「テキスト系フィールド型」と総称します。<analyzer />
子要素を持たないフィールド型を「基本フィールド型(または非テキスト系フィールド型)」と総称することにします。<fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
</analyzer>
</fieldType>
以下のような基本フィールド型があります。
<field name="id" type="string" indexed="true" stored="true" required="true"/>
<field name="done" type="boolean" indexed="true" stored="true"/>
<field name="price" type="pint" indexed="true" stored="true"/>
<field name="date" type="pdate" indexed="true" stored="true"/>
<field name="store" type="location" indexed="true" stored="true"/>
ライブラリーにあるECサイトのサンプルを使ってecコレクションを作成し、サンプルデータをインデクシングします。その上でKandaSearchやSolr Admin UIを使って以下を実行してみましょう。
<analyzer />
子要素で、テキスト分析の方法を細かく設定できます。<analyzer type="index">...</analyzer>
<analyzer type="query">...</analyzer>
```xml
<analyzer>
<charFilter class="solr.MappingCharFilterFactory" mapping="mapping.txt"/>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
コンピューターを買った
↓
↓
コンピューターを買った
↓
```<tokenizer />``` 単語分割
↓
コンピューター, を, 買っ, た
↓
```<filter />``` トークンのフィルタリングや類義語展開など
↓
コンピュータ, 買う, PC, パソコン
## P17 [演習4] テキスト系フィールド型の動作を確認
前の演習で作成したECサイトを使います。KandaSearch UIでの検索の場合、以下のように設定してください。
- ユニークキー:id、タイトル:productName、ボディ:productInfo、クエリパーサー:edismax
その上で、下記の検索を試してみましょう。
1. 「のり」や「ボンド」で検索
2. 「パソコン」や「パソコン」(半角カナ)で検索
3. 「青」や「ブラック」(半角カナ)で検索
4. 「黄色」や「黄」で検索 ⇒ ヒット0件
5. 「イエロー」で検索 ⇒ ヒットあり
## P18 text_ja フィールド型の設定詳細
```xml
<fieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="true" positionIncrementGap="100">
<analyzer type="index">
<charFilter class="solr.MappingCharFilterFactory" mapping="mapping-kana.txt"/>
<tokenizer class="solr.JapaneseTokenizerFactory" mode="normal"/>
<filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.SynonymGraphFilterFactory" tokenizerFactory="solr.JapaneseTokenizerFactory"
expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
</analyzer>
<analyzer type="query">
<charFilter class="solr.MappingCharFilterFactory" mapping="mapping-kana.txt"/>
<tokenizer class="solr.JapaneseTokenizerFactory" mode="normal"/>
<filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
<filter class="solr.ManagedSynonymGraphFilterFactory" managed="synonym-ja"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
CharFilterの一種で、Tokenizerの前段でmappingに指定されたファイル内容に基づき、文字を正規化します。
<charFilter class="solr.MappingCharFilterFactory" mapping="mapping-kana.txt"/>
mapping-kana.txt
"ア" => "ア"
"イ" => "イ"
"ウ" => "ウ"
"エ" => "エ"
"オ" => "オ"
"ガ" => "ガ"
"ギ" => "ギ"
"グ" => "グ"
"ゲ" => "ゲ"
"ゴ" => "ゴ"
JapaneseTokenizer
<tokenizer class="solr.JapaneseTokenizerFactory" mode="normal"/>
JapaneseKatakanaStemFilter
<filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
LowerCaseFilter
<filter class="solr.LowerCaseFilterFactory"/>
シノニム(類義語)のトークンフィルターです。synonymsに指定されたファイル内容に基づき、特定シノニムに正規化または展開します。
<filter class="solr.SynonymGraphFilterFactory" tokenizerFactory="solr.JapaneseTokenizerFactory"
expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
synonyms.txt
付箋, ポストイット
ボンド, のり, 接着剤
ブルー, 青
イエロー, 黄色, 黄
グリーン, 緑
ブラック, 黒
レッド, 赤
ホワイト, 白
pc, パソコン
Solr Admin UI の Analysis 画面を使うと、テキスト系フィールド(型)の動作を詳細に見ることができます。デバッグ用途でよく使われます。text_jaフィールド型を対象に、以下のキーワードを解析(Analyze)してみます。
(注意)この演習では Verbose Output をオフにしておきます。
アンケートで、「text_jaで『接着 / 剤』と単語分割される『接着剤』が『のり』でヒットするのはなぜか。text_jaで『イエロ』と出力されるため『イエロー』と『黄色』が相互ヒットしないこととの違いがわからない」というご質問をいただきました。
MCF, JT, JKSF, LCF, SGF, LBTS
まず前提として、類義語定義ファイルには次のように複数単語からなるキーワードも問題なく登録でき、相互ヒットさせることができます。
www, world wide web # (1)
who, world health organization # (2)
これは、類義語定義を一種のオートマトンのように保持しており、入力単語列が受理されると、当該類義語定義レコードが出力できるようになっているためです。
現在Luceneで提供されているSynonymGraphFilterの前身のSynonymFilterはSolrで開発されました。Solr公開当時、2-gramフィールドにおける類義語定義辞書は、前述のオートマトン構造を考慮し、ユーザーは次のように定義しなければなりませんでした。
マク クド ドナ ナル ルド, マッ ック, マク クド
これではあまりにも大変なので、tokenizerFactory機能が関口により実装され、上記のように書かなくてもすむようになりました。
<filter class="solr.SynonymFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"
tokenizerFactory="NGramTokenizerFactory"
tokenizerFactory.migGramSize="2" tokenizerFactory.maxGramSize="2"/>
当時のチケット(※1)を見ると、「tokenizerFactoryではなくtypeとしたらどうか」という意見が出されました。
(理想)
<filter class="solr.SynonymFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"
type="text_2g"/>
しかしながら、「まさにtext_2g型を作成している最中に未完成のtext_2gを指定できない」というジレンマから、最初の提案通り、tokenizerFactoryでの妥協となりました。もしtokenizerFactoryではなくtypeが指定できていたら、「黄色」で「イエロー」がヒットしていたでしょう。
※1 https://issues.apache.org/jira/browse/SOLR-319
WhitespaceTokenizer<tokenizer class="solr.WhitespaceTokenizerFactory"/>
NGramTokenizer<tokenizer class="solr.NGramTokenizerFactory" maxGramSize="2" minGramSize="2"/>
KeywordTokenizer<tokenizer class="solr.KeywordTokenizerFactory"/>
StopFilter<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords_en.txt"/>
EnglishPossessiveFilter<filter class="solr.EnglishPossessiveFilterFactory"/>
PorterStemFilter<filter class="solr.PorterStemFilterFactory"/>
続けてSolr Admin UI の Analysis 画面を使って講師と共に以下のフィールド型の動作を確認しましょう。
(注意)この演習では適宜 Verbose Output をオンにします。
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false"/>
<field name="sku" type="text_ws" indexed="true" stored="true" omitNorms="true"/>
<field name="name" type="text_ws" indexed="true" stored="true" autoGeneratePhraseQueries="true"/>
属性 | 説明 |
---|---|
indexed | true と指定すると、当該フィールドの転置インデックスが作成され、検索可能になります。 |
stored | true と指定すると、当該フィールドの値が保存され、flでの指定が可能になります。ハイライト対象フィールドはtrueにしなければなりません。 |
required | true と指定すると、必須フィールドとみなされ、インデクシング時に当該フィールド値が設定されていないと、エラーになります。 |
multiValued | true と指定すると、フィールド値を複数持たせることができます。ECデータの productInfo は multiValued="true" とするフィールドの例です。 |
omitNorms | true と指定すると、当該フィールドのフィールド長による重要度を計算しなくなります。Norm の効果により、「body より title フィールドの方の重要度を上げたい」というようなことを意識しなくても「いい感じ」にスコア計算されます。(次ページ) (重要度指定付き検索の例)q=title:東京駅^10 body:東京駅 |
autoGeneratePhraseQueries | true と指定すると、一続きの文字列が Analyzer によって複数単語に分割された場合に、フレーズクエリが生成されます。 |
Q. キーワード X を探しているときに、以下のどちらの文書を上位に表示して欲しいか?
title | body | |
---|---|---|
doc1 | ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ | ○ ○ ○ ○ ○ ○ ○ ○ X ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ X ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ |
doc2 | ○ ○ ○ ○ X ○ ○ ○ ○ ○ | ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ |
↓
Normにより長いフィールド(単語数が多いフィールド)ほどスコアを低減させる。
https://ja.wikipedia.org/wiki/Okapi_BM25
入力テキスト こんにちは、世界。
↓
JapaneseTokenizer (形態素解析器)
↓
オフセット | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
トークンテキスト | こ | ん | に | ち | は | 、 | 世 | 界 | 。 | |
ポジション | 1 | 2 | 3 | 4 |
text_2gフィールド型はautoGeneratePhraseQueries=trueが設定されています。デバッグ(KandaSearch の検索ページで「デバッグ」にチェックを入れます)機能を使って、「こんにちは」を同フィールドで検索したときに、以下のそれぞれでどのように変化するか、確認してみましょう。
検索キーワード
・コンピューター
・コンピューター
・コンピュータ
・computer
[要件]これらのいずれのキーワードでもヒット&ハイライトさせたい
検索結果(ハイライトスニペット表示)
新しい コンピューター を買いました!
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新 | し | い | コ | ン | ヒ | ゚ | ュ | ー | タ | ー | を | 買 | い | ま | し | た | ! |
MappingCharFilter ↓
0 | 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新 | し | い | コ | ン | ピ | ュ | ー | タ | ー | を | 買 | い | ま | し | た | ! |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新 | し | い | コ | ン | ピ | ュ | ー | タ | ー | を | 買 | い | ま | し | た | ! |
JapaneseTokenizer
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新 | し | い | コ | ン | ヒ | ゚ | ュ | ー | タ | ー | を | 買 | い | ま | し | た | ! |
MappingCharFilter ↓
0 | 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新 | し | い | コ | ン | ピ | ュ | ー | タ | ー | を | 買 | い | ま | し | た | ! |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新 | し | い | コ | ン | ピ | ュ | ー | タ | ー | を | 買 | い | ま | し | た | ! |
JapaneseTokenizer
Solr Admin UIのAnalysisでtext_jaフィールド型を使ってCharFilter〜Tokenizer間のオフセット補正の動作を確認します。入力文「ヴェランダ」(半角カナ)を解析してみましょう。
<charFilter class="solr.MappingCharFilterFactory" mapping="mapping-kana.txt"/>
<charFilter class="solr.MappingCharFilterFactory" mapping="mapping-kana2.txt"/>
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
ウ | ゙ | ェ | ラ | ン | タ | ゙ |
uniqueKey
文書のIDとなるフィールドを<uniqueKey />
で指定します。オプションの設定ですが、業務アプリケーションではほぼ必須と考えていいでしょう。ユニークキーの指定がないと、文書の更新やハイライトができません。SolrCloud にもユニークキーが必須です。
<uniqueKey>id</uniqueKey>
copyField
sourceで指定したフィールド値をdestで指定したフィールドにコピーします。
日本語では形態素解析器と文字N-gramの横断検索等で多用します。
<copyField source="name" dest="name_2g"/>
dynamicField
```<field />```で明示的に指定されていないフィールドは、インデクシング時や検索時に```<dynamicField />```が探索され、該当する```<dynamicField />```のフィールド設定が適用されます。
```xml
<dynamicField name="*_i" type="pint" indexed="true" stored="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
Solr 付属のサンプルデータは日本語を含まないので日本語の動作がよくわかりません。これ以降は日本語データを登録して日本語を検索できるか試してみます。