.NET アプリケーションから Azure Search を使用する方法How to use Azure Search from a .NET Application

この記事では、 Azure Search .NET SDKを使用する手順について説明します。This article is a walkthrough to get you up and running with the Azure Search .NET SDK. .NET SDK を使用すると、Azure Search を使用してアプリケーションにリッチな検索エクスペリエンスを実装できます。You can use the .NET SDK to implement a rich search experience in your application using Azure Search.

Azure Search SDK の内容What's in the Azure Search SDK

この SDK は、HTTP や JSON に関する詳しい知識がなくても、インデックスやデータ ソース、インデクサー、シノニム マップの管理、ドキュメントのアップロードと管理、クエリの実行を行うことを可能にするいくつかのクライアント ライブラリから構成されています。The SDK consists of a few client libraries that enable you to manage your indexes, data sources, indexers, and synonym maps, as well as upload and manage documents, and execute queries, all without having to deal with the details of HTTP and JSON. これらのクライアント ライブラリはすべて、NuGet パッケージとして配布されます。These client libraries are all distributed as NuGet packages.

メインの NuGet パッケージは Microsoft.Azure.Search です。このパッケージは、依存関係がある他のすべてのパッケージを含むメタパッケージです。The main NuGet package is Microsoft.Azure.Search, which is a meta-package that includes all the other packages as dependencies. 初めて取り組む場合、あるいはアプリケーションに Azure Search の全機能が必要と分かっている場合は、このパッケージを使用します。Use this package if you're just getting started or if you know your application will need all the features of Azure Search.

SDK のその他の NuGet パッケージとしては以下があります。The other NuGet packages in the SDK are:

  • Microsoft.Azure.Search.Data:Azure Search を使用して .NET アプリケーションを開発していて、インデックス内のドキュメントのクエリまたは更新のみを行う必要がある場合は、このパッケージを使用します。Microsoft.Azure.Search.Data: Use this package if you're developing a .NET application using Azure Search, and you only need to query or update documents in your indexes. インデックス、シノニム マップ、またはサービス レベルのその他のリソースの作成や更新も行う必要がある場合は、代わりに Microsoft.Azure.Search パッケージを使用します。If you also need to create or update indexes, synonym maps, or other service-level resources, use the Microsoft.Azure.Search package instead.
  • Microsoft.Azure.Search.Service:.NET で、Azure Search インデックス、シノニム マップ、インデクサー、データ ソース、またはサービスレベルのその他のリソースを管理するための自動化を開発する場合は、このパッケージを使用します。Microsoft.Azure.Search.Service: Use this package if you're developing automation in .NET to manage Azure Search indexes, synonym maps, indexers, data sources, or other service-level resources. インデックス内のドキュメントのクエリまたは更新のみを行う場合は、代わりに Microsoft.Azure.Search.Data パッケージを使用します。If you only need to query or update documents in your indexes, use the Microsoft.Azure.Search.Data package instead. Azure Search のすべての機能が必要な場合は、代わりに Microsoft.Azure.Search パッケージを使用します。If you need all the functionality of Azure Search, use the Microsoft.Azure.Search package instead.
  • Microsoft.Azure.Search.Common:Azure Search .NET ライブラリに必要な共通の型です。Microsoft.Azure.Search.Common: Common types needed by the Azure Search .NET libraries. このパッケージを直接アプリケーションで使用する必要はありません。You do not need to use this package directly in your application. これは、依存関係としてのみ使用されるように考慮されています。It is only meant to be used as a dependency.

各種クライアント ライブラリには、IndexFieldDocument などのクラスや、 SearchServiceClientSearchIndexClient クラスに対する Indexes.CreateDocuments.Search などの操作が定義されています。The various client libraries define classes like Index, Field, and Document, as well as operations like Indexes.Create and Documents.Search on the SearchServiceClient and SearchIndexClient classes. これらのクラスは、次の名前空間にまとめられています。These classes are organized into the following namespaces:

SDK の今後の更新プログラムについてフィードバックを提供する場合は、フィードバック ページ を参照するか、GitHub でイシューを作成し、イシューのタイトルに "Azure Search" を含めます。If you would like to provide feedback for a future update of the SDK, see our feedback page or create an issue on GitHub and mention "Azure Search" in the issue title.

.NET SDK は、バージョン 2019-05-06Azure Search REST API をサポートします。The .NET SDK supports version 2019-05-06 of the Azure Search REST API. このバージョンには、Azure BLOB にインデックスを付ける際の、複合型コグニティブ検索オートコンプリートJsonLines 分析モードに対するサポートが含まれます。This version includes support for complex types, cognitive search, autocomplete, and JsonLines parsing mode when indexing Azure Blobs.

この SDK では、Search サービスの作成とスケーリングや API キーの管理などの管理操作はサポートされていません。This SDK does not support Management Operations such as creating and scaling Search services and managing API keys. .NET アプリケーションから Search リソースを管理する必要がある場合は、Azure Search .NET Management SDK を使用できます。If you need to manage your Search resources from a .NET application, you can use the Azure Search .NET Management SDK.

最新バージョンの SDK へのアップグレードUpgrading to the latest version of the SDK

古いバージョンの Azure Search .NET SDK を既に使用しており、一般公開されている最新のバージョンにアップグレードする場合、この記事に方法が説明されています。If you're already using an older version of the Azure Search .NET SDK and you'd like to upgrade to the latest generally available version, this article explains how.

SDK の要件Requirements for the SDK

  1. Visual Studio 2017 以降。Visual Studio 2017 or later.
  2. 自分が所有する Azure Search サービス。Your own Azure Search service. SDK を使用するには、サービスの名前および 1 つまたは複数の API キーが必要です。In order to use the SDK, you will need the name of your service and one or more API keys. ポータルでの Azure Search サービスの作成 」は、これらの手順の参考になります。Create a service in the portal will help you through these steps.
  3. Visual Studio の [NuGet パッケージの管理] を使用して、Azure Search .NET SDK の NuGet パッケージ をダウンロードします。Download the Azure Search .NET SDK NuGet package by using "Manage NuGet Packages" in Visual Studio. NuGet.org でパッケージ名 Microsoft.Azure.Search (あるいは機能の一部のみ必要な場合は、上記のうちの対応するパッケージ名) を検索します。Just search for the package name Microsoft.Azure.Search on NuGet.org (or one of the other package names above if you only need a subset of the functionality).

Azure Search .NET SDK では、.NET Framework 4.5.2 以上と .NET Core 2.0 以上を対象とするアプリケーションがサポートされています。The Azure Search .NET SDK supports applications targeting the .NET Framework 4.5.2 and higher, as well as .NET Core 2.0 and higher.

主要なシナリオCore scenarios

検索アプリケーションではいくつかの処理を実行する必要があります。There are several things you'll need to do in your search application. このチュートリアルではこれらの主要なシナリオについて説明します。In this tutorial, we'll cover these core scenarios:

  • インデックスの作成Creating an index
  • インデックスへのドキュメントの設定Populating the index with documents
  • フルテキスト検索およびフィルターを使用したドキュメントの検索Searching for documents using full-text search and filters

次のサンプル コードは、これらの各シナリオを示しています。The following sample code illustrates each of these scenarios. これらのコード スニペットを独自のアプリケーションに自由に使用してください。Feel free to use the code snippets in your own application.

概要Overview

これから説明するサンプル アプリケーションは、"hotels" という名前のインデックスを新しく作成し、いくつかのドキュメントをそこに格納してから、検索クエリを実行します。The sample application we'll be exploring creates a new index named "hotels", populates it with a few documents, then executes some search queries. 全体的な流れがわかるメイン プログラムを次に示します。Here is the main program, showing the overall flow:

// 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();
}

注意

このチュートリアルで使用したサンプル アプリケーションの完全なソース コードが GitHub にあります。You can find the full source code of the sample application used in this walk through on GitHub.

このプログラムの手順を詳しく見ていきましょう。We'll walk through this step by step. 最初に、新しい SearchServiceClientを作成する必要があります。First we need to create a new SearchServiceClient. このオブジェクトを使用してインデックスを管理できます。This object allows you to manage indexes. このオブジェクトを作成するには、Azure Search サービス名および管理 API キーを提供する必要があります。In order to construct one, you need to provide your Azure Search service name as well as an admin API key. この情報を、サンプル アプリケーションappsettings.json ファイルに入力できます。You can enter this information in the appsettings.json file of the sample application.

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

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

注意

正しくないキーを提供すると (たとえば、管理者キーが必要なときにクエリ キーを渡すなど)、Indexes.Create などの操作メソッドを初めて呼び出したときに、SearchServiceClientCloudException をスローして "アクセス不可" メッセージを表示します。If you provide an incorrect key (for example, a query key where an admin key was required), the SearchServiceClient will throw a CloudException with the error message "Forbidden" the first time you call an operation method on it, such as Indexes.Create. このような場合は、API キーを再確認してください。If this happens to you, double-check our API key.

次の数行では、メソッドを呼び出して "hotels" という名前のインデックスを作成します。インデックスが既にある場合は最初に削除します。The next few lines call methods to create an index named "hotels", deleting it first if it already exists. これらのメソッドについては後で説明します。We will walk through these methods a little later.

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

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

次に、インデックスを設定する必要があります。Next, the index needs to be populated. インデックスを設定するには、SearchIndexClient が必要です。To do populate the index, we will need a SearchIndexClient. これを取得するには、作成する方法と、SearchServiceClientIndexes.GetClient を呼び出す方法があります。There are two ways to obtain one: by constructing it, or by calling Indexes.GetClient on the SearchServiceClient. ここでは簡単な後者を使用します。We use the latter for convenience.

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

注意

一般的な検索アプリケーションでは、インデックスの管理とインデックスの設定は、検索クエリとは別のコンポーネントによって処理される場合があります。In a typical search application, index management and population may be handled by a separate component from search queries. Indexes.GetClient は、追加の SearchCredentials を指定する手間を省くため、インデックスを作成するのに便利です。Indexes.GetClient is convenient for populating an index because it saves you the trouble of providing additional SearchCredentials. そのためには、SearchServiceClient を作成するときに使用した管理者キーを新しい SearchIndexClient に渡します。It does this by passing the admin key that you used to create the SearchServiceClient to the new SearchIndexClient. ただし、アプリケーションのクエリを実行する部分では、管理者キーではなく、データの読み取りのみを可能にするクエリ キーを渡すことができるように、SearchIndexClient を直接作成する方が適しています。However, in the part of your application that executes queries, it is better to create the SearchIndexClient directly so that you can pass in a query key, which only allows you to read data, instead of an admin key. これは、最小権限の原則にも適合しており、アプリケーションのセキュリティ強化に役立ちます。This is consistent with the principle of least privilege and will help to make your application more secure. 管理者キーとクエリ キーの詳細については、 こちらを参照してください。You can find out more about admin keys and query keys here.

SearchIndexClientを作成したので、インデックスを設定できます。Now that we have a SearchIndexClient, we can populate the index. インデックスの設定は、後で説明する別のメソッドによって実行されます。Index population is done by another method that we will walk through later.

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

最後に、検索クエリをいくつか実行し、結果を表示します。Finally, we execute a few search queries and display the results. 今回は別の SearchIndexClient を使用します。This time we use a different SearchIndexClient:

ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(indexName, configuration);

RunQueries(indexClientForQueries);

RunQueries メソッドについては、後ほど詳しく説明します。We will take a closer look at the RunQueries method later. 新しい SearchIndexClient を作成するコードを次に示します。Here is the code to create the new 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;
}

ここでは、インデックスへの書き込みアクセスは不要であるため、クエリ キーを使用します。This time we use a query key since we do not need write access to the index. この情報を、サンプル アプリケーションappsettings.json ファイルに入力できます。You can enter this information in the appsettings.json file of the sample application.

有効なサービス名と API キーを使用してこのアプリケーションを実行すると、出力は次の例のようになります。(一部のコンソール出力は、説明のため "..." で置き換えられています。)If you run this application with a valid service name and API keys, the output should look like this example: (Some console output has been replaced with "..." for illustration purposes.)

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... 

アプリケーションの完全なソース コードは、この記事の最後で提供します。The full source code of the application is provided at the end of this article.

次に、 Mainによって呼び出される各メソッドを詳しく見ていきます。Next, we will take a closer look at each of the methods called by Main.

インデックスの作成Creating an index

SearchServiceClient を作成した後、Main は次に、"hotels" インデックスが既に存在する場合はそれを削除します。After creating a SearchServiceClient, Main deletes the "hotels" index if it already exists. その削除は、次のメソッドによって実行されます。That deletion is done by the following method:

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

このメソッドは、指定された SearchServiceClient を使用してインデックスが存在するかどうかを確認し、存在する場合は、それを削除します。This method uses the given SearchServiceClient to check if the index exists, and if so, delete it.

注意

この記事のコード例では、わかりやすくするため、Azure Search .NET SDK の同期メソッドを使用します。The example code in this article uses the synchronous methods of the Azure Search .NET SDK for simplicity. 実際のアプリケーションでは、高い拡張性と応答性を維持するため、非同期メソッドを使用することをお勧めします。We recommend that you use the asynchronous methods in your own applications to keep them scalable and responsive. たとえば、上記のメソッドでは、ExistsDelete の代わりに、ExistsAsync および DeleteAsync を使用できます。For example, in the method above you could use ExistsAsync and DeleteAsync instead of Exists and Delete.

次に、 Main は次のメソッドを呼び出すことによって、新しい "hotels" インデックスを作成します。Next, Main creates a new "hotels" index by calling this method:

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

このメソッドは、新しいインデックスのスキーマを定義する Field オブジェクトのリストで新しい Index オブジェクトを作成します。This method creates a new Index object with a list of Field objects that defines the schema of the new index. 各フィールドには、名前、データ型、および検索動作を定義するいくつかの属性があります。Each field has a name, data type, and several attributes that define its search behavior. FieldBuilder クラスでは、リフレクションを使用して指定された Hotel モデル クラスのパブリック プロパティと属性を調べることで、インデックスの Field オブジェクトのリストを作成します。The FieldBuilder class uses reflection to create a list of Field objects for the index by examining the public properties and attributes of the given Hotel model class. Hotel クラスについては、後ほど詳しく説明します。We'll take a closer look at the Hotel class later on.

注意

必要に応じて、FieldBuilder を使用するのではなく、Field オブジェクトのリストをいつでも直接作成できます。You can always create the list of Field objects directly instead of using FieldBuilder if needed. たとえば、モデル クラスを使用しない場合や、属性を追加して変更するのは望ましくない既存のモデル クラスを使用する必要がある場合などです。For example, you may not want to use a model class or you may need to use an existing model class that you don't want to modify by adding attributes.

フィールドに加えて、スコアリング プロファイル、サジェスター、または CORS オプションも Index に追加できます (簡潔にするために、これらのパラメーターはサンプルから省略されています)。In addition to fields, you can also add scoring profiles, suggesters, or CORS options to the Index (these parameters are omitted from the sample for brevity). Index オブジェクトとその構成要素の詳細については、SDK リファレンスおよび Azure Search REST API リファレンスをご覧ください。You can find more information about the Index object and its constituent parts in the SDK reference, as well as in the Azure Search REST API reference.

インデックスの設定Populating the index

Main の次の手順では、新しく作成したインデックスを設定します。The next step in Main populates the newly-created index. このインデックス設定は、次のメソッドで実行されます。(説明のため "..." で置き換えられた一部のコード。This index population is done in the following method: (Some code replaced with "..." for illustration purposes. 完全なデータ生成コードに対する完全なサンプル ソリューションを参照してください。)See the full sample solution for the full data population code.)

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 つの部分があります。This method has four parts. 最初の部分では、インデックスにアップロードする入力データとして使用される、3 つの Hotel オブジェクトと、それぞれに含まれる 3 つの Room オブジェクトの配列を作成します。The first creates an array of 3 Hotel objects each with 3 Room objects that will serve as our input data to upload to the index. このデータは、わかりやすくするためハードコーディングされています。This data is hard-coded for simplicity. 実際のアプリケーションでは、通常、データは SQL Database などの外部データ ソースから取得されます。In your own application, your data will likely come from an external data source such as a SQL database.

2 番目の部分では、ドキュメントを含む IndexBatch を作成します。The second part creates an IndexBatch containing the documents. この場合は IndexBatch.Uploadを呼び出すことによって、作成時にバッチに適用する操作を指定します。You specify the operation you want to apply to the batch at the time you create it, in this case by calling IndexBatch.Upload. その後、バッチは Documents.Index メソッドによって Azure Search インデックスにアップロードされます。The batch is then uploaded to the Azure Search index by the Documents.Index method.

注意

この例では、単にドキュメントをアップロードします。In this example, we are just uploading documents. 既存のドキュメントに変更をマージする、またはドキュメントを削除する場合は、代わりに IndexBatch.MergeIndexBatch.MergeOrUpload、または IndexBatch.Delete を呼び出すことによってバッチを作成できます。If you wanted to merge changes into existing documents or delete documents, you could create batches by calling IndexBatch.Merge, IndexBatch.MergeOrUpload, or IndexBatch.Delete instead. IndexBatch.New を呼び出すことによって 1 つのバッチでさまざまな操作を組み合わせることもできます。これによって、IndexAction オブジェクトのコレクションを受け取り、各オブジェクトがドキュメントで特定の操作を実行するよう Azure Search に指示します。You can also mix different operations in a single batch by calling IndexBatch.New, which takes a collection of IndexAction objects, each of which tells Azure Search to perform a particular operation on a document. IndexAction.MergeIndexAction.Upload などの対応するメソッドを呼び出すことによって、独自の操作を行う IndexAction を作成できます。You can create each IndexAction with its own operation by calling the corresponding method such as IndexAction.Merge, IndexAction.Upload, and so on.

このメソッドの 3 番目の部分は、インデックス作成の重要なエラー ケースを処理する catch ブロックです。The third part of this method is a catch block that handles an important error case for indexing. Azure Search がバッチ内の一部のドキュメントのインデックス作成に失敗した場合、Documents.IndexIndexBatchException をスローします。If your Azure Search service fails to index some of the documents in the batch, an IndexBatchException is thrown by Documents.Index. この例外は、サービスの負荷が高いときにドキュメントのインデックスを作成していると発生する場合があります。This exception can happen if you are indexing documents while your service is under heavy load. コードでこのケースを明示的に処理することを強くお勧めします。We strongly recommend explicitly handling this case in your code. しばらく待ってから失敗したドキュメントのインデックス作成を再試行したり、サンプルと同じようにログに記録してから続けることができます。または、アプリケーションのデータ整合性要件に応じて他の処理を行うこともできます。You can delay and then retry indexing the documents that failed, or you can log and continue like the sample does, or you can do something else depending on your application's data consistency requirements.

注意

FindFailedActionsToRetry メソッドを使用して、Index の前回の呼び出しで失敗したアクションだけを含む新しいバッチを作成できます。You can use the FindFailedActionsToRetry method to construct a new batch containing only the actions that failed in a previous call to Index. このメソッドの適切な使用方法については、StackOverflow をご覧ください。There is a discussion of how to properly use it on StackOverflow.

最後に、UploadDocuments メソッドは 2 秒間遅延します。Finally, the UploadDocuments method delays for two seconds. インデックスの作成は Azure Search サービスで非同期的に行われるので、サンプル アプリケーションは短い時間待機して、確実にドキュメントを検索に使用できるようにする必要があります。Indexing happens asynchronously in your Azure Search service, so the sample application needs to wait a short time to ensure that the documents are available for searching. 通常、このような遅延は、デモ、テスト、およびサンプル アプリケーションでのみ必要です。Delays like this are typically only necessary in demos, tests, and sample applications.

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

Azure Search .NET SDK が Hotel のようなユーザー定義クラスのインスタンスをどのようにしてインデックスにアップロードできるのか不思議に思われるかもしれません。You may be wondering how the Azure Search .NET SDK is able to upload instances of a user-defined class like Hotel to the index. その質問に答えるため、 Hotel クラスを見ていくことにします。To help answer that question, let's look at the Hotel class:

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 クラス内の各パブリック プロパティの名前がインデックス定義内の同じ名前でフィールドにマップされることです。The first thing to notice is that the name of each public property in the Hotel class will map to a field with the same name in the index definition. 各フィールドを小文字で始める場合 ("camel case")、そのクラス上の [SerializePropertyNamesAsCamelCase] 属性を使用してプロパティ名を自動的に camel-case にマップするよう、SDK に指示することができます。If you would like each field to start with a lower-case letter ("camel case"), you can tell the SDK to map the property names to camel-case automatically with the [SerializePropertyNamesAsCamelCase] attribute on the class. このシナリオは、ターゲット スキーマがアプリケーション開発者の制御外にあるときに、.NET の "パスカル ケース" 命名に関するガイドラインに違反することなくデータ バインドを実行する .NET アプリケーションでは一般的です。This scenario is common in .NET applications that perform data-binding where the target schema is outside the control of the application developer without having to violate the "Pascal case" naming guidelines in .NET.

注意

Azure Search .NET SDK は、 NewtonSoft JSON.NET ライブラリを使用して、カスタムのモデル オブジェクトから JSON 形式へのシリアル化や JSON 形式からの逆シリアル化を行います。The Azure Search .NET SDK uses the NewtonSoft JSON.NET library to serialize and deserialize your custom model objects to and from JSON. 必要に応じてこのシリアル化をカスタマイズできます。You can customize this serialization if needed. 詳細については、「JSON.NET 使用したシリアル化のカスタマイズ」をご覧ください。For more information, see Custom Serialization with JSON.NET.

次に注目すべき点は、各プロパティが IsFilterableIsSearchableKeyAnalyzer などの属性で装飾されていることです。The second thing to notice is each property is decorated with attributes such as IsFilterable, IsSearchable, Key, and Analyzer. これらの属性は、Azure Search インデックス内の対応するフィールド属性に直接マップされます。These attributes map directly to the corresponding field attributes in an Azure Search index. FieldBuilder クラスは、これらのプロパティを使用してインデックスのフィールド定義を構築します。The FieldBuilder class uses these properties to construct field definitions for the index.

Hotel クラスに関する 3 番目の重要な点は、パブリック プロパティのデータ型です。The third important thing about the Hotel class is the data types of the public properties. これらのプロパティの .NET 型は、インデックス定義でそれらと同等のフィールド型にマップします。The .NET types of these properties map to their equivalent field types in the index definition. たとえば、Category 文字列プロパティは、Edm.String 型の category フィールドにマップします。For example, the Category string property maps to the category field, which is of type Edm.String. bool?Edm.BooleanDateTimeOffset?Edm.DateTimeOffset などの間にも、同じような型のマッピングがあります。There are similar type mappings between bool?, Edm.Boolean, DateTimeOffset?, and Edm.DateTimeOffset and so on. 型のマッピングの具体的なルールについては、Azure Search .NET SDK リファレンスDocuments.Get メソッドを参照してください。The specific rules for the type mapping are documented with the Documents.Get method in the Azure Search .NET SDK reference. FieldBuilder クラスは、このマッピングに自動的に対処しますが、シリアル化の問題のトラブルシューティングを行う必要がある場合に、マッピングを理解しておくと役立ちます。The FieldBuilder class takes care of this mapping for you, but it can still be helpful to understand in case you need to troubleshoot any serialization issues.

SmokingAllowed プロパティを見つけてしまいましたか?Did you happen to notice the SmokingAllowed property?

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

このプロパティの JsonIgnore 属性によって、フィールドとしてインデックスにシリアル化しないよう、FieldBuilder に指示が与えられます。The JsonIgnore attribute on this property tells the FieldBuilder to not serialize it to the index as a field. これは、ご利用のアプリケーションでヘルパーとして使用できる、クライアント側の計算されたプロパティを作成する優れた方法です。This is a great way to create client-side calculated properties you can use as helpers in your application. この場合、SmokingAllowed プロパティは、Rooms コレクション内に喫煙可能な Room があるかどうかを反映します。In this case, the SmokingAllowed property reflects whether any Room in the Rooms collection allows smoking. すべて false の場合、ホテル全館で喫煙できないことを示します。If all are false, it indicates that the entire hotel does not allow smoking.

AddressRooms など、いくつかのプロパティは、.NET クラスのインスタンスです。Some properties such as Address and Rooms are instances of .NET classes. これらのプロパティは、さらに複雑なデータ構造を表し、インデックス内で複合データ型を備えたフィールドを必要とします。These properties represent more complex data structures and, as a result, require fields with a complex data type in the index.

Address プロパティは、以下に定義された Address クラス内の複数の値のセットを表します。The Address property represents a set of multiple values in the Address class, defined below:

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; }
    }
}

このクラスには、米国またはカナダで住所を記述するために使用される標準的な値が含まれます。This class contains the standard values used to describe addresses in the United States or Canada. インデックス内で論理フィールドをグループ化するには、このような型を使用できます。You can use types like this to group logical fields together in the index.

Rooms プロパティは、Room オブジェクトの配列を表します。The Rooms property represents an array of Room objects:

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 のデータ モデルおよびその対応するインデックス スキーマは、エンド ユーザーに提供する検索エクスペリエンスをサポートするように設計されなくてはなりません。Your data model in .NET and its corresponding index schema should be designed to support the search experience you'd like to give to your end user. .NET の最上位レベル オブジェクト、すなわちインデックス内のドキュメントはそれぞれ、ユーザー インターフェイスで示される検索結果に対応します。Each top level object in .NET, ie document in the index, corresponds to a search result you would present in your user interface. たとえば、ホテル検索アプリケーションで、エンド ユーザーは、ホテル名、ホテルの機能、または特定の部屋の特徴で検索することができます。For example, in a hotel search application your end users may want to search by hotel name, features of the hotel, or the characteristics of a particular room. 後でクエリの例をいくつか説明します。We'll cover some query examples a little later.

インデックス内でドキュメントの操作を行うために独自のクラスを使用するこの機能は、次のセクションで説明するように、検索結果の取得、および SDK による任意の型への自動逆シリアル化という両方向で動作します。This ability to use your own classes to interact with documents in the index works in both directions; You can also retrieve search results and have the SDK automatically deserialize them to a type of your choice, as we will see in the next section.

注意

Azure Search .NET SDK は、Document クラスを使用して動的に型指定されたドキュメントもサポートします。これは、フィールドの値に対するフィールド名のキー/値マッピングです。The Azure Search .NET SDK also supports dynamically-typed documents using the Document class, which is a key/value mapping of field names to field values. この機能は、設計時にインデックス スキーマがわからない場合、または特定のモデル クラスにバインドすると不都合な場合に便利です。This is useful in scenarios where you don't know the index schema at design-time, or where it would be inconvenient to bind to specific model classes. ドキュメントを処理する SDK のすべてのメソッドには、Document クラスを使用するオーバーロード、およびジェネリック型パラメーターを使用する厳密な型指定のオーバーロードがあります。All the methods in the SDK that deal with documents have overloads that work with the Document class, as well as strongly-typed overloads that take a generic type parameter. このチュートリアルのサンプル コードでは、後者のみを使用しています。Only the latter are used in the sample code in this tutorial. Document クラスDictionary<string, object> から継承します。The Document class inherits from Dictionary<string, object>.

null 許容のデータ型を使用する理由Why you should use nullable data types

Azure Search インデックスにマップする独自のモデル クラスを設計するときは、boolint などの値型のプロパティを null 許容型として宣言することをお勧めします (たとえば、bool ではなく bool? を使用する)。When designing your own model classes to map to an Azure Search index, we recommend declaring properties of value types such as bool and int to be nullable (for example, bool? instead of bool). null 非許容プロパティを使用する場合、対応するフィールドに null 値が含まれるドキュメントがインデックス内に存在しないことを、開発者が 保証する 必要があります。If you use a non-nullable property, you have to guarantee that no documents in your index contain a null value for the corresponding field. SDK または Azure Search サービスで、これを強制することはできません。Neither the SDK nor the Azure Search service will help you to enforce this.

これは単なる仮定上の問題ではありません。Edm.Int32 型の既存のインデックスに新しいフィールドを追加する場合を考えてみてください。This is not just a hypothetical concern: Imagine a scenario where you add a new field to an existing index that is of type Edm.Int32. インデックスの定義を更新した後、(Azure Search ではすべての型が null を許容するので) すべてのドキュメントでその新しいフィールドの値が null になります。After updating the index definition, all documents will have a null value for that new field (since all types are nullable in Azure Search). その後、そのフィールドが null 非許容型の int プロパティであるモデル クラスを使用した場合、ドキュメントを取得しようとすると、次のような JsonSerializationException が発生します。If you then use a model class with a non-nullable int property for that field, you will get a JsonSerializationException like this when trying to retrieve documents:

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

このため、ベスト プラクティスとして、モデル クラスでは null 許容型を使用することをお勧めします。For this reason, we recommend that you use nullable types in your model classes as a best practice.

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

SDK では、ドキュメントのシリアル化と逆シリアル化に JSON.NET を使用します。The SDK uses JSON.NET for serializing and deserializing documents. 独自の JsonConverter または IContractResolver を定義することによって、必要に応じてシリアル化と逆シリアル化をカスタマイズできます。You can customize serialization and deserialization if needed by defining your own JsonConverter or IContractResolver. 詳細については、JSON.NET のドキュメントを参照してください。For more information, see the JSON.NET documentation. この機能は、アプリケーションの既存のモデル クラスを Azure Search 用に適合させる場合、およびその他の高度なシナリオに役立ちます。This can be useful when you want to adapt an existing model class from your application for use with Azure Search, and other more advanced scenarios. たとえば、カスタム シリアル化を使用すると次のことが可能です。For example, with custom serialization you can:

  • ドキュメント フィールドとして格納されるものに、モデル クラスの特定のプロパティを含める、または除外する。Include or exclude certain properties of your model class from being stored as document fields.
  • コードのプロパティ名とインデックスのフィールド名をマップする。Map between property names in your code and field names in your index.
  • ドキュメント フィールドへのプロパティのマッピングに使用できるカスタム属性を作成する。Create custom attributes that can be used for mapping properties to document fields.

Azure Search .NET SDK のユニット テストにカスタム シリアル化を実装する例については、GitHub を参照してください。You can find examples of implementing custom serialization in the unit tests for the Azure Search .NET SDK on GitHub. 手始めとしては、このフォルダーが適しています。A good starting point is this folder. カスタム シリアル化のテストに使用されるクラスが含まれます。It contains classes that are used by the custom serialization tests.

インデックス内のドキュメントの検索Searching for documents in the index

サンプル アプリケーションの最後の手順として、インデックス内のいくつかのドキュメントを検索します。The last step in the sample application is to search for some documents in the index:

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 オブジェクトを作成します。Each time it executes a query, this method first creates a new SearchParameters object. このオブジェクトは、並べ替え、フィルター処理、ページング、ファセットなどの、クエリへの追加オプションを指定するために使用されます。This object is used to specify additional options for the query such as sorting, filtering, paging, and faceting. このメソッドでは、さまざまなクエリの FilterSelectOrderByTop の各プロパティを設定します。In this method, we're setting the Filter, Select, OrderBy, and Top property for different queries. SearchParameters のすべてのプロパティについては、こちらをご覧ください。All the SearchParameters properties are documented here.

次の手順では、検索クエリを実際に実行します。The next step is to actually execute the search query. この検索は、Documents.Search メソッドを使用して実行されます。Running the search is done using the Documents.Search method. 各クエリでは、使用する検索テキストを文字列として渡し (検索テキストがない場合は "*")、以前に作成した検索パラメーターも渡します。For each query, we pass the search text to use as a string (or "*" if there is no search text), plus the search parameters created earlier. また、Documents.Search に対する型パラメーターとして Hotel も指定します。これは、検索結果のドキュメントを Hotel 型のオブジェクトに逆シリアル化するように SDK に指示します。We also specify Hotel as the type parameter for Documents.Search, which tells the SDK to deserialize documents in the search results into objects of type Hotel.

注意

検索クエリ式の構文の詳細については、こちらをご覧ください。You can find more information about the search query expression syntax here.

最後に、各クエリの実行後、このメソッドは検索結果のすべての一致を反復処理し、各ドキュメントをコンソールに出力します。Finally, after each query this method iterates through all the matches in the search results, printing each document to the console:

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

    Console.WriteLine();
}

各クエリについて詳しく見ていきましょう。Let's take a closer look at each of the queries in turn. 最初のクエリを実行するコードを次に示します。Here is the code to execute the first query:

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

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

WriteDocuments(results);

この例では、検索可能なあらゆるフィールドで "motel" という単語のインデックス全体を検索し、Select パラメーターによって指定されるとおり、ホテル名だけを返します。In this case, we're searching the entire index for the word "motel" in any searchable field and we only want to retrieve the hotel names, as specified by the Select parameter. 結果は次のとおりです。Here are the results:

Name: Secret Point Motel

Name: Twin Dome Motel

次のクエリは、さらに興味深いものです。The next query is a little more interesting. 1 泊 100 ドル未満の部屋があるホテルを検索し、ホテル ID と説明だけを返します。We want to find any hotels that have a room with a nightly rate of less than $100 and return only the hotel ID and description:

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)) を使用して、インデックス内のドキュメントをフィルター処理します。This query uses an OData $filter expression, Rooms/any(r: r/BaseRate lt 100), to filter the documents in the index. ここでは、あらゆる演算子を使用して、'BaseRate lt 100' をルーム コレクションのすべての項目に適用します。This uses the any operator to apply the 'BaseRate lt 100' to every item in the Rooms collection. Azure Search がサポートする OData 構文の詳細については、 こちらを参照してください。You can find out more about the OData syntax that Azure Search supports here.

クエリの結果は次のとおりです。Here are the results of the query:

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 つのホテルを検索し、ホテル名と最終改装日を表示します。Next, we want to find the top two hotels that have been most recently renovated, and show the hotel name and last renovation date. 次にコードを示します。Here is the code:

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 を指定しています。In this case, we again use OData syntax to specify the OrderBy parameter as lastRenovationDate desc. また、上位 2 つのドキュメントだけを取得できるように、Top を 2 に設定しています。We also set Top to 2 to ensure we only get the top two documents. 前と同様に、Select を設定して返す必要があるフィールドを指定します。As before, we set Select to specify which fields should be returned.

結果は次のとおりです。Here are the results:

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" という単語に一致するすべてのホテル名を検索します。Finally, we want to find all hotels names that match the word "hotel":

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

WriteDocuments(results);

結果は次のとおりです。Select プロパティを指定しなかったので、この結果にはすべてのフィールドが含まれています。And here are the results, which include all fields since we did not specify the Select property:

HotelId: 3
Name: Triple Landscape Hotel
...

チュートリアルはここまでですが、ここで止めないでください。This step completes the tutorial, but don't stop here. **次のステップでは、Azure Search をさらに学習するための他のリソースを提供します。**Next steps provide additional resources for learning more about Azure Search.

次の手順Next steps