BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Azure Cognitive Search - 検索のインデックス更新の基礎 ( C#, SDK v11 )

Azure Cognitive Search でインデックスを更新する際 C# でサクッと更新できるのですが、それに関するメモです。

インデックスの更新は大きく2パターン

Pull model

データソースを設定しておくことで、Cognitive Search が (正確にはインデクサーが)データソースからデータをプルしてインデックスの追加・更新を行う方法です。定期的に実行する機能もついています。

f:id:beachside:20210219114507p:plain

インデックスを最初に作るときや、とりあえずさっと試してみたいときは、この方法でさっと作るのがよいです。

データソースは以下が対応してます。Blob に json のデータを置いてプルするとかめっちゃ簡単にできます。ほかのデータソースでも簡単。

  • Blob Storage (Data lake Storage Gen2 は preview)
  • Table Storage
  • Cosmos DB
  • SQL Database, SQL Managed Instance, Azure VM 上の SQL Server

Push model

データソースに変更があったタイミングでそのデータを Cognitive Search へ Push する方法です。
ほぼリアルタイムにインデックスを更新できるので、プロダクションの運用では、多くの場合この方法が有効だと思っています。

例えばデータソースが Cosmos DB だと Change Feed でインデックスを更新できるので相性が良いです。

注意点として1度に送信できるのは 1000個 or 16MB という制限がありますが、分割して送信すればよいだけなので問題になることはないと感じています。

SDK v11 の基礎知識

これからは基本的に古いバージョンは使わず v11 以降を使うと思うので、それを前提にいくつか基本的な情報を整理しておきます。

パッケージ名

v10は Microsoft.Azure.Search というパッケージでしたが、v11 では Azure.Search.Documents に変わりました。class 名もかなり変わっているので間違って v10 のドキュメントをみないよう注意しましょう。

Json の Serializer

v11 の Json の処理は、System.Text.Json に依存しています。
たまにやりそうな Attribute で Json のプロパティ名を定義している場合は多少の注意が必要になります。具体的には、Json.NET の [JsonProperty("...")] だと変換されません。System.Text.Json の `[JsonPropertyName("...")] を使う必要があります。そのため、Json.NET に依存してるライブラリと v11 SDK を併用してるとめんどくさってなります。
そんなときは class に attribute を付けて管理するのではなく、SearchClient のインスタンス化時に Serializer を設定して上がるパターンがベターです。実装方法は後述します。

実装してみる

Azure Portal から必要な情報を取得

今回は SDK の使い方のメモなので、Cognitive Search を作ってインデックスが作られてることを前提に進めます。今回 SDK を使う際に必要なのは3つ。

  • Cognitive Search の Uri
  • インデックス名
  • API キー

Cognitive Search の Uri とインデックスの名前は、Azure Portal でCognitive Search のリソースを開き、メニューの Overview (概要) を開くと Uri を確認できます。また、下の方の Index のタブをクリックすればインデックス名が確認できますので、これから更新したいインデックス名をメモしておきます。

f:id:beachside:20210221195604p:plain

API キーは、ポータルで Keys のメニュー開くと確認できます。admin keyがインデックスを操作するときに必要なキーです。ちなみに下の方にある query key は検索に使えるキーですので、今回はこちらではありません。

f:id:beachside:20210221195903p:plain

SearchClient のインスタンス化

Index の更新には SearchClient のインスタンスが必要になります。生成は以下コードのように何パターンかありますが、用途に応じて使えばよいです。

インデックスの更新

公式ドキュメントだと以下のリンクに書かれています。翻訳されたタイトルがインデックスを読み込む ってなってるので混乱しますが、データを Cognitive Search の index に読み込ませるコードです。

ただ、このドキュメントに書かれてるやり方はいまいちめんどくさいので、SearchClient には更新するのには、UploadDocumentsAsync メソッドでよいかなって感じです。コレクションをメソッドの引数にいれるだけなので、上記リンクのドキュメントのサンプルよりシンプルに書けます。

// インデックス更新に必要なドキュメントのコレクションを取ってくる適当なメソッド `GetSampleItems();` があるとして
var items = GetSampleItems();
// これだけで更新完了
var response = await searchClient.UploadDocumentsAsync(items);

これは Upload しか使わない場合ですが、MergeOrUpload のメソッドもあるので汎用的に使えます。

JSON Serializer のカスタマイズ

冒頭で JSON の Serializer の扱いには注意とふれましたが、このブログで書いておきたかったのがカスタマイズ方法です。

前提として デフォルトの v11 の SDK は

  • System.Text.Json に依存しています。
  • C# のコードのプロパティのままシリアライズするため、(命名規約に沿ってコーディングしていれば) Pascal case でシリアライズされます。

Json 扱ってるなら camel case やろってのが一般的だと思ってますので、camel case にシリアライズするよう設定します。そしてSystem.Text.Json と Newtonsoft の 2つのパターンを書いていきます。

System.Text.Json の Serializer で camel case に設定する

なんらかの理由で Newtonsoft させたいことがない場合は、デフォルトの System.Text.Json で camel case を設定すればよいです。

追加する NuGet は以下です。

コードはこんな感じ。SearchClient のインスタンスを生成する際、searchClientOptions を用意してあげるだけですね。

Newtonsoft の Serializer で camel case に設定する

追加する NuGet は以下です。

コードはこんな感じ。SearchClient のインスタンスを生成する際、searchClientOptions を用意してあげるってのはお決まりのぱたーんだけど、中にセットしてる Serializer が異なる。

終わりに

雑な Factory 作るとしたらこんな感じですかね。そもそも同一プロジェクトで3つのパターンを利用する必要性はないと思いますがw

上のコードで使ってる options も雑にこんな感じで。ASP.NET Core とかだと DI で入れてあげればよいですね。