Azure AI 検索ソリューションを開発する（基礎編）

## はじめに
あなたが旅行会社で働いているとします。過去のすべての旅行についてpdfのドキュメントで保管しています。このpdfドキュメントのような内部データを参照して質問に答えてくれるチャットシステムを作りたいと考えました。その時に、活躍するのがAzure AI Searchです。以下ではAzure AI Searchの基礎部分をひとつづつ解説します。

## キャパシティの管理
キャパシティ（キャパオーバーとかのキャパです）の管理という意味です。この項目で重要な概念が「レプリカ」と「パーテーション」の二つです。

## レプリカ
レプリカとは、検索サービスのインスタンスのことです。あなたが図書館にいることをイメージしてください。お客さんが、「○○の本をください」といった注文に対して、本を渡すことが役割の図書員がいるとします。少ない注文なら一人で対処できますが、注文が増えると対処できなくなります。そのような場合に図書員と同じ仕事ができる人を増やして処理をします。これがレプリカです。

## パーテーション
パーテーションとは、仕分けです。すべての本が一つの本棚にあると探すのに苦労します。そこでジャンルごとや発刊日などで分けて保管すれば非常に簡単に見つけられます。これがパーテーションです。

## 検索ユニット
検索ユニットとは、「パーテーション×レプリカ」です。なので、レプリカ（図書員3人）とパーテーション（仕切り4つ）の場合は、検索ユニットが12になります。

## 検索コンポーネントの概要
AI 検索ソリューションは複数のコンポーネントから構成され、それぞれがデータの抽出、エンリッチ、インデックス作成、検索のプロセスにおいて重要な役割を果たします。

## データソース
ほとんどの検索ソリューションは、検索するデータが含まれる "データ ソース" から開始します。 Azure AI 検索では、以下を含む複数の種類のデータ ソースがサポートされています。

- Azure BLOB ストレージ コンテナー内の非構造化ファイル。
- Azure SQL Database 内のテーブル。
- Cosmos DB 内のドキュメント。
- Azure AI 検索はインデックス作成のために、これらのソースからデータをプルできます。

または、アプリケーションから、JSON データを直接インデックスにプッシュすることができ、既存のデータ ストアから取得する必要はありません。

## スキルセット

基本的な検索ソリューションでは、データ ソースから抽出されたデータのインデックスを作成することがあります。
抽出できる情報は、データ ソースによって異なります。 

たとえば、データベース内のデータのインデックスを作成する場合、データベース テーブル内のフィールドを抽出できます。

また、一連のドキュメントのインデックスを作成する場合、ファイル名、変更日、サイズ、作成者などのファイル メタデータをドキュメントのテキスト コンテンツと共に抽出できます。

データ ソースから直接抽出されたデータ値のインデックスを作成する基本的な検索ソリューションで十分役立つ場合もありますが、最近のアプリケーション ユーザーの期待により、データのよりリッチな分析情報に対するニーズが高まっています。

Azure AI 検索では、インデックス作成プロセスの一環として人工知能 (AI) "スキル" を適用して、インデックス フィールドにマップできる新しい情報でソース データをエンリッチできます。 
インデクサーによって使用されるスキルは、各ステップで特定の AI スキルによって取得された分析情報を使用してソース データを拡張するエンリッチメント パイプラインを定義する "スキルセット" にカプセル化されます。

AI スキルによって抽出できる情報の種類の例を次に示します。

- ドキュメントの記述言語。
- ドキュメントに記載されている主なテーマまたはトピックを特定するのに役立つ可能性のあるキー フレーズ。
- ドキュメントがどれほど肯定的または否定的であるかを定量化するセンチメント スコア。
- コンテンツに記載されている特定の場所、人、組織、またはランドマーク。
- AI によって生成された画像の説明、または光学式文字認識によって抽出された画像テキスト。
- 特定の要件を満たすために開発したカスタム スキル。

以下はスキルセットの一例です。どのように情報を抽出するのかを定義します。
```JSON:JSON
{
  "name": "my-skillset",
  "skills": [
    {
      "@odata.type": "#Microsoft.Skills.Text.EntityRecognitionSkill",
      "categories": ["Organization", "Person", "Location"],
      "defaultLanguageCode": "ja",
      "inputs": [{"name": "text", "source": "/document/content"}],
      "outputs": [{"name": "organizations", "targetName": "organizations"},
                  {"name": "persons", "targetName": "persons"},
                  {"name": "locations", "targetName": "locations"}]
    },
    {
      "@odata.type": "#Microsoft.Skills.Text.LanguageDetectionSkill",
      "inputs": [{"name": "text", "source": "/document"}],
      "outputs": [{"name": "languageCode", "targetName": "languageCode"}]
    },
    {
      "@odata.type": "#Microsoft.Skills.Text.KeyPhraseExtractionSkill",
      "inputs": [{"name": "text", "source": "/document/content"}],
      "outputs": [{"name": "keyPhrases", "targetName": "keyPhrases"}]
    }
  ]
}

```


## Indexer

"インデクサー" は、インデックス作成プロセス全体を駆動するエンジンです。 スキルセットのスキルを使用して抽出された出力と、元のデータ ソースから抽出されたデータおよびメタデータ値を取得し、それらをインデックスのフィールドにマップします。

インデクサーは、作成時に自動的に実行され、定期的に実行されるようにスケジュールすることも、必要に応じて実行してインデックスにドキュメントを追加することもできます。 新しいフィールドをインデックスに追加するときやスキルセットに新しいスキルを追加するときなど、場合によっては、インデクサーを再実行する前にインデックスをリセットしなければならないことがあります。

## Index
インデックスは、インデックス作成プロセスの検索可能な結果です。 JSON ドキュメントのコレクションで構成され、インデックス作成中に抽出された値が含まれるフィールドがあります。 クライアント アプリケーションから、インデックスに対してクエリを実行して、情報の取得、フィルター処理、および並べ替えを行うことができます。

各インデックス フィールドは、次の属性を使用して構成できます。

- key: インデックス レコードの一意のキーを定義するフィールド。
- searchable: フルテキスト検索を使用してクエリを実行できるフィールド。
- filterable: 指定された制約に一致するドキュメントのみを返すためにフィルター式に含めることができるフィールド。
- sortable: 結果の並べ替えに使用できるフィールド。
^ facetable: "ファセット" (既知のフィールド値の一覧に基づいて結果をフィルター処理するために使用されるユーザー インターフェイス要素) の値を決定するために使用できるフィールド。
- retrievable: 検索結果に含めることができるフィールド ("既定では、この属性が明示的に削除されていない限り、すべてのフィールドが取得可能です")。

```JSON:JSON
{
  "name": "my-index",
  "fields": [
    {"name": "id", "type": "Edm.String", "key": true, "retrievable": true},
    {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
    {"name": "content", "type": "Edm.String", "searchable": true, "retrievable": true},
    {"name": "createdDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "retrievable": true}
  ]
}

```

## つまり、、、
検索コンポーネントは、データソースからスキルセットの定義に基づいた抽出する情報をindexerが抽出して、マッピングしていく。そして、その結果がindexであるということです。

## フルテキスト検索
これは、文字通り、インデックス内のすべての文章を調べる検索方法です。

流れとしては、

1.クエリ解析：検索された言葉「ラーメン　銀座」のようなものをクエリといいます。検索できる形に整理する工程がクエリ解析です。

2.字句解析: クエリの言葉を基本形に変えたり、不要な言葉を取り除いたりして、検索しやすくします。例えば、runningをrunに変更したりします。

3.ドキュメント取得: 解析されたクエリをもとに、インデックスから一致するドキュメントを検索します。このステップでは、クエリに関連するすべてのドキュメントが特定されます。

4.スコアリング: 最後に、見つかったドキュメントがクエリにどれだけマッチしているかを評価し、その関連性に基づいてスコアを割り当てます。スコアが高いドキュメントほど、検索結果の上位に表示されます。

## 結果のフィルター処理
結果のフィルター処理とは、検索するクエリに条件をつけてフィルタリングすることを言います。本を検索する際に「著者：〇〇」のようにして絞り込む処理です。
simplesearch 式にフィルターを入れる処理とfull 構文の検索式にODataのフィルター処理を記載する処理があります。
```javascript:simplesearchにフィルター処理を行う
search=London+author='Reviewer'
queryType=Simple
```
```javascript:full構文にフィルター処理を行う
search=London
$filter=author eq 'Reviewer'
queryType=Full
```

## ファセット
ファセットもフィルター処理です。検索結果にカテゴリーを指定して絞りこみを行います。ユーザーが直感的に絞りこむことができる点が魅力です。ユーザーが「著者」の部分であらかじめ用意された選択肢の中から選択することで調べたいデータを得ることができるのです。
```
search=*
facet=author
```
```
search=*
$filter=author eq 'selected-facet-value-here'
```

## 検索結果を並べ替える
通常は、検索結果はクエリの関連性スコアの高い順に表示されます。しかし、検索結果を並べ替えることも可能で、$orderbyで並べ替えることができます。

```
search=*
$orderby=last_modified desc
```
## インデックスを拡張する


### 逐次検索
検索欄に入力されている状態で、すべての入力が完了する前でも、予測して検索結果を表示することができる機能です。オートコンプリートというのは自動で検索内容を補完することです。

### カスタム スコアリングと結果のブースト
カスタムスコアイングとは、通常のスコアリングではなく独自のスコアリングを設定する機能です。例えば、位置情報を活用して、「距離が近いもののスコアを高くする」というようなスコアリングの方法です。

### シノニム
シノニムとは、同じような名前を示す場合に利用します。
「イギリス」「英国」「UK」「GB*」はどれも同じような言葉です。これを関連付けるのがシノニムです。
```
SF, サイエンスフィクション
自伝, 伝記
写真, フォト
コンピュータ, パソコン, コンピューター
モバイル, 携帯, スマートフォン, スマホ
```