Udostępnij za pośrednictwem


Jak używać usługi Microsoft.Azure.Search w aplikacji .NET w języku C#

W tym artykule wyjaśniono, jak tworzyć obiekty wyszukiwania i zarządzać nimi przy użyciu języka C# i starszej biblioteki klienta Microsoft.Azure.Search (wersja 10) w zestawie Azure SDK dla platformy .NET.

Wersja 10 to ostatnia wersja pakietu Microsoft.Azure.Search. W przyszłości nowe funkcje zostaną wdrożone w witrynie Azure.Search.Documents od zespołu zestawu Azure SDK.

Uwaga

Jeśli masz istniejące lub wlotowe projekty programistyczne, możesz nadal używać wersji 10. W przypadku nowych projektów lub używania nowych funkcji należy przejść do nowej biblioteki.

Informacje o wersji 10

Zestaw SDK składa się z kilku bibliotek klienckich, które umożliwiają zarządzanie indeksami, źródłami danych, indeksatorami i mapami synonimów, a także przekazywanie dokumentów i zarządzanie nimi oraz wykonywanie zapytań bez konieczności obsługi szczegółów protokołu HTTP i JSON. Te biblioteki klienckie są dystrybuowane jako pakiety NuGet.

Głównym pakietem NuGet jest Microsoft.Azure.Search, który jest meta-pakietem zawierającym wszystkie inne pakiety jako zależności. Użyj tego pakietu, jeśli dopiero zaczynasz pracę lub wiesz, że aplikacja będzie potrzebować wszystkich funkcji Azure Cognitive Search.

Inne pakiety NuGet w zestawie SDK to:

  • Microsoft.Azure.Search.Data: użyj tego pakietu, jeśli tworzysz aplikację platformy .NET przy użyciu Azure Cognitive Search i musisz wykonywać zapytania o dokumenty lub aktualizować je tylko w indeksach. Jeśli musisz również utworzyć lub zaktualizować indeksy, mapy synonimów lub inne zasoby na poziomie usługi, użyj Microsoft.Azure.Search pakietu.
  • Microsoft.Azure.Search.Service: użyj tego pakietu, jeśli tworzysz automatyzację na platformie .NET, aby zarządzać indeksami Azure Cognitive Search, mapami synonimów, indeksatorami, źródłami danych lub innymi zasobami na poziomie usługi. Jeśli musisz wykonywać zapytania o dokumenty lub aktualizować je tylko w indeksach, użyj Microsoft.Azure.Search.Data pakietu. Jeśli potrzebujesz wszystkich funkcji Azure Cognitive Search, użyj Microsoft.Azure.Search pakietu.
  • Microsoft.Azure.Search.Common: typowe typy wymagane przez biblioteki platformy .NET Azure Cognitive Search. Nie musisz używać tego pakietu bezpośrednio w aplikacji. Ma być używana tylko jako zależność.

Różne biblioteki klienckie definiują klasy, takie jak Index, Fieldi Document, oraz operacje, takie jak Indexes.Create i Documents.Search w SearchServiceClient klasach i SearchIndexClient . Te klasy są zorganizowane w następujące przestrzenie nazw:

Jeśli chcesz przekazać opinię na temat przyszłej aktualizacji zestawu SDK, zobacz naszą stronę opinii lub utwórz problem w GitHub i podaj nazwę "Azure Cognitive Search" w tytule problemu.

Zestaw SDK platformy .NET jest przeznaczony dla wersji 2019-05-06 interfejsu API REST Azure Cognitive Search. Ta wersja obejmuje obsługę typów złożonych, wzbogacania sztucznej inteligencji, autouzupełniania i analizowania JsonLines podczas indeksowania obiektów blob platformy Azure.

Ten zestaw SDK nie obsługuje operacji zarządzania , takich jak tworzenie i skalowanie usług wyszukiwania oraz zarządzanie kluczami interfejsu API. Jeśli musisz zarządzać zasobami wyszukiwania z poziomu aplikacji .NET, możesz użyć zestawu SDK zarządzania platformy .NET Azure Cognitive Search.

Uaktualnianie do wersji 10

Jeśli używasz już starszej wersji zestawu SDK platformy .NET Azure Cognitive Search i chcesz uaktualnić do najnowszej ogólnie dostępnej wersji, w tym artykule wyjaśniono, jak to zrobić.

Wymagania dotyczące zestawu SDK

  1. Visual Studio 2017 lub nowszym.
  2. Własna usługa Azure Cognitive Search. Aby użyć zestawu SDK, potrzebna będzie nazwa usługi i co najmniej jeden klucz interfejsu API. Utworzenie usługi w portalu ułatwi ci wykonanie tych kroków.
  3. Pobierz pakiet Azure Cognitive Search zestawu SDK platformy .NET NuGet przy użyciu polecenia "Zarządzanie pakietami NuGet" w Visual Studio. Wystarczy wyszukać nazwę Microsoft.Azure.Search pakietu w witrynie NuGet.org (lub jednej z innych nazw pakietów powyżej, jeśli potrzebujesz tylko podzestawu funkcji).

Zestaw SDK platformy .NET Azure Cognitive Search obsługuje aplikacje przeznaczone dla .NET Framework 4.5.2 i nowszych, a także platformę .NET Core 2.0 i nowsze.

Podstawowe scenariusze

Istnieje kilka czynności, które należy wykonać w aplikacji wyszukiwania. W tym samouczku omówimy następujące podstawowe scenariusze:

  • Tworzenie indeksu
  • Wypełnianie indeksu dokumentami
  • Wyszukiwanie dokumentów przy użyciu wyszukiwania pełnotekstowego i filtrów

Poniższy przykładowy kod ilustruje każdy z tych scenariuszy. Możesz użyć fragmentów kodu we własnej aplikacji.

Omówienie

Przykładowa aplikacja, z której będziemy korzystać, tworzy nowy indeks o nazwie "hotels", wypełnia go kilkoma dokumentami, a następnie wykonuje niektóre zapytania wyszukiwania. Oto główny program przedstawiający ogólny przepływ:

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

Uwaga

Możesz znaleźć pełny kod źródłowy przykładowej aplikacji używanej w tym poradniku w portalu GitHub.

Omówimy ten krok po kroku. Najpierw musimy utworzyć nowy SearchServiceClientelement . Ten obiekt umożliwia zarządzanie indeksami. Aby je skonstruować, musisz podać nazwę usługi Azure Cognitive Search oraz klucz interfejsu API administratora. Te informacje można wprowadzić w appsettings.json pliku przykładowej aplikacji.

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

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

Uwaga

Jeśli podasz nieprawidłowy klucz (na przykład klucz zapytania, w którym był wymagany klucz administracyjny), SearchServiceClient zostanie wyświetlony CloudException komunikat o błędzie "Zabronione" przy pierwszym wywołaniu metody operacji, takiej jak Indexes.Create. Jeśli tak się stanie, sprawdź dwukrotnie klucz interfejsu API.

Kilka następnych wierszy wywołuje metody tworzenia indeksu o nazwie "hotels", usuwając go najpierw, jeśli już istnieje. Omówimy te metody nieco później.

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

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

Następnie należy wypełnić indeks. Aby wypełnić indeks, będziemy potrzebować elementu SearchIndexClient. Istnieją dwa sposoby uzyskania jednego: przez utworzenie go lub wywołanie Indexes.GetClient polecenia SearchServiceClient. Używamy tego ostatniego dla wygody.

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

Uwaga

W typowej aplikacji wyszukiwania zarządzanie indeksami i populacja mogą być obsługiwane przez oddzielny składnik z zapytań wyszukiwania. Indexes.GetClient jest wygodne do wypełniania indeksu, ponieważ pozwala zaoszczędzić problemy z zapewnieniem dodatkowego SearchCredentialselementu . Dzieje się tak dzięki przekazaniu klucza administratora, który został użyty w celu utworzenia klasy SearchServiceClient do nowej klasy SearchIndexClient. Jednak w części aplikacji, która wykonuje zapytania, lepiej jest utworzyć SearchIndexClient bezpośrednio, aby można było przekazać klucz zapytania, który umożliwia tylko odczytywanie danych zamiast klucza administratora. Jest to zgodne z zasadą najniższych uprawnień i pomoże zapewnić większe bezpieczeństwo aplikacji. Więcej informacji na temat kluczy administracyjnych i kluczy zapytań można znaleźć tutaj.

Teraz, gdy mamy element SearchIndexClient, możemy wypełnić indeks. Populacja indeksów jest wykonywana przez inną metodę, którą przejdziemy później.

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

Na koniec wykonamy kilka zapytań wyszukiwania i wyświetlimy wyniki. Tym razem użyjemy innego SearchIndexClientelementu :

ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(indexName, configuration);

RunQueries(indexClientForQueries);

Przyjrzymy się bliżej metodzie RunQueries później. Oto kod umożliwiający utworzenie nowego SearchIndexClientelementu :

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

Tym razem używamy klucza zapytania, ponieważ nie potrzebujemy dostępu do zapisu do indeksu. Te informacje można wprowadzić w appsettings.json pliku przykładowej aplikacji.

Jeśli uruchomisz tę aplikację z prawidłową nazwą usługi i kluczami interfejsu API, dane wyjściowe powinny wyglądać następująco: (Niektóre dane wyjściowe konsoli zostały zastąpione ciągiem "..." w celach ilustracyjnych).


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

Pełny kod źródłowy aplikacji jest udostępniany na końcu tego artykułu.

Następnie przyjrzymy się bliżej każdej z metod wywoływanych przez Mainmetodę .

Tworzenie indeksu

Po utworzeniu obiektu SearchServiceClientprogram usuwa indeks "hotels", Main jeśli już istnieje. Usunięcie odbywa się za pomocą następującej metody:

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

Ta metoda używa danej SearchServiceClient metody, aby sprawdzić, czy indeks istnieje, a jeśli tak, usuń go.

Uwaga

Przykładowy kod w tym artykule używa synchronicznych metod zestawu SDK platformy .NET Azure Cognitive Search dla uproszczenia. Zalecamy użycie metod asynchronicznych w aplikacjach, aby pozostały skalowalne i szybko reagowały. Na przykład w powyższej metodzie można użyć ExistsAsync metody i zamiast Exists i DeleteAsyncDelete.

Main Następnie tworzy nowy indeks "hotels" przez wywołanie tej metody:

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

Ta metoda tworzy nowy Index obiekt z listą Field obiektów definiujących schemat nowego indeksu. Każde pole ma nazwę, typ danych i kilka atrybutów definiujących zachowanie wyszukiwania. Klasa FieldBuilder używa odbicia, aby utworzyć listę Field obiektów dla indeksu, sprawdzając właściwości publiczne i atrybuty danej Hotel klasy modelu. Przyjrzymy się bliżej Hotel klasie później.

Uwaga

Zawsze można utworzyć listę Field obiektów bezpośrednio, zamiast używać ich FieldBuilder w razie potrzeby. Na przykład możesz nie chcieć użyć klasy modelu lub może być konieczne użycie istniejącej klasy modelu, której nie chcesz modyfikować, dodając atrybuty.

Oprócz pól można również dodawać profile oceniania, sugestory lub opcje MECHANIZMU CORS do indeksu (te parametry są pomijane z przykładu dla zwięzłości). Więcej informacji na temat obiektu Index i jego składników można znaleźć w dokumentacji zestawu SDK, a także w dokumentacji interfejsu API REST Azure Cognitive Search.

Wypełnianie indeksu

Następny krok w pliku Main wypełnia nowo utworzony indeks. Ta populacja indeksów jest wykonywana w następującej metodzie: (Kod zastąpiony ciągiem "..." do celów ilustracyjnych. Zobacz pełne przykładowe rozwiązanie dla pełnego kodu populacji danych).

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

Ta metoda ma cztery części. Pierwszy tworzy tablicę 3 Hotel obiektów z 3 Room obiektami, które będą służyć jako dane wejściowe do przekazania do indeksu. Te dane są zakodowane na twardo dla uproszczenia. We własnej aplikacji dane prawdopodobnie pochodzą z zewnętrznego źródła danych, takiego jak baza danych SQL.

Druga część tworzy dokument IndexBatch zawierający. W tym przypadku należy określić operację, którą chcesz zastosować do partii w momencie jej utworzenia, wywołując metodę IndexBatch.Upload. Następnie partia zostanie przekazana do indeksu Azure Cognitive Search za pomocą Documents.Index metody .

Uwaga

W tym przykładzie po prostu przekazujemy dokumenty. Jeśli chcesz scalić zmiany z istniejącymi dokumentami lub usunąć dokumenty, możesz utworzyć partie, wywołując metodę IndexBatch.Merge, IndexBatch.MergeOrUploadlub IndexBatch.Delete zamiast tego. Można również mieszać różne operacje w jednej partii, wywołując metodę IndexBatch.New, która pobiera kolekcję IndexAction obiektów, z których każda informuje Azure Cognitive Search o wykonaniu określonej operacji na dokumencie. Każdy z nich można utworzyć IndexAction przy użyciu własnej operacji, wywołując odpowiednią metodę, taką jak IndexAction.Merge, IndexAction.Uploadi tak dalej.

Trzecia część tej metody to blok catch, który obsługuje ważny przypadek błędu indeksowania. Jeśli usługa Azure Cognitive Search nie może indeksować niektórych dokumentów w partii, element IndexBatchException jest zgłaszany przez polecenie Documents.Index. Ten wyjątek może wystąpić, jeśli indeksujesz dokumenty, gdy usługa jest obciążona dużym obciążeniem. Zdecydowanie zalecamy jawną obsługę tego przypadku w kodzie. Możesz opóźnić, a następnie ponowić indeksowanie dokumentów, których przetwarzanie zakończyło się niepowodzeniem, lub możesz zarejestrować błąd i kontynuować, tak jak w prezentowanym przykładzie. Możesz też wykonać inną akcję w zależności od wymagań spójności danych aplikacji.

Uwaga

Za pomocą FindFailedActionsToRetry metody można utworzyć nową partię zawierającą tylko akcje, które zakończyły się niepowodzeniem w poprzednim wywołaniu metody .Index Istnieje dyskusja na temat prawidłowego używania go w witrynie StackOverflow.

UploadDocuments Na koniec metoda opóźnia się przez dwie sekundy. Indeksowanie odbywa się asynchronicznie w usłudze Azure Cognitive Search, więc przykładowa aplikacja musi poczekać chwilę, aby upewnić się, że dokumenty są dostępne do wyszukiwania. Tego typu opóźnienia są zazwyczaj konieczne tylko w przypadku pokazów, testów i przykładowych aplikacji.

Jak zestaw .NET SDK obsługuje dokumenty

Być może zastanawiasz się, w jaki sposób zestaw SDK platformy .NET Azure Cognitive Search może przekazać wystąpienia klasy zdefiniowanej przez użytkownika, takie jak Hotel do indeksu. Aby odpowiedzieć na to pytanie, przyjrzyjmy Hotel się klasie:

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

Pierwszą rzeczą, którą należy zauważyć, jest to, że nazwa każdej właściwości publicznej w Hotel klasie będzie mapować na pole o tej samej nazwie w definicji indeksu. Jeśli chcesz, aby każde pole zaczęło się od małej litery ("przypadek camel"), możesz poinformować zestaw SDK, aby zamapować nazwy właściwości na przypadek camel automatycznie przy użyciu atrybutu [SerializePropertyNamesAsCamelCase] w klasie. Ten scenariusz jest typowy w aplikacjach .NET, które wykonują powiązanie danych, gdzie schemat docelowy jest poza kontrolą dewelopera aplikacji bez konieczności naruszania wytycznych dotyczących nazewnictwa "Pascal case" na platformie .NET.

Uwaga

Zestaw SDK platformy .NET Azure Cognitive Search używa biblioteki NewtonSoft JSON.NET do serializacji i deserializacji obiektów modelu niestandardowego do i z formatu JSON. W razie potrzeby możesz dostosować serializację. Aby uzyskać więcej informacji, zobacz Custom Serialization with JSON.NET (Niestandardowa serializacja z JSON.NET).

Drugą rzeczą, którą należy zauważyć, jest to, że każda właściwość jest ozdobiona atrybutami, takimi jak IsFilterable, IsSearchable, Keyi Analyzer. Te atrybuty są mapowania bezpośrednio na odpowiednie atrybuty pól w indeksie Azure Cognitive Search. Klasa FieldBuilder używa tych właściwości do konstruowania definicji pól dla indeksu.

Trzecią ważną rzeczą w Hotel klasie są typy danych właściwości publicznych. Typy .NET tych właściwości są mapowane na odpowiadające im typy pól w definicji indeksu. Na przykład właściwość ciągu Category jest mapowana na pole category mające typ Edm.String. Istnieją podobne mapowania typów między bool?, , Edm.BooleanDateTimeOffset?i Edm.DateTimeOffset tak dalej. Określone reguły mapowania typów są udokumentowane przy Documents.Get użyciu metody w dokumentacji zestawu SDK platformy Azure Cognitive Search .NET. Klasa FieldBuilder zajmuje się tym mapowaniem, ale nadal może być przydatna do zrozumienia w przypadku konieczności rozwiązywania problemów z serializacji.

Czy zdarzyło ci się zauważyć SmokingAllowed właściwość?

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

Atrybut JsonIgnore tej właściwości informuje, FieldBuilder aby nie serializować go do indeksu jako pola. Jest to doskonały sposób tworzenia właściwości obliczeniowych po stronie klienta, których można użyć jako pomocników w aplikacji. W tym przypadku właściwość odzwierciedla, SmokingAllowed czy którakolwiek Room z kolekcji Rooms zezwala na palenie. Jeśli wszystkie są fałszywe, oznacza to, że cały hotel nie zezwala na palenie.

Niektóre właściwości, takie jak Address i Rooms są wystąpieniami klas platformy .NET. Te właściwości reprezentują bardziej złożone struktury danych i w rezultacie wymagają pól ze złożonym typem danych w indeksie.

Właściwość Address reprezentuje zestaw wielu wartości w Address klasie zdefiniowanej poniżej:

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

Ta klasa zawiera standardowe wartości używane do opisywania adresów w Stany Zjednoczone lub Kanadzie. Możesz użyć takich typów, aby grupować pola logiczne razem w indeksie.

Właściwość Rooms reprezentuje tablicę Room obiektów:

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

Model danych na platformie .NET i odpowiadający mu schemat indeksu powinien być zaprojektowany tak, aby obsługiwał środowisko wyszukiwania, które chcesz nadać użytkownikowi końcowemu. Każdy obiekt najwyższego poziomu na platformie .NET, czyli dokument w indeksie, odpowiada wynikowi wyszukiwania przedstawionemu w interfejsie użytkownika. Na przykład w aplikacji wyszukiwania hotelowego użytkownicy końcowi mogą chcieć wyszukiwać według nazwy hotelu, funkcji hotelu lub cech określonego pokoju. Omówimy kilka przykładów zapytań nieco później.

Ta możliwość korzystania z własnych klas do interakcji z dokumentami w indeksie działa w obu kierunkach; Możesz również pobrać wyniki wyszukiwania i automatycznie wykonać deserializacji zestawu SDK do wybranego typu, jak zobaczymy w następnej sekcji.

Uwaga

Zestaw SDK platformy .NET Azure Cognitive Search obsługuje również dynamicznie wpisywane dokumenty przy użyciu Document klasy, która jest mapowaniem klucz/wartość nazw pól na wartości pól. Jest to przydatne, jeśli nie znasz schematu indeksu w czasie projektowania lub jeśli powiązanie z określonymi klasami modelu jest niedogodne. Wszystkie metody w zestawie SDK, które obsługują dokumenty, mają przeciążenia działające z klasą Document, a także przeciążenia o silnych typach przyjmujące parametr typu ogólnego. Tylko te ostatnie są używane w przykładowym kodzie w tym samouczku. KlasaDocument dziedziczy z Dictionary<string, object>klasy .

Dlaczego należy używać typów danych dopuszczających wartość null

Podczas projektowania własnych klas modelu do mapowania na indeks Azure Cognitive Search zalecamy deklarowanie właściwości typów wartości, takich jak bool i int ma być nullable (na przykład bool? zamiast bool). W przypadku użycia właściwości niedopuszczającej wartości null musisz zagwarantować, że żaden dokument w indeksie nie zawiera wartości null w odpowiednim polu. Ani zestaw SDK, ani usługa Azure Cognitive Search nie pomogą Ci wymusić tego.

Nie jest to czysto hipotetyczny problem: wyobraź sobie scenariusz, w którym dodajesz nowe pole do istniejącego indeksu typu Edm.Int32. Po zaktualizowaniu definicji indeksu wszystkie dokumenty będą miały wartość null dla tego nowego pola (ponieważ wszystkie typy mają wartość null w Azure Cognitive Search). Jeśli następnie dla tego pola użyjesz klasy modelu z właściwością int niedopuszczającą wartości null, podczas próby pobrania dokumentów otrzymasz wyjątek JsonSerializationException podobny do poniższego:

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

Z tego powodu najlepszym i zalecanym rozwiązaniem jest używanie w klasach modeli typów dopuszczających wartość null.

Serializacja niestandardowa z JSON.NET

Zestaw SDK używa JSON.NET do serializacji i deserializacji dokumentów. W razie potrzeby można dostosować serializacji i deserializacji, definiując własne JsonConverter lub IContractResolver. Aby uzyskać więcej informacji, zobacz dokumentację JSON.NET. Może to być przydatne, gdy chcesz dostosować istniejącą klasę modelu z aplikacji do użycia z Azure Cognitive Search i innymi bardziej zaawansowanymi scenariuszami. Na przykład za pomocą niestandardowej serializacji można wykonywać następujące czynności:

  • Dołącz lub wyklucz pewne właściwości klasy modelu z przechowywania jako pól dokumentu.
  • Zamapuj nazwy właściwości w kodzie i nazwach pól w indeksie.
  • Utwórz atrybuty niestandardowe, których można użyć do mapowania właściwości w polach dokumentu.

Przykłady implementowania serializacji niestandardowej można znaleźć w testach jednostkowych dla zestawu SDK platformy .NET Azure Cognitive Search w GitHub. Dobrym punktem wyjścia jest ten folder. Zawiera klasy, które są używane przez niestandardowe testy serializacji.

Wyszukiwanie dokumentów w indeksie

Ostatnim krokiem w przykładowej aplikacji jest wyszukiwanie niektórych dokumentów w indeksie:

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

Za każdym razem, gdy wykonuje zapytanie, ta metoda najpierw tworzy nowy SearchParameters obiekt. Ten obiekt służy do określania dodatkowych opcji zapytania, takich jak sortowanie, filtrowanie, stronicowanie i tworzenie aspektów. W tej metodzie ustawiamy Filterwłaściwość , Select, OrderByi Top dla różnych zapytań. SearchParameters Wszystkie właściwości są udokumentowane tutaj.

Następnym krokiem jest wykonanie zapytania wyszukiwania. Uruchamianie wyszukiwania odbywa się przy użyciu Documents.Search metody . Dla każdego zapytania przekazujemy tekst wyszukiwania do użycia jako ciąg (lub "*" jeśli nie ma tekstu wyszukiwania) oraz utworzone wcześniej parametry wyszukiwania. Określamy również Hotel jako parametr typu dla Documents.Searchparametru , który nakazuje zestawowi SDK deserializowanie dokumentów w wynikach wyszukiwania do obiektów typu Hotel.

Uwaga

Więcej informacji na temat składni wyrażenia zapytania wyszukiwania można znaleźć tutaj.

Na koniec po wykonaniu każdego zapytania ta metoda wykonuje iterację po wszystkich dopasowaniach w wynikach wyszukiwania, drukuj każdy dokument w konsoli:

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

    Console.WriteLine();
}

Przyjrzyjmy się bliżej poszczególnym zapytaniom. Oto kod do wykonania pierwszego zapytania:

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

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

WriteDocuments(results);

W tym przypadku przeszukujemy cały indeks słowa "motel" w dowolnym polu z możliwością wyszukiwania i chcemy pobrać nazwy hoteli zgodnie z parametrem Select . Oto wyniki:

Name: Secret Point Motel

Name: Twin Dome Motel

Następne zapytanie jest nieco bardziej interesujące. Chcemy znaleźć wszystkie hotele, które mają pokój z nocną stawką mniejszą niż $100 i zwracają tylko identyfikator hotelu i opis:

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

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

WriteDocuments(results);

To zapytanie używa wyrażenia OData $filter , Rooms/any(r: r/BaseRate lt 100)do filtrowania dokumentów w indeksie. Używa to dowolnego operatora , aby zastosować element "BaseRate lt 100" do każdego elementu w kolekcji Rooms. Więcej informacji na temat składni OData, która Azure Cognitive Search obsługuje, można znaleźć tutaj.

Oto wyniki zapytania:

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

Następnie chcemy znaleźć dwa najlepsze hotele, które zostały ostatnio odnowione, i pokazać nazwę hotelu i datę ostatniej renowacji. Oto kod:

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

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

WriteDocuments(results);

W tym przypadku ponownie użyjemy składni OData, aby określić OrderBy parametr jako lastRenovationDate desc. Ustawiliśmy również wartość Top 2, aby upewnić się, że uzyskamy tylko dwa pierwsze dokumenty. Tak jak wcześniej ustawiliśmy opcję Select określania pól, które mają być zwracane.

Oto wyniki:

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

Na koniec chcemy znaleźć wszystkie nazwy hoteli, które pasują do słowa "hotel":

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

WriteDocuments(results);

Oto wyniki, które zawierają wszystkie pola, ponieważ nie określono Select właściwości :

	HotelId: 3
	Name: Triple Landscape Hotel
	...

Ten krok kończy samouczek, ale nie zatrzymaj się tutaj. **Następne kroki zapewniają dodatkowe zasoby, aby dowiedzieć się więcej na temat Azure Cognitive Search.

Następne kroki