C# .NET アプリケーションで Microsoft.Azure.Search を使用する方法

この記事では、C#、および Azure SDK for .NET の従来のクライアント ライブラリ Microsoft.Azure.Search (バージョン 10) を使用して、検索オブジェクトを作成および管理する方法について説明します。

バージョン 10 は、最新バージョンの Microsoft.Azure.Search パッケージです。 今後、新機能は、Azure SDK チームによって、Azure.Search.Documents でロールアウトされます。

Note

既存または開発中のプロジェクトがある場合は、引き続きバージョン 10 を使用してください。 新しいプロジェクトの場合、または新しい機能を使用する場合は、新しいライブラリに移行する必要があります。

バージョン 10 について

この SDK は、HTTP や JSON に関する詳しい知識がなくても、インデックスやデータ ソース、インデクサー、シノニム マップの管理、ドキュメントのアップロードと管理、クエリの実行を行うことを可能にするいくつかのクライアント ライブラリから構成されています。 これらのクライアント ライブラリはすべて、NuGet パッケージとして配布されます。

メインの NuGet パッケージは Microsoft.Azure.Search です。このパッケージは、依存関係がある他のすべてのパッケージを含むメタパッケージです。 初めて取り組む場合、あるいはアプリケーションに Azure Cognitive Search の全機能が必要と分かっている場合は、このパッケージを使用します。

SDK のその他の NuGet パッケージとしては以下があります。

  • Microsoft.Azure.Search.Data:Azure Cognitive Search を使用して .NET アプリケーションを開発していて、インデックス内のドキュメントのクエリまたは更新のみを行う必要がある場合は、このパッケージを使用します。 インデックス、シノニム マップ、またはサービス レベルのその他のリソースの作成や更新も行う必要がある場合は、代わりに Microsoft.Azure.Search パッケージを使用します。
  • Microsoft.Azure.Search.Service:.NET で、Azure Cognitive Search インデックス、シノニム マップ、インデクサー、データ ソース、またはサービスレベルのその他のリソースを管理するための自動化を開発する場合は、このパッケージを使用します。 インデックス内のドキュメントのクエリまたは更新のみを行う場合は、代わりに Microsoft.Azure.Search.Data パッケージを使用します。 Azure Cognitive Search のすべての機能が必要な場合は、代わりに Microsoft.Azure.Search パッケージを使用します。
  • Microsoft.Azure.Search.Common:Azure Cognitive Search .NET ライブラリに必要な共通の型です。 このパッケージを直接アプリケーションで使用する必要はありません。 これは、依存関係としてのみ使用されるように考慮されています。

各種クライアント ライブラリには、IndexFieldDocument などのクラスや、 SearchServiceClientSearchIndexClient クラスに対する Indexes.CreateDocuments.Search などの操作が定義されています。 これらのクラスは、次の名前空間にまとめられています。

SDK の今後の更新プログラムについてフィードバックを提供する場合は、フィードバック ページ を参照するか、GitHub でイシューを作成し、イシューのタイトルに "Azure Cognitive Search" を含めます。

.NET SDK は、バージョン 2019-05-06Azure Cognitive Search REST API を対象としています。 このバージョンには、Azure BLOB にインデックスを付ける際の、複合型AI エンリッチメントオートコンプリートJsonLines 分析モードに対するサポートが含まれます。

この SDK では、Search サービスの作成とスケーリングや API キーの管理などの管理操作はサポートされていません。 .NET アプリケーションから Search リソースを管理する必要がある場合は、Azure Cognitive Search .NET Management SDK を使用できます。

v10 にアップグレードする

古いバージョンの Azure Cognitive Search .NET SDK を既に使用しており、一般公開されている最新のバージョンにアップグレードする場合、この記事に方法が説明されています。

SDK の要件

  1. Visual Studio 2017 以降。
  2. 自分が所有する Azure Cognitive Search サービス。 SDK を使用するには、サービスの名前および 1 つまたは複数の API キーが必要です。 ポータルでの Azure Search サービスの作成 」は、これらの手順の参考になります。
  3. Visual Studio の [NuGet パッケージの管理] を使用して、Azure Cognitive Search .NET SDK の NuGet パッケージ をダウンロードします。 NuGet.org でパッケージ名 Microsoft.Azure.Search (あるいは機能の一部のみ必要な場合は、上記のうちの対応するパッケージ名) を検索します。

Azure Cognitive Search .NET SDK では、.NET Framework 4.5.2 以上と .NET Core 2.0 以上を対象とするアプリケーションがサポートされています。

主要なシナリオ

検索アプリケーションではいくつかの処理を実行する必要があります。 このチュートリアルではこれらの主要なシナリオについて説明します。

  • インデックスの作成
  • インデックスへのドキュメントの設定
  • フルテキスト検索およびフィルターを使用したドキュメントの検索

次のサンプル コードは、これらの各シナリオを示しています。 これらのコード スニペットを独自のアプリケーションに自由に使用してください。

概要

これから説明するサンプル アプリケーションは、"hotels" という名前のインデックスを新しく作成し、いくつかのドキュメントをそこに格納してから、検索クエリを実行します。 全体的な流れがわかるメイン プログラムを次に示します。

// This sample shows how to delete, create, upload documents and query an index
static void Main(string[] args)
{
    IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
    IConfigurationRoot configuration = builder.Build();

    SearchServiceClient serviceClient = CreateSearchServiceClient(configuration);

    string indexName = configuration["SearchIndexName"];

    Console.WriteLine("{0}", "Deleting index...\n");
    DeleteIndexIfExists(indexName, serviceClient);

    Console.WriteLine("{0}", "Creating index...\n");
    CreateIndex(indexName, serviceClient);

    ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);

    Console.WriteLine("{0}", "Uploading documents...\n");
    UploadDocuments(indexClient);

    ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(configuration);

    RunQueries(indexClientForQueries);

    Console.WriteLine("{0}", "Complete.  Press any key to end application...\n");
    Console.ReadKey();
}

Note

このチュートリアルで使用したサンプル アプリケーションの完全なソース コードが GitHub にあります。

このプログラムの手順を詳しく見ていきましょう。 最初に、新しい SearchServiceClientを作成する必要があります。 このオブジェクトを使用してインデックスを管理できます。 このオブジェクトを作成するには、Azure Cognitive Search サービス名および管理 API キーを提供する必要があります。 この情報を、サンプル アプリケーションappsettings.json ファイルに入力できます。

private static SearchServiceClient CreateSearchServiceClient(IConfigurationRoot configuration)
{
    string searchServiceName = configuration["SearchServiceName"];
    string adminApiKey = configuration["SearchServiceAdminApiKey"];

    SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
    return serviceClient;
}

Note

正しくないキーを提供すると (たとえば、管理者キーが必要なときにクエリ キーを渡すなど)、Indexes.Create などの操作メソッドを初めて呼び出したときに、SearchServiceClientCloudException をスローして "アクセス不可" メッセージを表示します。 このような場合は、API キーを再確認してください。

次の数行では、メソッドを呼び出して "hotels" という名前のインデックスを作成します。インデックスが既にある場合は最初に削除します。 これらのメソッドについては後で説明します。

Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, serviceClient);

Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, serviceClient);

次に、インデックスを設定する必要があります。 インデックスを設定するには、SearchIndexClient が必要です。 これを取得するには、作成する方法と、SearchServiceClientIndexes.GetClient を呼び出す方法があります。 ここでは簡単な後者を使用します。

ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);

Note

一般的な検索アプリケーションでは、インデックスの管理とインデックスの設定は、検索クエリとは別のコンポーネントによって処理される場合があります。 Indexes.GetClient は、追加の SearchCredentials を指定する手間を省くため、インデックスを作成するのに便利です。 そのためには、SearchServiceClient を作成するときに使用した管理者キーを新しい SearchIndexClient に渡します。 ただし、アプリケーションのクエリを実行する部分では、管理者キーではなく、データの読み取りのみを可能にするクエリ キーを渡すことができるように、SearchIndexClient を直接作成する方が適しています。 これは、最小権限の原則にも適合しており、アプリケーションのセキュリティ強化に役立ちます。 管理者キーとクエリ キーの詳細については、 こちらを参照してください。

SearchIndexClientを作成したので、インデックスを設定できます。 インデックスの設定は、後で説明する別のメソッドによって実行されます。

Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(indexClient);

最後に、検索クエリをいくつか実行し、結果を表示します。 今回は別の SearchIndexClient を使用します。

ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(indexName, configuration);

RunQueries(indexClientForQueries);

RunQueries メソッドについては、後ほど詳しく説明します。 新しい SearchIndexClient を作成するコードを次に示します。

private static SearchIndexClient CreateSearchIndexClient(string indexName, IConfigurationRoot configuration)
{
    string searchServiceName = configuration["SearchServiceName"];
    string queryApiKey = configuration["SearchServiceQueryApiKey"];

    SearchIndexClient indexClient = new SearchIndexClient(searchServiceName, indexName, new SearchCredentials(queryApiKey));
    return indexClient;
}

ここでは、インデックスへの書き込みアクセスは不要であるため、クエリ キーを使用します。 この情報を、サンプル アプリケーションappsettings.json ファイルに入力できます。

有効なサービス名と API キーを使用してこのアプリケーションを実行すると、出力は次の例のようになります。(一部のコンソール出力は、説明のため "..." で置き換えられています。)


Deleting index...

Creating index...

Uploading documents...

Waiting for documents to be indexed...

Search the entire index for the term 'motel' and return only the HotelName field:

Name: Secret Point Motel

Name: Twin Dome Motel


Apply a filter to the index to find hotels with a room cheaper than $100 per night, and return the hotelId and description:

HotelId: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.

HotelId: 2
Description: The hotel is situated in a  nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.


Search the entire index, order by a specific field (lastRenovationDate) in descending order, take the top two results, and show only hotelName and lastRenovationDate:

Name: Triple Landscape Hotel
Last renovated on: 9/20/2015 12:00:00 AM +00:00

Name: Twin Dome Motel
Last renovated on: 2/18/1979 12:00:00 AM +00:00


Search the hotel names for the term 'hotel':

HotelId: 3
Name: Triple Landscape Hotel
...

Complete.  Press any key to end application... 

アプリケーションの完全なソース コードは、この記事の最後で提供します。

次に、 Mainによって呼び出される各メソッドを詳しく見ていきます。

インデックスの作成

SearchServiceClient を作成した後、Main は次に、"hotels" インデックスが既に存在する場合はそれを削除します。 その削除は、次のメソッドによって実行されます。

private static void DeleteIndexIfExists(string indexName, SearchServiceClient serviceClient)
{
    if (serviceClient.Indexes.Exists(indexName))
    {
        serviceClient.Indexes.Delete(indexName);
    }
}

このメソッドは、指定された SearchServiceClient を使用してインデックスが存在するかどうかを確認し、存在する場合は、それを削除します。

Note

この記事のコード例では、わかりやすくするため、Azure Cognitive Search .NET SDK の同期メソッドを使用します。 実際のアプリケーションでは、高い拡張性と応答性を維持するため、非同期メソッドを使用することをお勧めします。 たとえば、上記のメソッドでは、ExistsDelete の代わりに、ExistsAsync および DeleteAsync を使用できます。

次に、 Main は次のメソッドを呼び出すことによって、新しい "hotels" インデックスを作成します。

private static void CreateIndex(string indexName, SearchServiceClient serviceClient)
{
    var definition = new Index()
    {
        Name = indexName,
        Fields = FieldBuilder.BuildForType<Hotel>()
    };
    
    serviceClient.Indexes.Create(definition);
}

このメソッドは、新しいインデックスのスキーマを定義する Field オブジェクトのリストで新しい Index オブジェクトを作成します。 各フィールドには、名前、データ型、および検索動作を定義するいくつかの属性があります。 FieldBuilder クラスでは、リフレクションを使用して指定された Hotel モデル クラスのパブリック プロパティと属性を調べることで、インデックスの Field オブジェクトのリストを作成します。 Hotel クラスについては、後ほど詳しく説明します。

Note

必要に応じて、FieldBuilder を使用するのではなく、Field オブジェクトのリストをいつでも直接作成できます。 たとえば、モデル クラスを使用しない場合や、属性を追加して変更するのは望ましくない既存のモデル クラスを使用する必要がある場合などです。

フィールドに加えて、スコアリング プロファイル、サジェスター、または CORS オプションも Index に追加できます (簡潔にするために、これらのパラメーターはサンプルから省略されています)。 Index オブジェクトとその構成要素の詳細については、SDK リファレンスおよび Azure Cognitive Search REST API リファレンスをご覧ください。

インデックスの設定

Main の次の手順では、新しく作成したインデックスを設定します。 このインデックスの作成は、次の方法で実行されます: (説明のために一部のコードは "..." に置き換えられました。完全なデータ作成コードについては、完全なサンプル ソリューションを参照してください。)

private static void UploadDocuments(ISearchIndexClient indexClient)
{
    var hotels = new Hotel[]
    {
        new Hotel()
        {
            HotelId = "1",
            HotelName = "Secret Point Motel",
            ...
            Address = new Address()
            {
                StreetAddress = "677 5th Ave",
                ...
            },
            Rooms = new Room[]
            {
                new Room()
                {
                    Description = "Budget Room, 1 Queen Bed (Cityside)",
                    ...
                },
                new Room()
                {
                    Description = "Budget Room, 1 King Bed (Mountain View)",
                    ...
                },
                new Room()
                {
                    Description = "Deluxe Room, 2 Double Beds (City View)",
                    ...
                }
            }
        },
        new Hotel()
        {
            HotelId = "2",
            HotelName = "Twin Dome Motel",
            ...
            {
                StreetAddress = "140 University Town Center Dr",
                ...
            },
            Rooms = new Room[]
            {
                new Room()
                {
                    Description = "Suite, 2 Double Beds (Mountain View)",
                    ...
                },
                new Room()
                {
                    Description = "Standard Room, 1 Queen Bed (City View)",
                    ...
                },
                new Room()
                {
                    Description = "Budget Room, 1 King Bed (Waterfront View)",
                    ...
                }
            }
        },
        new Hotel()
        {
            HotelId = "3",
            HotelName = "Triple Landscape Hotel",
            ...
            Address = new Address()
            {
                StreetAddress = "3393 Peachtree Rd",
                ...
            },
            Rooms = new Room[]
            {
                new Room()
                {
                    Description = "Standard Room, 2 Queen Beds (Amenities)",
                    ...
                },
                new Room ()
                {
                    Description = "Standard Room, 2 Double Beds (Waterfront View)",
                    ...
                },
                new Room()
                {
                    Description = "Deluxe Room, 2 Double Beds (Cityside)",
                    ...
                }
            }
        }
    };

    var batch = IndexBatch.Upload(hotels);

    try
    {
        indexClient.Documents.Index(batch);
    }
    catch (IndexBatchException e)
    {
        // Sometimes when your Search service is under load, indexing will fail for some of the documents in
        // the batch. Depending on your application, you can take compensating actions like delaying and
        // retrying. For this simple demo, we just log the failed document keys and continue.
        Console.WriteLine(
            "Failed to index some of the documents: {0}",
            String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
    }

    Console.WriteLine("Waiting for documents to be indexed...\n");
    Thread.Sleep(2000);
}

このメソッドには 4 つの部分があります。 最初の部分では、インデックスにアップロードする入力データとして使用される、3 つの Hotel オブジェクトと、それぞれに含まれる 3 つの Room オブジェクトの配列を作成します。 このデータは、わかりやすくするためハードコーディングされています。 実際のアプリケーションでは、通常、データは SQL Database などの外部データ ソースから取得されます。

2 番目の部分では、ドキュメントを含む IndexBatch を作成します。 この場合は IndexBatch.Uploadを呼び出すことによって、作成時にバッチに適用する操作を指定します。 その後、バッチは Documents.Index メソッドによって Azure Cognitive Search インデックスにアップロードされます。

注意

この例では、単にドキュメントをアップロードします。 既存のドキュメントに変更をマージする、またはドキュメントを削除する場合は、代わりに IndexBatch.MergeIndexBatch.MergeOrUpload、または IndexBatch.Delete を呼び出すことによってバッチを作成できます。 IndexBatch.New を呼び出すことによって 1 つのバッチでさまざまな操作を組み合わせることもできます。これによって、IndexAction オブジェクトのコレクションを受け取り、各オブジェクトがドキュメントで特定の操作を実行するよう Azure Cognitive Search に指示します。 IndexAction.MergeIndexAction.Upload などの対応するメソッドを呼び出すことによって、独自の操作を行う IndexAction を作成できます。

このメソッドの 3 番目の部分は、インデックス作成の重要なエラー ケースを処理する catch ブロックです。 Azure Cognitive Search がバッチ内の一部のドキュメントのインデックス作成に失敗した場合、Documents.IndexIndexBatchException をスローします。 この例外は、サービスの負荷が高いときにドキュメントのインデックスを作成していると発生する場合があります。 コードでこのケースを明示的に処理することを強くお勧めします。 しばらく待ってから失敗したドキュメントのインデックス作成を再試行したり、サンプルと同じようにログに記録してから続けることができます。または、アプリケーションのデータ整合性要件に応じて他の処理を行うこともできます。

Note

FindFailedActionsToRetry メソッドを使用して、Index の前回の呼び出しで失敗したアクションだけを含む新しいバッチを作成できます。 このメソッドの適切な使用方法については、StackOverflow をご覧ください。

最後に、UploadDocuments メソッドは 2 秒間遅延します。 インデックスの作成は Azure Cognitive Search サービスで非同期的に行われるので、サンプル アプリケーションは短い時間待機して、確実にドキュメントを検索に使用できるようにする必要があります。 通常、このような遅延は、デモ、テスト、およびサンプル アプリケーションでのみ必要です。

.NET SDK がドキュメントを処理する方法

Azure Cognitive Search .NET SDK が Hotel のようなユーザー定義クラスのインスタンスをどのようにしてインデックスにアップロードできるのか不思議に思われるかもしれません。 その質問に答えるため、 Hotel クラスを見ていくことにします。

using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Spatial;
using Newtonsoft.Json;

public partial class Hotel
{
    [System.ComponentModel.DataAnnotations.Key]
    [IsFilterable]
    public string HotelId { get; set; }

    [IsSearchable, IsSortable]
    public string HotelName { get; set; }

    [IsSearchable]
    [Analyzer(AnalyzerName.AsString.EnLucene)]
    public string Description { get; set; }

    [IsSearchable]
    [Analyzer(AnalyzerName.AsString.FrLucene)]
    [JsonProperty("Description_fr")]
    public string DescriptionFr { get; set; }

    [IsSearchable, IsFilterable, IsSortable, IsFacetable]
    public string Category { get; set; }

    [IsSearchable, IsFilterable, IsFacetable]
    public string[] Tags { get; set; }

    [IsFilterable, IsSortable, IsFacetable]
    public bool? ParkingIncluded { get; set; }

    // SmokingAllowed reflects whether any room in the hotel allows smoking.
    // The JsonIgnore attribute indicates that a field should not be created 
    // in the index for this property and it will only be used by code in the client.
    [JsonIgnore]
    public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;

    [IsFilterable, IsSortable, IsFacetable]
    public DateTimeOffset? LastRenovationDate { get; set; }

    [IsFilterable, IsSortable, IsFacetable]
    public double? Rating { get; set; }

    public Address Address { get; set; }

    [IsFilterable, IsSortable]
    public GeographyPoint Location { get; set; }

    public Room[] Rooms { get; set; }
}

最初に注目すべき点は、Hotel クラス内の各パブリック プロパティの名前がインデックス定義内の同じ名前でフィールドにマップされることです。 各フィールドを小文字で始める場合 ("camel case")、そのクラス上の [SerializePropertyNamesAsCamelCase] 属性を使用してプロパティ名を自動的に camel-case にマップするよう、SDK に指示することができます。 このシナリオは、ターゲット スキーマがアプリケーション開発者の制御外にあるときに、.NET の "パスカル ケース" 命名に関するガイドラインに違反することなくデータ バインドを実行する .NET アプリケーションでは一般的です。

Note

Azure Cognitive Search .NET SDK は、NewtonSoft JSON.NET ライブラリを使用して、カスタムのモデル オブジェクトから JSON 形式へのシリアル化や JSON 形式からの逆シリアル化を行います。 必要に応じてこのシリアル化をカスタマイズできます。 詳細については、「JSON.NET 使用したシリアル化のカスタマイズ」をご覧ください。

次に注目すべき点は、各プロパティが IsFilterableIsSearchableKeyAnalyzer などの属性で装飾されていることです。 これらの属性は、Azure Cognitive Search インデックス内の対応するフィールド属性に直接マップされます。 FieldBuilder クラスは、これらのプロパティを使用してインデックスのフィールド定義を構築します。

Hotel クラスに関する 3 番目の重要な点は、パブリック プロパティのデータ型です。 これらのプロパティの .NET 型は、インデックス定義でそれらと同等のフィールド型にマップします。 たとえば、Category 文字列プロパティは、Edm.String 型の category フィールドにマップします。 bool?Edm.BooleanDateTimeOffset?Edm.DateTimeOffset などの間にも、同じような型のマッピングがあります。 型のマッピングの具体的なルールについては、Azure Cognitive Search .NET SDK リファレンスDocuments.Get メソッドを参照してください。 FieldBuilder クラスは、このマッピングに自動的に対処しますが、シリアル化の問題のトラブルシューティングを行う必要がある場合に、マッピングを理解しておくと役立ちます。

SmokingAllowed プロパティを見つけてしまいましたか?

[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;

このプロパティの JsonIgnore 属性によって、フィールドとしてインデックスにシリアル化しないよう、FieldBuilder に指示が与えられます。 これは、ご利用のアプリケーションでヘルパーとして使用できる、クライアント側の計算されたプロパティを作成する優れた方法です。 この場合、SmokingAllowed プロパティは、Rooms コレクション内に喫煙可能な Room があるかどうかを反映します。 すべて false の場合、ホテル全館で喫煙できないことを示します。

AddressRooms など、いくつかのプロパティは、.NET クラスのインスタンスです。 これらのプロパティは、さらに複雑なデータ構造を表し、インデックス内で複合データ型を備えたフィールドを必要とします。

Address プロパティは、以下に定義された Address クラス内の複数の値のセットを表します。

using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;

namespace AzureSearch.SDKHowTo
{
    public partial class Address
    {
        [IsSearchable]
        public string StreetAddress { get; set; }

        [IsSearchable, IsFilterable, IsSortable, IsFacetable]
        public string City { get; set; }

        [IsSearchable, IsFilterable, IsSortable, IsFacetable]
        public string StateProvince { get; set; }

        [IsSearchable, IsFilterable, IsSortable, IsFacetable]
        public string PostalCode { get; set; }

        [IsSearchable, IsFilterable, IsSortable, IsFacetable]
        public string Country { get; set; }
    }
}

このクラスには、米国またはカナダで住所を記述するために使用される標準的な値が含まれます。 インデックス内で論理フィールドをグループ化するには、このような型を使用できます。

Rooms プロパティは、Room オブジェクトの配列を表します。

using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;

namespace AzureSearch.SDKHowTo
{
    public partial class Room
    {
        [IsSearchable]
        [Analyzer(AnalyzerName.AsString.EnMicrosoft)]
        public string Description { get; set; }

        [IsSearchable]
        [Analyzer(AnalyzerName.AsString.FrMicrosoft)]
        [JsonProperty("Description_fr")]
        public string DescriptionFr { get; set; }

        [IsSearchable, IsFilterable, IsFacetable]
        public string Type { get; set; }

        [IsFilterable, IsFacetable]
        public double? BaseRate { get; set; }

        [IsSearchable, IsFilterable, IsFacetable]
        public string BedOptions { get; set; }

        [IsFilterable, IsFacetable]
        public int SleepsCount { get; set; }

        [IsFilterable, IsFacetable]
        public bool? SmokingAllowed { get; set; }

        [IsSearchable, IsFilterable, IsFacetable]
        public string[] Tags { get; set; }
    }
}

.NET のデータ モデルおよびその対応するインデックス スキーマは、エンド ユーザーに提供する検索エクスペリエンスをサポートするように設計されなくてはなりません。 .NET の最上位レベル オブジェクト、すなわちインデックス内のドキュメントはそれぞれ、ユーザー インターフェイスで示される検索結果に対応します。 たとえば、ホテル検索アプリケーションで、エンド ユーザーは、ホテル名、ホテルの機能、または特定の部屋の特徴で検索することができます。 後でクエリの例をいくつか説明します。

インデックス内でドキュメントの操作を行うために独自のクラスを使用するこの機能は、次のセクションで説明するように、検索結果の取得、および SDK による任意の型への自動逆シリアル化という両方向で動作します。

Note

Azure Cognitive Search .NET SDK は、Document クラスを使用して動的に型指定されたドキュメントもサポートします。これは、フィールドの値に対するフィールド名のキー/値マッピングです。 この機能は、設計時にインデックス スキーマがわからない場合、または特定のモデル クラスにバインドすると不都合な場合に便利です。 ドキュメントを処理する SDK のすべてのメソッドには、Document クラスを使用するオーバーロード、およびジェネリック型パラメーターを使用する厳密な型指定のオーバーロードがあります。 このチュートリアルのサンプル コードでは、後者のみを使用しています。 Document クラスDictionary<string, object> から継承します。

null 許容のデータ型を使用する理由

Azure Cognitive Search インデックスにマップする独自のモデル クラスを設計するときは、boolint などの値型のプロパティを null 許容型として宣言することをお勧めします (たとえば、bool ではなく bool? を使用する)。 null 非許容プロパティを使用する場合、対応するフィールドに null 値が含まれるドキュメントがインデックス内に存在しないことを、開発者が 保証する 必要があります。 SDK または Azure Cognitive Search サービスで、これを強制することはできません。

これは単なる仮定上の問題ではありません。Edm.Int32 型の既存のインデックスに新しいフィールドを追加する場合を考えてみてください。 インデックスの定義を更新した後、(Azure Cognitive Search ではすべての型が null を許容するので) すべてのドキュメントでその新しいフィールドの値が null になります。 その後、そのフィールドが null 非許容型の int プロパティであるモデル クラスを使用した場合、ドキュメントを取得しようとすると、次のような JsonSerializationException が発生します。

Error converting value {null} to type 'System.Int32'. Path 'IntValue'.

このため、ベスト プラクティスとして、モデル クラスでは null 許容型を使用することをお勧めします。

JSON.NET 使用したシリアル化のカスタマイズ

SDK では、ドキュメントのシリアル化と逆シリアル化に JSON.NET を使用します。 独自の JsonConverter または IContractResolver を定義することによって、必要に応じてシリアル化と逆シリアル化をカスタマイズできます。 詳細については、JSON.NET のドキュメントを参照してください。 この機能は、アプリケーションの既存のモデル クラスを Azure Cognitive Search 用に適合させる場合、およびその他の高度なシナリオに役立ちます。 たとえば、カスタム シリアル化を使用すると次のことが可能です。

  • ドキュメント フィールドとして格納されるものに、モデル クラスの特定のプロパティを含める、または除外する。
  • コードのプロパティ名とインデックスのフィールド名をマップする。
  • ドキュメント フィールドへのプロパティのマッピングに使用できるカスタム属性を作成する。

Azure Cognitive Search .NET SDK のユニット テストにカスタム シリアル化を実装する例については、GitHub を参照してください。 手始めとしては、このフォルダーが適しています。 カスタム シリアル化のテストに使用されるクラスが含まれます。

インデックス内のドキュメントの検索

サンプル アプリケーションの最後の手順として、インデックス内のいくつかのドキュメントを検索します。

private static void RunQueries(ISearchIndexClient indexClient)
{
    SearchParameters parameters;
    DocumentSearchResult<Hotel> results;

    Console.WriteLine("Search the entire index for the term 'motel' and return only the HotelName field:\n");

    parameters =
        new SearchParameters()
        {
            Select = new[] { "HotelName" }
        };

    results = indexClient.Documents.Search<Hotel>("motel", parameters);

    WriteDocuments(results);

    Console.Write("Apply a filter to the index to find hotels with a room cheaper than $100 per night, ");
    Console.WriteLine("and return the hotelId and description:\n");

    parameters =
        new SearchParameters()
        {
            Filter = "Rooms/any(r: r/BaseRate lt 100)",
            Select = new[] { "HotelId", "Description" }
        };

    results = indexClient.Documents.Search<Hotel>("*", parameters);

    WriteDocuments(results);

    Console.Write("Search the entire index, order by a specific field (lastRenovationDate) ");
    Console.Write("in descending order, take the top two results, and show only hotelName and ");
    Console.WriteLine("lastRenovationDate:\n");

    parameters =
        new SearchParameters()
        {
            OrderBy = new[] { "LastRenovationDate desc" },
            Select = new[] { "HotelName", "LastRenovationDate" },
            Top = 2
        };

    results = indexClient.Documents.Search<Hotel>("*", parameters);

    WriteDocuments(results);

    Console.WriteLine("Search the entire index for the term 'hotel':\n");

    parameters = new SearchParameters();
    results = indexClient.Documents.Search<Hotel>("hotel", parameters);

    WriteDocuments(results);
}

クエリを実行するたびに、このメソッドはまず新しい SearchParameters オブジェクトを作成します。 このオブジェクトは、並べ替え、フィルター処理、ページング、ファセットなどの、クエリへの追加オプションを指定するために使用されます。 このメソッドでは、さまざまなクエリの FilterSelectOrderByTop の各プロパティを設定します。 SearchParameters のすべてのプロパティについては、こちらをご覧ください。

次の手順では、検索クエリを実際に実行します。 この検索は、Documents.Search メソッドを使用して実行されます。 各クエリでは、使用する検索テキストを文字列として渡し (検索テキストがない場合は "*")、以前に作成した検索パラメーターも渡します。 また、Documents.Search に対する型パラメーターとして Hotel も指定します。これは、検索結果のドキュメントを Hotel 型のオブジェクトに逆シリアル化するように SDK に指示します。

Note

検索クエリ式の構文の詳細については、こちらをご覧ください。

最後に、各クエリの実行後、このメソッドは検索結果のすべての一致を反復処理し、各ドキュメントをコンソールに出力します。

private static void WriteDocuments(DocumentSearchResult<Hotel> searchResults)
{
    foreach (SearchResult<Hotel> result in searchResults.Results)
    {
        Console.WriteLine(result.Document);
    }

    Console.WriteLine();
}

各クエリについて詳しく見ていきましょう。 最初のクエリを実行するコードを次に示します。

parameters =
    new SearchParameters()
    {
        Select = new[] { "HotelName" }
    };

results = indexClient.Documents.Search<Hotel>("motel", parameters);

WriteDocuments(results);

この例では、検索可能なあらゆるフィールドで "motel" という単語のインデックス全体を検索し、Select パラメーターによって指定されるとおり、ホテル名だけを返します。 結果は次のようになります。

Name: Secret Point Motel

Name: Twin Dome Motel

次のクエリは、さらに興味深いものです。 1 泊 100 ドル未満の部屋があるホテルを検索し、ホテル ID と説明だけを返します。

parameters =
    new SearchParameters()
    {
        Filter = "Rooms/any(r: r/BaseRate lt 100)",
        Select = new[] { "HotelId", "Description" }
    };

results = indexClient.Documents.Search<Hotel>("*", parameters);

WriteDocuments(results);

このクエリでは、OData の $filter 式 (Rooms/any(r: r/BaseRate lt 100)) を使用して、インデックス内のドキュメントをフィルター処理します。 ここでは、あらゆる演算子を使用して、'BaseRate lt 100' をルーム コレクションのすべての項目に適用します。 Azure Cognitive Search がサポートする OData 構文の詳細については、こちらを参照してください。

クエリの結果は次のとおりです。

HotelId: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York...

HotelId: 2
Description: The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to...

次に、最近改装された上位 2 つのホテルを検索し、ホテル名と最終改装日を表示します。 次にコードを示します。

parameters =
    new SearchParameters()
    {
        OrderBy = new[] { "LastRenovationDate desc" },
        Select = new[] { "HotelName", "LastRenovationDate" },
        Top = 2
    };

results = indexClient.Documents.Search<Hotel>("*", parameters);

WriteDocuments(results);

この例でも、OData 構文を使用して OrderBy パラメーターに lastRenovationDate desc を指定しています。 また、上位 2 つのドキュメントだけを取得できるように、Top を 2 に設定しています。 前と同様に、Select を設定して返す必要があるフィールドを指定します。

結果は次のようになります。

Name: Fancy Stay        Last renovated on: 6/27/2010 12:00:00 AM +00:00
Name: Roach Motel       Last renovated on: 4/28/1982 12:00:00 AM +00:00

最後に、"hotel" という単語に一致するすべてのホテル名を検索します。

parameters = new SearchParameters()
{
    SearchFields = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("hotel", parameters);

WriteDocuments(results);

結果は次のとおりです。Select プロパティを指定しなかったので、この結果にはすべてのフィールドが含まれています。

	HotelId: 3
	Name: Triple Landscape Hotel
	...

チュートリアルはここまでですが、ここで止めないでください。 **次のステップでは、Azure Cognitive Search をさらに学習するための他のリソースを提供します。

次のステップ