Elastic App Search のエンジンを使用すると、ドキュメントのインデックスを作成し、すぐに調整可能な検索機能を提供できます。デフォルトでは、エンジンは定義済みの言語リストをサポートしています。お使いの言語がそのリストにない場合は、このブログで他の言語のサポートを追加する方法を説明しています。これを行うには、その言語用に設定されたアナライザーを使用してアプリ検索エンジンを作成します。
詳細に入る前に、Elasticsearch アナライザーとは何かを定義しましょう。
Elasticsearch アナライザーは、文字フィルター、トークナイザー、トークン フィルターの 3 つの下位レベルのビルディング ブロックのパッケージです。アナライザーは、組み込みまたはカスタムにすることができます。ビルトイン アナライザー ビルディング ブロックをさまざまな言語やテキスト タイプに適したアナライザーに事前にパッケージ化します。Analyzer の詳細については、記事「Elasticsearch: アナライザー」を参照してください。
各フィールドのアナライザーは、次の目的で使用されます。
- 索引文書。各ドキュメント フィールドは、対応するアナライザーで処理され、簡単に検索できるようにトークンに分割されます。
- ドキュメントを検索します。検索クエリが分析され、分析されたインデックス フィールドと正しく一致することが保証されます。
Elasticsearch インデックスに基づくエンジンを使用すると、既存の Elasticsearch インデックスから App Search エンジンを作成できます。独自のアナライザーとマッピングを使用して Elasticsearch インデックスを作成し、そのインデックスを App Search で使用します。
このプロセスには次の 4 つのステップがあります。
- Elasticsearch インデックスとインデックス ドキュメントを作成する
- このインデックスに言語アナライザーを追加します
- アナライザーを使用するようにインデックス マッピングを更新する
- ドキュメントの再インデックス
1) Elasticsearch インデックスとインデックス ドキュメントを作成する
まず、どの言語にも最適化されていないインデックスを使用してみましょう。これが事前定義されたマッピングのない新しいインデックスであると仮定すると、ドキュメントが初めてインデックス付けされるときに作成されます。
Elasticsearch では、マッピングとは、ドキュメントとドキュメントに含まれるフィールドの保存方法とインデックス作成方法を定義するプロセスです。各ドキュメントはフィールドのコレクションであり、それぞれに独自のデータ型があります。データをマッピングするときは、ドキュメントに関連するフィールドのリストを含むマッピング定義を作成します。
例に戻ります。索引は本と呼ばれ、タイトルはルーマニア語です。ルーマニア語を選択したのは、それが私の言語であり、App Search がデフォルトでサポートする言語のリストに含まれていないためです。
POST books/_doc/1
{
"title": "Un veac de singurătate",
"author": "Gabriel García Márquez"
}
POST books/_doc/2
{
"title": "Dragoste în vremea holerei",
"author": "Gabriel García Márquez"
}
POST books/_doc/3
{
"title": "Obosit de viaţă, obosit de moarte",
"author": "Mo Yan"
}
POST books/_doc/4
{
"title": "Maestrul și Margareta",
"author": "Mihail Bulgakov"
}
2) 書籍索引に言語アナライザーを追加する
books インデックス マッピングを調べると、ルーマニア語用に最適化されていないことがわかります。設定ブロックに分析フィールドがなく、テキスト フィールドがカスタム アナライザーを使用していないことがわかります。
GET books
{
"books": {
"aliases": {},
"mappings": {
"properties": {
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "books",
"creation_date": "1679310576178",
"number_of_replicas": "1",
"uuid": "0KuiDk8iSZ-YHVQGg3B0iw",
"version": {
"created": "8080099"
}
}
}
}
}
ブック インデックスを使用して App Search エンジンを作成しようとすると、2 つの問題が発生します。第一に、検索結果がルーマニア語用に最適化されなくなり、第二に、微調整などの機能が無効になります。
さまざまな種類の Elastic App Search エンジンに関する簡単なメモ:
- デフォルトのオプションは、非表示の Elasticsearch インデックスを自動的に作成および管理する App Search Managed Engine です。このオプションでは、App Search Documentation APIを使用してエンジンでデータを抽出する必要があります。
- 別のオプションとして、App Search は既存の Elasticsearch インデックスを使用してエンジンを作成します。この場合、App Search はそのインデックスをそのまま使用します。ここでは、Elasticsearch インデックス ドキュメント APIを使用して、基になるインデックスからデータを直接抽出できます。
[関連記事: Elasticsearch Search API: A New Way to Locate App Search Documents ]
既存の Elasticsearch インデックスからエンジンを作成するときに、マッピングが App Search 規則に従っていない場合、エンジンに対してすべての機能が有効になるわけではありません。App Search によって完全に管理されているエンジンを見て、App Search のマッピング規則を詳しく見てみましょう。このエンジンには、タイトルと作成者の 2 つのフィールドがあり、英語を使用します。
GET .ent-search-engine-documents-app-search-books/_mapping/field/title
{
".ent-search-engine-documents-app-search-books": {
"mappings": {
"title": {
"full_name": "title",
"mapping": {
"title": {
"type": "text",
"fields": {
"date": {
"type": "date",
"format": "strict_date_time||strict_date",
"ignore_malformed": true
},
"delimiter": {
"type": "text",
"index_options": "freqs",
"analyzer": "iq_text_delimiter"
},
"enum": {
"type": "keyword",
"ignore_above": 2048
},
"float": {
"type": "double",
"ignore_malformed": true
},
"joined": {
"type": "text",
"index_options": "freqs",
"analyzer": "i_text_bigram",
"search_analyzer": "q_text_bigram"
},
"location": {
"type": "geo_point",
"ignore_malformed": true,
"ignore_z_value": false
},
"prefix": {
"type": "text",
"index_options": "docs",
"analyzer": "i_prefix",
"search_analyzer": "q_prefix"
},
"stem": {
"type": "text",
"analyzer": "iq_text_stem"
}
},
"index_options": "freqs",
"analyzer": "iq_text_base"
}
}
}
}
}
}
タイトル フィールドにいくつかのサブフィールドがあることがわかります。date、float、および location サブフィールドはテキスト フィールドではありません。
ここでは、App Search で必要なテキスト フィールドを設定する方法に関心があります。フィールドはあと少し!このドキュメントページでは、App Search で使用されるテキスト フィールドについて説明します。App Search ホスティング エンジンに属する非表示のインデックスに対して App Search が設定したアナライザーを見てみましょう。
GET .ent-search-engine-documents-app-search-books/_settings/index.analysis*
{
".ent-search-engine-documents-app-search-books": {
"settings": {
"index": {
"analysis": {
"filter": {
"front_ngram": {
"type": "edge_ngram",
"min_gram": "1",
"max_gram": "12"
},
"bigram_joiner": {
"max_shingle_size": "2",
"token_separator": "",
"output_unigrams": "false",
"type": "shingle"
},
"bigram_max_size": {
"type": "length",
"max": "16",
"min": "0"
},
"en-stem-filter": {
"name": "light_english",
"type": "stemmer"
},
"bigram_joiner_unigrams": {
"max_shingle_size": "2",
"token_separator": "",
"output_unigrams": "true",
"type": "shingle"
},
"delimiter": {
"split_on_numerics": "true",
"generate_word_parts": "true",
"preserve_original": "false",
"catenate_words": "true",
"generate_number_parts": "true",
"catenate_all": "true",
"split_on_case_change": "true",
"type": "word_delimiter_graph",
"catenate_numbers": "true",
"stem_english_possessive": "true"
},
"en-stop-words-filter": {
"type": "stop",
"stopwords": "_english_"
}
},
"analyzer": {
"i_prefix": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"front_ngram"
],
"tokenizer": "standard"
},
"iq_text_delimiter": {
"filter": [
"delimiter",
"cjk_width",
"lowercase",
"asciifolding",
"en-stop-words-filter",
"en-stem-filter"
],
"tokenizer": "whitespace"
},
"q_prefix": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding"
],
"tokenizer": "standard"
},
"iq_text_base": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"en-stop-words-filter"
],
"tokenizer": "standard"
},
"iq_text_stem": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"en-stop-words-filter",
"en-stem-filter"
],
"tokenizer": "standard"
},
"i_text_bigram": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"en-stem-filter",
"bigram_joiner",
"bigram_max_size"
],
"tokenizer": "standard"
},
"q_text_bigram": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"en-stem-filter",
"bigram_joiner_unigrams",
"bigram_max_size"
],
"tokenizer": "standard"
}
}
}
}
}
}
}
App Search で使用できるさまざまな言語 (ノルウェー語、フィンランド語、アラビア語など) のインデックスを作成する場合は、同様のアナライザーが必要になります。この例では、ステミング フィルターとストップワード フィルターがルーマニア語バージョンを使用していることを確認する必要があります。
元の本のインデックスに戻り、正しいアナライザーを追加しましょう。
ここで簡単に警告します。既存のインデックスの場合、プロファイラーは、インデックスがダウンしている間のみ変更できる Elasticsearch 設定です。このアプローチでは、既存のインデックスから開始するため、インデックスを閉じ、アナライザーを追加してから、インデックスを再度開く必要があります。
注: 別の方法として、正しいマッピングを使用してインデックスを最初から再作成し、すべてのドキュメントにインデックスを付けることもできます。それがユースケースにより適している場合は、インデックス作成のオンとオフ、および再インデックス作成について説明しているこのガイドのセクションをスキップしてください。
POST books/_close を実行してインデックスを閉じることができます。その後、アナライザーを追加します。
PUT books/_settings
{
"analysis": {
"filter": {
"front_ngram": {
"type": "edge_ngram",
"min_gram": "1",
"max_gram": "12"
},
"bigram_joiner": {
"max_shingle_size": "2",
"token_separator": "",
"output_unigrams": "false",
"type": "shingle"
},
"bigram_max_size": {
"type": "length",
"max": "16",
"min": "0"
},
"ro-stem-filter": {
"name": "romanian",
"type": "stemmer"
},
"bigram_joiner_unigrams": {
"max_shingle_size": "2",
"token_separator": "",
"output_unigrams": "true",
"type": "shingle"
},
"delimiter": {
"split_on_numerics": "true",
"generate_word_parts": "true",
"preserve_original": "false",
"catenate_words": "true",
"generate_number_parts": "true",
"catenate_all": "true",
"split_on_case_change": "true",
"type": "word_delimiter_graph",
"catenate_numbers": "true"
},
"ro-stop-words-filter": {
"type": "stop",
"stopwords": "_romanian_"
}
},
"analyzer": {
"i_prefix": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"front_ngram"
],
"tokenizer": "standard"
},
"iq_text_delimiter": {
"filter": [
"delimiter",
"cjk_width",
"lowercase",
"asciifolding",
"ro-stop-words-filter",
"ro-stem-filter"
],
"tokenizer": "whitespace"
},
"q_prefix": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding"
],
"tokenizer": "standard"
},
"iq_text_base": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"ro-stop-words-filter"
],
"tokenizer": "standard"
},
"iq_text_stem": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"ro-stop-words-filter",
"ro-stem-filter"
],
"tokenizer": "standard"
},
"i_text_bigram": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"ro-stem-filter",
"bigram_joiner",
"bigram_max_size"
],
"tokenizer": "standard"
},
"q_text_bigram": {
"filter": [
"cjk_width",
"lowercase",
"asciifolding",
"ro-stem-filter",
"bigram_joiner_unigrams",
"bigram_max_size"
],
"tokenizer": "standard"
}
}
}
}
ご覧のとおり、ro-stem-filter を追加してルーマニア語の語幹を抽出します。これにより、ルーマニア語固有の単語の変形の検索関連性が向上します。ルーマニア語のストップ ワードが検索目的で考慮されないようにするために、ルーマニア語のストップ ワード フィルター (ro-stop-words-filter) が含まれています。
POST books/_open を実行して、インデックスを再度開きます。
3) インデックス マップを更新してアナライザーを使用する
分析のセットアップが完了したら、インデックス マッピングを変更できます。App Search は動的テンプレートを使用して、新しいフィールドに正しいサブフィールドとアナライザーが含まれるようにします。この例では、既存の title フィールドと author フィールドにサブフィールドを追加するだけです。
PUT books/_mapping
{
"properties": {
"author": {
"type": "text",
"fields": {
"delimiter": {
"type": "text",
"index_options": "freqs",
"analyzer": "iq_text_delimiter"
},
"enum": {
"type": "keyword",
"ignore_above": 2048
},
"joined": {
"type": "text",
"index_options": "freqs",
"analyzer": "i_text_bigram",
"search_analyzer": "q_text_bigram"
},
"prefix": {
"type": "text",
"index_options": "docs",
"analyzer": "i_prefix",
"search_analyzer": "q_prefix"
},
"stem": {
"type": "text",
"analyzer": "iq_text_stem"
}
}
},
"title": {
"type": "text",
"fields": {
"delimiter": {
"type": "text",
"index_options": "freqs",
"analyzer": "iq_text_delimiter"
},
"enum": {
"type": "keyword",
"ignore_above": 2048
},
"joined": {
"type": "text",
"index_options": "freqs",
"analyzer": "i_text_bigram",
"search_analyzer": "q_text_bigram"
},
"prefix": {
"type": "text",
"index_options": "docs",
"analyzer": "i_prefix",
"search_analyzer": "q_prefix"
},
"stem": {
"type": "text",
"analyzer": "iq_text_stem"
}
}
}
}
}
4) ドキュメントの再インデックス
ブック インデックスがアプリ検索でほぼ利用できるようになりました。
マッピングを変更する前に、インデックスを作成するドキュメントに正しいサブフィールドがすべて含まれていることを確認する必要があります。これを行うには、 update_by_queryを使用してインプレースで再構築インデックスを実行できます。
POST books/_update_by_query?refresh
{
"query": {
"match_all": {
}
}
}
match_all クエリを使用しているため、既存のすべてのドキュメントが更新されます。
update by queryリクエストでは 、ドキュメントの更新方法を定義するスクリプト パラメータを含めることもできます。
ドキュメントを変更するわけではありませんが、既存のドキュメントをそのまま再インデックスして、テキスト フィールドの author と title に正しいサブフィールドが含まれるようにすることに注意してください。したがって、クエリ リクエストの更新にスクリプトを含める必要はありません。
Elasticsearch エンジンを使用して App Search で使用できる、言語に最適化されたインデックスが用意されました。次のスクリーンショットで実際の利点を確認できます。
本のタイトル「百年の孤独」 を参考にします。ルーマニア語に翻訳されたタイトルは Un veac de singurătate です。ルーマニア語で「世紀」を意味する veac という言葉に注意してください。veac、veacuriの複数形で検索します。このデータ レコードは、これから説明する両方の例で抽出しました。
{
"title": "Un veac de singurătate",
"author": "Gabriel García Márquez"
}
インデックスが言語用に最適化されていない場合、ルーマニア語のタイトル Un veac de singurătate は標準アナライザーを使用してインデックス化されます。これはほとんどの言語で機能しますが、関連するドキュメントと常に一致するとは限りません。この検索入力はデータ レコード内のプレーン テキストと一致しないため、veacuri を検索しても結果は返されません。
ただし、言語に最適化されたインデックス作成を使用して veacuri を検索すると、Elastic App Search はそれをルーマニア語の veac と照合し、探していたデータを返します。相対調整ビューには微調整フィールドも用意されています。この画像で強調表示されているすべてのビットを確認してください。
そこで、私の言語である Elastic Enterprise Search にルーマニア語のサポートを追加しました! このガイドで使用されている手順を複製して、Elasticsearch でサポートされている他の言語用に最適化されたインデックスを作成できます。Elasticsearch でサポートされている言語アナライザーの完全なリストについては、このドキュメントページを参照してください。
Elasticsearch のアナライザーは魅力的なトピックです。詳細については、次のリソースを参照してください。
- Elasticsearch Text Analytics の概要ドキュメント ページ
- Elasticsearch組み込みアナライザーのリファレンスドキュメント ページ (サポートされている言語アナライザーのリストについては、このサブページを参照してください。)
- Elastic Enterprise SearchとElastic Cloudのトライアルについて詳しく知る