部分的な用語検索と特殊文字を含むパターン (ハイフン、ワイルドカード、正規表現、パターン)
"部分的な用語検索" とは、用語全体ではなく、用語の先頭、中間、または末尾のみを含む用語のフラグメントで構成されたクエリ (プレフィックス、インフィックス、またはサフィックス クエリとも呼ばれる) を指します。 部分的な用語検索ではフラグメントを組み合わせて使用することもできます。多くの場合、ハイフン、ダッシュ、スラッシュなどの特殊文字をクエリ文字列の一部として使います。 一般的なユースケースには、電話番号、URL、コード、またはハイフンでつながれた複合語の一部が含まれます。
検索するテキスト フラグメントを表しているトークンがインデックスに含まれていない場合、部分的な語句と特殊文字が問題になる可能性があります。 キーワードのインデックス作成の字句解析フェーズ (既定の標準アナライザーを想定) では、特殊文字は破棄され、複合語は分割され、空白は削除されます。 字句解析中に変更されたテキスト フラグメントを検索する場合、クエリは、一致するものが見つからないため、失敗します。 次の例を考えてみましょう。+1 (425) 703-6214
("1"
、"425"
、"703"
、"6214"
としてトークン化) のような電話番号は、実際にはインデックスに存在しないため、"3-62"
クエリに表示されません。
解決策として、インデックスの作成中に、空白や特殊文字などを必要に応じて含む完全な文字列が保持されているアナライザーを呼び出します。これにより、クエリ文字列にスペースと文字を含めることができます。 トークン化されていない文字列全体を使用すると、"starts with" または "ends with" クエリのパターン マッチングが有効になります。指定するパターンは、字句解析によって変換されない用語に対して評価することができます。
分析済みと分析されていないコンテンツを要求する検索シナリオをサポートする必要がある場合は、シナリオごとに 1 つずつ、インデックスに 2 つのフィールドを作成することを検討してください。 1 つのフィールドで語彙解析が行われます。 2 番目のフィールドには、パターン マッチングのために文字列全体のトークンを出力するコンテンツ保持アナライザーを使用して、そのままの文字列が保存されます。
部分的な用語検索について
Azure AI 検索は、インデックス内のトークン化された用語全体をスキャンし、ワイルドカード プレースホルダー演算子 (*
と ?
) を含めたり、クエリを正規表現として書式設定したりしない限り、部分的な用語で一致するものが見つかりません。
部分的な用語は、次の方法を使用して指定します。
正規表現クエリは、Apache Lucene で有効な任意の正規表現とすることができます。
プレフィックス一致を使用したワイルドカード演算子は、一般的に認識されたパターンであり、用語の先頭の後にサフィックス演算子
*
または?
が続きます ("Cap'n Jack's Waterfront Inn" または "Highline Capital" に一致するsearch=cap*
など)。 プレフィックス一致は、シンプルで完全な Lucene クエリ構文でサポートされています。挿入およびサフィックス一致を使用したワイルドカードでは、
*
および?
の演算子が用語の中または先頭に配置され、正規表現の構文 (式はスラッシュで囲む) が必要になります。 たとえば、クエリ文字列 (search=/.*numeric.*/
) の場合、サフィックスおよび挿入一致として、"alphanumeric" および "alphanumerical"の結果が返されます。
正規表現、ワイルドカード、あいまい検索の場合は、クエリ時にアナライザーは使用されません。 演算子と区切り記号の存在を基にパーサーによって検出されるこれらのクエリ フォームの場合、クエリ文字列は字句解析なしでエンジンに渡されます。 これらのクエリ フォームでは、フィールドに指定されたアナライザーは無視されます。
Note
URL フラグメント内のスラッシュなどの文字が部分的なクエリ文字列に含まれている場合、エスケープ文字の追加が必要になることがあります。 JSON では、スラッシュ /
はバックスラッシュ \
によってエスケープされます。 このため、search=/.*microsoft.com\/azure\/.*/
は URL フラグメント「microsoft.com/azure/」の構文です。
部分的検索またはパターン検索の問題の解決
フラグメントまたはパターンまたは特殊文字を検索する必要がある場合は、単純なトークン化ルールで動作するカスタム アナライザーを使用して、既定のアナライザーをオーバーライドできます。これにより、文字列全体がインデックス内に保持されます。
このアプローチは次のようなものです。
- 文字列をそのまま保存するための 2 番目のフィールドを定義する (クエリ時に分析されたテキストと分析されていないテキストが必要であると想定)
- 適切なレベルの細分性でトークンを生成するさまざまなアナライザーを評価してその中から選択する
- フィールドにアナライザーを割り当てる
- インデックスを構築してテストする
1 - 専用フィールドを作成する
アナライザーによって、インデックス内で用語をトークン化する方法が決定されます。 アナライザーはフィールドごとに割り当てられるので、インデックス内にフィールドを作成し、さまざまなシナリオに合わせて最適化できます。 たとえば、最初のフルテキスト検索をサポートするために "featureCode" と "featureCodeRegex" を定義し、2 番目に高度なパターン マッチングを適用することができます。 各フィールドに割り当てられたアナライザーによって、各フィールドの内容をインデックス内でトークン化する方法が決められます。
{
"name": "featureCode",
"type": "Edm.String",
"retrievable": true,
"searchable": true,
"analyzer": null
},
{
"name": "featureCodeRegex",
"type": "Edm.String",
"retrievable": true,
"searchable": true,
"analyzer": "my_custom_analyzer"
},
2 - アナライザーを設定する
用語全体のトークンを生成するアナライザーを選択する場合は、次のアナライザーが一般的な選択肢となります。
Analyzer | 動作 |
---|---|
言語アナライザー | 複合語または文字列のハイフン、母音の変化、および動詞の形を保持します。 クエリ パターンにダッシュが含まれている場合は、言語アナライザーを使用するだけで十分な場合があります。 |
keyword | フィールド全体の内容は、1 つの用語としてトークン化されます。 |
whitespace | 空白文字のみを区切ります。 ダッシュまたはその他の文字を含む用語は、1 つのトークンとして扱われます。 |
カスタム アナライザー | (推薦) カスタム アナライザーを作成すると、トークナイザーとトークン フィルターの両方を指定できます。 前述のアナライザーはそのまま使用する必要があります。 カスタム アナライザーでは、使用するトークナイザーとトークン フィルターを選択できます。 推奨される組み合わせは、小文字のトークン フィルターとキーワード トークナイザーです。 組み込みキーワード アナライザー単体では大文字のテキストを小文字に変換できないため、クエリが失敗する可能性があります。 カスタム アナライザーには、小文字のトークン フィルターを追加するためのメカニズムが用意されています。 |
REST クライアントを使用して、テスト アナライザーの REST 呼び出しを追加して、トークン化された出力を検査できます。
インデックスが検索サービスに存在する必要がありますが、それを空にすることはできます。 既存のインデックスと、ダッシュまたは用語の一部を含むフィールドが指定されている場合は、特定の用語に対してさまざまなアナライザーを試して、どのトークンが生成されるかを確認できます。
まず、標準アナライザーを使用して、用語が既定でトークン化されているかどうかを確認します。
{ "text": "SVP10-NOR-00", "analyzer": "standard" }
応答を評価して、インデックス内でテキストがどのようにトークン化されるかを確認します。 各用語がどのように小文字に変換され、ハイフンが削除され、部分文字列が個別のトークンに分割されているかに注目してください。 これらのトークンに一致するクエリによってのみ、このドキュメントが結果として返されます。 "10-NOR" を含むクエリは失敗します。
{ "tokens": [ { "token": "svp10", "startOffset": 0, "endOffset": 5, "position": 0 }, { "token": "nor", "startOffset": 6, "endOffset": 9, "position": 1 }, { "token": "00", "startOffset": 10, "endOffset": 12, "position": 2 } ] }
次に、
whitespace
またはkeyword
アナライザーを使用するように要求を変更します。{ "text": "SVP10-NOR-00", "analyzer": "keyword" }
今回、応答は 1 つのトークンで構成され、文字列の一部としてダッシュが保持されます。 パターンまたは用語の一部 ("10-NOR" など) を検索する必要がある場合、これでクエリ エンジンが一致を見つけるための基礎となります。
{ "tokens": [ { "token": "SVP10-NOR-00", "startOffset": 0, "endOffset": 12, "position": 0 } ] }
重要
多くの場合、クエリ ツリーの構築時に、クエリ パーサーにより検索式内の用語が小文字に変換されることに注意してください。 インデックス作成時にテキスト入力が小文字に変換されないアナライザーを使用していて、期待した結果が得られない場合は、これが原因である可能性があります。 この問題を解決するため、以下の「カスタム アナライザーを使用する」セクションで説明されているように、小文字のトークン フィルターを追加します。
3 - アナライザーを構成する
アナライザーを評価する場合も、特定の構成を使用する場合も、アナライザーをフィールド定義に指定する必要があります。また、組み込みのアナライザーを使用しない場合は、アナライザー自体を構成することもできます。 アナライザーをスワップする場合は、通常、インデックスを再構築 (削除、再作成、および再読み込み) する必要があります。
組み込みアナライザーを使用する
組み込みアナライザーは、フィールド定義の analyzer
プロパティに名前を使用して指定できます。インデックスに追加の構成は必要ありません。 次の例では、フィールドに whitespace
アナライザーを設定する方法を示します。
他のシナリオと、他の組み込みアナライザーの詳細については、組み込みアナライザーに関するページを参照してください。
{
"name": "phoneNumber",
"type": "Edm.String",
"key": false,
"retrievable": true,
"searchable": true,
"analyzer": "whitespace"
}
カスタム アナライザーを使用する
カスタム アナライザーを使用している場合は、ユーザー定義のトークナイザー、トークン フィルターに可能な構成設定を組み合わせてインデックスで定義します。 次に、組み込みアナライザーの場合と同様に、フィールド定義で参照します。
用語全体のトークン化が目的である場合は、キーワード トークナイザーで構成されるカスタム アナライザーと小文字のトークン フィルターを選択することをお勧めします。
- キーワード トークナイザーは、フィールドの内容全体に対して 1 つのトークンを作成します。
- lowercase トークン フィルターは、大文字を小文字に変換します。 通常、クエリ パーサーでは、大文字のテキスト入力が小文字になります。 小文字への変換により、トークン化された用語を使用して入力を均質化します。
次の例は、キーワード トークナイザーと lowercase トークン フィルターを提供するカスタム アナライザーを示しています。
{
"fields": [
{
"name": "accountNumber",
"analyzer":"myCustomAnalyzer",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"retrievable": true,
"sortable": false,
"facetable": false
}
],
"analyzers": [
{
"@odata.type":"#Microsoft.Azure.Search.CustomAnalyzer",
"name":"myCustomAnalyzer",
"charFilters":[],
"tokenizer":"keyword_v2",
"tokenFilters":["lowercase"]
}
],
"tokenizers":[],
"charFilters": [],
"tokenFilters": []
}
Note
keyword_v2
トークナイザーおよび lowercase
トークン フィルターはシステムで認識され、既定の構成を使用しています。そのため、最初に定義しなくても名前で参照できます。
4 - ビルドおよびテスト
シナリオをサポートするアナライザーとフィールド定義を使ってインデックスを定義したら、部分的な文字列クエリをテストできるように、代表的な文字列を含むドキュメントを読み込みます。
REST クライアントを使用して、この記事で説明する部分的な用語と特殊文字に対してクエリを実行します。
前のセクションではロジックについて説明しました。 このセクションでは、ソリューションをテストする際に呼び出す必要がある各 API について説明します。
[インデックスの削除] では、同じ名前の既存のインデックスが削除されるため、再び作成できるようになります。
[インデックスの作成] では、アナライザーの定義やアナライザーの仕様を含むフィールドなど、検索サービスでインデックス構造が作成されます。
[ドキュメントの読み込み] では、インデックスと同じ構造のドキュメントおよび検索可能なコンテンツをインポートします。 この手順の後、インデックスはクエリやテストを実行できるようになります。
テスト アナライザーについては「アナライザーを設定する」で紹介しました。 さまざまなアナライザーを使用して、インデックスの一部の文字列をテストし、用語のトークン化方法を理解します。
[ドキュメントの検索] では、ワイルドカードと正規表現について単純な構文または完全な Lucene 構文のいずれかを使用してクエリ要求を作成する方法を説明します。
"+1 (425) 703-6214" に対して "3-6214" のクエリを実行して一致を検索するなど、部分的な用語クエリの場合は、単純な構文 (
search=3-6214&queryType=simple
) を使用できます。"alphanumeric" に対して "num" または "numeric" のクエリを実行して一致を検索するなど、インフィックスおよびサフィックス クエリの場合は、完全な Lucene 構文と正規表現 (
search=/.*num.*/&queryType=full
) を使用します。
プレフィックスおよびサフィックス クエリの最適化
既定のアナライザーを使用してプレフィックスとサフィックスを一致させるには、追加のクエリ機能が必要です。 プレフィックスにはワイルドカード検索が必要であり、サフィックスには正規表現検索が必要です。 これらの両方の機能により、クエリのパフォーマンスが低下する可能性があります。
次の例では、プレフィックスまたはサフィックスの一致を高速化するために EdgeNGramTokenFilter
を追加します。 トークンは、文字を含む 2 から 25 文字の組み合わせで生成されます。 次に示すのは、2 文字から 7 文字まで増えるトークンの例です: MS、MSF、MSFT、MSFT/、MSFT/S、MSFT/SQ、MSFT/SQL。 EdgeNGramTokenFilter
には、文字列の文字の組み合わせのどちら側から生成するかを決定する side
パラメーターが必要です。 プレフィックス クエリには front
を、サフィックス クエリには back
を使用します。
追加のトークン化によってインデックスは大きくなります。 より大きなインデックスを収めるのに十分な容量がある場合、応答時間が短くなるこのアプローチが最善の解決策になる可能性があります。
{
"fields": [
{
"name": "accountNumber_prefix",
"indexAnalyzer": "ngram_front_analyzer",
"searchAnalyzer": "keyword",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"retrievable": true,
"sortable": false,
"facetable": false
},
{
"name": "accountNumber_suffix",
"indexAnalyzer": "ngram_back_analyzer",
"searchAnalyzer": "keyword",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"retrievable": true,
"sortable": false,
"facetable": false
}
],
"analyzers": [
{
"@odata.type":"#Microsoft.Azure.Search.CustomAnalyzer",
"name":"ngram_front_analyzer",
"charFilters":[],
"tokenizer":"keyword_v2",
"tokenFilters":["lowercase", "front_edgeNGram"]
},
{
"@odata.type":"#Microsoft.Azure.Search.CustomAnalyzer",
"name":"ngram_back_analyzer",
"charFilters":[],
"tokenizer":"keyword_v2",
"tokenFilters":["lowercase", "back_edgeNGram"]
}
],
"tokenizers":[],
"charFilters": [],
"tokenFilters": [
{
"@odata.type":"#Microsoft.Azure.Search.EdgeNGramTokenFilterV2",
"name":"front_edgeNGram",
"minGram": 2,
"maxGram": 25,
"side": "front"
},
{
"@odata.type":"#Microsoft.Azure.Search.EdgeNGramTokenFilterV2",
"name":"back_edgeNGram",
"minGram": 2,
"maxGram": 25,
"side": "back"
}
]
}
123
で始まるアカウント番号を検索する場合は、次のクエリを使用できます。
{
"search": "123",
"searchFields": "accountNumber_prefix"
}
456
で終わるアカウント番号を検索する場合は、次のクエリを使用できます。
{
"search": "456",
"searchFields": "accountNumber_suffix"
}
次のステップ
この記事では、アナライザーがクエリの問題に寄与し、クエリの問題を解決する方法について説明します。 次のステップとして、アナライザーによるインデックス作成とクエリ処理への影響について詳しく見ていきます。