다음을 통해 공유


C# .NET 애플리케이션에서 Microsoft.Azure.Search를 사용하는 방법

이 문서에서는 .NET용 Azure SDK에서 C# 및 레거시 클라이언트 라이브러리인 Microsoft.Azure.Search (버전 10)를 사용하여 검색 개체를 만들고 관리하는 방법을 설명합니다.

버전 10은 Microsoft.Azure.Search 패키지의 마지막 버전입니다. 앞으로 Azure SDK 팀의 Azure.Search.Documents 에 새로운 기능이 출시될 예정입니다.

비고

기존 또는 진행 중인 개발 프로젝트가 있는 경우 버전 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 라이브러리에 필요한 일반적인 형식입니다. 애플리케이션에서 이 패키지를 직접 사용할 필요가 없습니다. 종속성으로만 사용됩니다.

다양한 클라이언트 라이브러리는 Index, Field, 그리고 Document와 같은 클래스뿐만 아니라 Indexes.CreateDocuments.Search 클래스에 대한 SearchServiceClientSearchIndexClient와 같은 작업도 정의합니다. 이러한 클래스는 다음과 같은 네임 스페이스에 구성됩니다.

향후 SDK 업데이트에 대한 피드백을 제공하려면 피드백 페이지를 참조하거나 GitHub 에서 문제를 만들고 문제 제목에 "Azure Cognitive Search"를 언급하세요.

.NET SDK는 Azure Cognitive Search REST API 버전을2019-05-06 대상으로 합니다. 이 버전에는 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를 사용하려면 서비스 이름과 하나 이상의 API 키가 필요합니다. 포털에서 서비스를 만들 면 이러한 단계를 수행하는 데 도움이 됩니다.
  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();
}

비고

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

비고

잘못된 키(예: 관리자 키가 필요한 쿼리 키)SearchServiceClient를 제공하는 경우 작업 메서드CloudException를 처음 호출할 때 "금지됨"이라는 오류 메시지가 표시됩니다Indexes.Create. 만약 이런 일이 발생한다면, 우리 API 키를 다시 확인하세요.

다음 몇 줄은 메서드를 호출하여 "hotels"라는 인덱스를 만들고, 인덱스가 이미 있는 경우 먼저 삭제합니다. 잠시 후에 이러한 방법을 살펴보겠습니다.

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

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

다음으로 인덱스를 채워야 합니다. 인덱스를 채우려면 우리는 SearchIndexClient이 필요합니다. 하나를 얻는 방법에는 두 가지가 있습니다: 생성하거나, Indexes.GetClient에서 SearchServiceClient을(를) 호출하는 것입니다. 편의를 위해 후자를 사용합니다.

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

비고

일반적인 검색 애플리케이션에서 인덱스 관리 및 채우기는 검색 쿼리와는 별도의 구성 요소에서 처리할 수 있습니다. 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 인덱스를 사용하여 인덱스가 있는지 확인하고 있는 경우 삭제합니다.

비고

이 문서의 예제 코드는 간단히 하기 위해 Azure Cognitive Search .NET SDK의 동기 메서드를 사용합니다. 사용자 고유의 애플리케이션에서 비동기 메서드를 사용하여 확장성과 응답성을 유지하는 것이 좋습니다. 예를 들어, 위의 메서드에서 ExistsAsyncDeleteAsync 대신 ExistsDelete를 사용할 수 있습니다.

다음으로, Main 이 메서드를 호출하여 새 "hotels" 인덱스 만들기:

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

이 메서드는 새 Index 인덱스의 Field 스키마를 정의하는 개체 목록을 사용하여 새 개체를 만듭니다. 각 필드에는 이름, 데이터 유형, 그리고 검색 동작을 정의하는 몇 가지 특성이 있습니다. FieldBuilder 클래스는 리플렉션을 사용하여 지정된 Field 모델 클래스의 public 속성 및 특성을 검사하고 인덱스에 대한 Hotel 개체 목록을 만듭니다. Hotel 클래스에 대해서는 이후에 좀 더 자세히 알아보겠습니다.

비고

필요한 경우 Field를 사용하는 대신, FieldBuilder 개체 목록을 항상 직접 만들 수 있습니다. 예를 들어 모델 클래스를 사용하지 않으려는 경우 또는 특성을 추가하여 수정하지 않으려는 기존 모델 클래스를 사용해야 할 수 있습니다.

필드 외에도 점수 매기기 프로필, 제안기 또는 CORS 옵션을 인덱스에 추가할 수도 있습니다(이러한 매개 변수는 간단히 하기 위해 샘플에서 생략됨). 인덱스 개체 및 해당 구성 요소에 대한 자세한 내용은 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);
}

이 메서드는 네 부분으로 이루어져 있습니다. 첫 번째는 인덱스로 업로드할 입력 데이터 역할을 하는 개체 3개와 함께 각각 3 HotelRoom 개의 개체 배열을 만듭니다. 이 데이터는 간단히 하기 위해 하드 코딩합니다. 사용자 고유의 애플리케이션에서 데이터는 SQL 데이터베이스와 같은 외부 데이터 원본에서 나올 수 있습니다.

두 번째 부분은 문서를 포함하는 IndexBatch를 만듭니다. 배치를 만들 때 배치에 적용할 작업을 지정합니다. 이 경우에는 IndexBatch.Upload를 호출하여 지정합니다. 그런 다음, 배치가 Documents.Index 메서드에 의해 Azure Cognitive Search 인덱스로 업로드됩니다.

비고

이 예제에서는 문서를 업로드하기만 하면됩니다. 변경 사항을 기존 문서에 병합하거나 문서를 삭제하려면 IndexBatch.Merge, IndexBatch.MergeOrUpload 또는 IndexBatch.Delete를 호출하여 배치를 만듭니다. IndexBatch.New를 호출하여 서로 다른 여러 작업을 단일 일괄 처리로 혼합할 수도 있습니다. 이는 각 IndexAction 개체가 Azure Cognitive Search에 문서에서 특정 작업을 수행하도록 지시하는 컬렉션입니다. 각 IndexAction을 자체 작업으로 생성하려면 IndexAction.Merge, IndexAction.Upload 등과 같은 해당 메서드를 호출할 수 있습니다.

이 메서드의 세 번째 부분은 인덱싱에 중요한 오류 사례를 처리하는 catch 블록입니다. Azure Cognitive Search 서비스가 일괄 처리 중 일부 문서를 인덱싱하지 못하면 IndexBatchException에서 Documents.Index이(가) 발생합니다. 이 예외는 서비스가 부하가 많은 동안 문서를 인덱싱하는 경우에 발생할 수 있습니다. 이 경우 코드에서 명시적으로 처리하는 것이 좋습니다. 실패한 문서 인덱싱을 잠시 후 다시 시도하거나, 샘플에서 하던 것처럼 기록하여 계속하거나, 애플리케이션의 데이터 일관성 요구 사항에 따라 다른 작업을 수행할 수 있습니다.

비고

이 메서드를 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; }
}

가장 먼저 알 수 있는 것은 클래스의 각 public 속성 Hotel 이름이 인덱스 정의에서 이름이 같은 필드에 매핑된다는 것입니다. 각 필드가 소문자("카멜 케이스")로 시작하도록 하려면 속성 이름을 클래스의 특성과 함께 [SerializePropertyNamesAsCamelCase] 카멜 케이스에 자동으로 매핑하도록 SDK에 지시할 수 있습니다. 이 시나리오는 .NET에서 "Pascal 사례" 명명 지침을 위반하지 않고도 대상 스키마가 애플리케이션 개발자의 제어 범위를 벗어나는 데이터 바인딩을 수행하는 .NET 애플리케이션에서 일반적입니다.

비고

Azure Cognitive Search .NET SDK는 NewtonSoft JSON.NET 라이브러리를 사용하여 JSON과 사용자 지정 모델 개체를 직렬화하고 역직렬화합니다. 필요한 경우 이 serialization을 사용자 지정할 수 있습니다. 자세한 내용은 JSON.NET 사용하여 사용자 지정 Serialization을 참조하세요.

두 번째로 주목해야 할 점은 각 속성이 IsFilterable, IsSearchable, Key, 및 Analyzer와 같은 특성으로 데코레이팅되어 있다는 것입니다. 이러한 특성은 Azure Cognitive Search 인덱스 내 해당 필드 특성에 직접 매핑합니다. FieldBuilder 클래스는 이러한 속성을 사용하여 인덱스에 대한 필드 정의를 생성합니다.

클래스의 Hotel 세 번째 중요한 점은 public 속성의 데이터 형식입니다. 이러한 속성의 .NET 형식은 인덱스 정의의 해당 필드 형식에 매핑됩니다. 예를 들어, Category 문자열 속성은 category 유형인 Edm.String 필드에 매핑됩니다. 유사한 유형 매핑이 bool?, Edm.Boolean, DateTimeOffset?, 그리고 Edm.DateTimeOffset 등의 사이에 있습니다. 형식 매핑에 대한 특정 규칙은 Azure Cognitive Search .NET SDK 참조에서 Documents.Get 메서드로 설명됩니다. FieldBuilder 클래스는 이 매핑을 처리해주지만, serialization 문제를 해결해야 할 경우를 대비해 이해하는 것이 여전히 도움이 될 수 있습니다.

SmokingAllowed 속성을 확인했나요?

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

이 속성의 JsonIgnore 특성은 FieldBuilder 에 이 속성을 인덱스에 필드로 직렬화하지 않도록 지시합니다. 이는 애플리케이션에서 도우미로 사용할 수 있는 클라이언트 쪽 계산 속성을 만드는 좋은 방법입니다. 이 경우 SmokingAllowed 컬렉션에 있는 Room 중에서 어느 것이 흡연을 허용하는지를 Rooms 속성이 반영합니다. 모두 거짓이면 호텔 전체가 흡연을 허용하지 않음을 나타냅니다.

일부 속성, 예를 들어 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가 원하는 형식으로 자동으로 역직렬화하도록 할 수도 있습니다.

비고

또한 Azure Cognitive Search .NET SDK는 필드 이름과 필드 값의 키/값 매핑인 클래스를 사용하여 Document 동적으로 형식화된 문서를 지원합니다. 디자인 타임에 인덱스 스키마를 모르거나 특정 모델 클래스에 바인딩하는 것이 불편한 시나리오에서 유용합니다. SDK의 문서를 처리하는 모든 메서드에는 Document 클래스와 함께 작동하는 오버로드 및 제네릭 유형 매개변수를 사용하는 강력한 형식의 오버로드가 있습니다. 이 자습서의 샘플 코드에서는 후자만 사용됩니다. 클래스는 Document .에서 Dictionary<string, object>상속됩니다.

nullable 데이터 형식을 사용해야 하는 이유

Azure Cognitive Search 인덱스에 매핑하기 위해 사용자 고유의 모델 클래스를 디자인할 때, boolint와 같은 값 형식의 속성은 null 허용으로 선언(bool? 대신 bool)할 것을 권장합니다. nullable이 아닌 속성을 사용하는 경우 인덱스 내의 문서에 해당 필드의 null 값이 포함되어 있지 않은지 보장 해야 합니다. SDK나 Azure Cognitive Search 서비스 모두 이를 적용하는 데 도움이 되지 않습니다.

이것은 단지 가상의 문제가 아닙니다. 형식 Edm.Int32인 기존 인덱스로 새 필드를 추가하는 시나리오를 가정해 보세요. 인덱스 정의를 업데이트한 후 모든 문서에는 해당 새 필드에 대한 null 값이 있습니다(모든 형식은 Azure Cognitive Search에서 null을 허용하기 때문에). 해당 필드에서 nullable이 아닌 int 속성을 사용하는 모델 클래스를 사용하면, 문서를 검색하려고 할 때 다음과 같은 JsonSerializationException을 볼 수 있습니다.

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

이러한 이유로 모델 클래스에서 nullable 형식을 모범 사례로 사용하는 것이 좋습니다.

JSON.NET을 활용한 사용자 지정 직렬화

SDK는 JSON.NET 사용하여 문서를 직렬화하고 역직렬화합니다. 필요한 경우 JsonConverter 또는 IContractResolver를 정의하여 serialization 및 deserialization을 사용자 정의할 수 있습니다. 자세한 내용은 JSON.NET 설명서를 참조하세요. 이 기능은 Azure Cognitive Search 및 기타 고급 시나리오에서 사용할 애플리케이션의 기존 모델 클래스를 조정하려는 경우에 유용할 수 있습니다. 예를 들어 사용자 지정 serialization을 사용하면 다음을 수행할 수 있습니다.

  • 모델 클래스의 특정 속성을 문서 필드로 저장하지 않도록 포함하거나 제외합니다.
  • 코드의 속성 이름과 인덱스 필드 이름 간에 매핑합니다.
  • 문서 필드에 속성을 매핑하는 데 사용할 수 있는 사용자 지정 특성을 만듭니다.

GitHub의 Azure Cognitive Search .NET SDK에 대한 단위 테스트에서 사용자 지정 serialization을 구현하는 예제를 찾을 수 있습니다. 좋은 시작점은 이 폴더입니다. 여기에는 사용자 지정 serialization 테스트에서 사용되는 클래스가 포함됩니다.

인덱스에서 문서 검색

샘플 애플리케이션의 마지막 단계는 인덱스 내의 일부 문서를 검색하는 것입니다.

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 개체를 만듭니다. 이 개체는 정렬, 필터링, 페이징, 패시팅 같은 쿼리에 대한 추가 옵션을 지정하는 데 사용됩니다. 이 메서드에서는 서로 다른 쿼리에 대해 Filter, Select, OrderBy, 및 Top 속성을 설정합니다. 모든 속성은 SearchParameters 여기에 설명되어 있습니다.

다음 단계는 검색 쿼리를 실제로 실행하는 것입니다. 검색은 Documents.Search 메서드를 사용하여 실행됩니다. 각 쿼리에 대해 문자열로 사용할 검색 텍스트(또는 "*" 검색 텍스트가 없는 경우)와 이전에 만든 검색 매개 변수를 전달합니다. 또한 SDK에 검색 결과 내 문서를 Hotel 유형의 개체로 역직렬화하도록 명령하는 Documents.Search에 대한 유형 매개 변수로 Hotel을(를) 지정할 수도 있습니다.

비고

검색 쿼리 식 구문에 대한 자세한 내용은 여기에서 확인할 수 있습니다.

마지막으로 각 쿼리 후에 이 메서드는 검색 결과의 모든 일치 항목을 반복하여 각 문서를 콘솔에 인쇄합니다.

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

다음 쿼리는 좀 더 흥미롭습니다. 야간 요금이 $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 $filterRooms/any(r: r/BaseRate lt 100)을 사용하여 인덱스의 문서를 필터링합니다. 이 경우 모든 연산 자를 사용하여 Rooms 컬렉션의 모든 항목에 '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개 문서만 가져오기 위해 2로 설정 Top 했습니다. 이전과 마찬가지로 반환할 필드를 지정하도록 설정 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에 대해 자세히 알아보기 위한 추가 리소스를 제공합니다.

다음 단계