Come utilizzare Ricerca di Azure da un'applicazione .NETHow to use Azure Search from a .NET Application

In questo articolo è descritta una procedura dettagliata per eseguire .NET SDK di Ricerca di Azure.This article is a walkthrough to get you up and running with the Azure Search .NET SDK. È possibile utilizzare .NET SDK per implementare un'esperienza di ricerca completa nell'applicazione tramite Ricerca di Azure.You can use the .NET SDK to implement a rich search experience in your application using Azure Search.

Novità dell'SDK di Ricerca di AzureWhat's in the Azure Search SDK

Questo SDK è costituito da una libreria client, Microsoft.Azure.Search.The SDK consists of a client library, Microsoft.Azure.Search. Consente di gestire indici, origini dati e indicizzatori, nonché di caricare e gestire documenti ed eseguire query, senza la necessità di affrontare i dettagli di HTTP e JSON.It enables you to manage your indexes, data sources, and indexers, as well as upload and manage documents, and execute queries, all without having to deal with the details of HTTP and JSON.

La libreria client definisce classi come Index, Field, e Document, nonché operazioni quali Indexes.Create e Documents.Search sulle classi SearchServiceClient e SearchIndexClient.The client library defines classes like Index, Field, and Document, as well as operations like Indexes.Create and Documents.Search on the SearchServiceClient and SearchIndexClient classes. Le classi sono organizzate negli spazi dei nomi seguenti:These classes are organized into the following namespaces:

La versione corrente di Azure Search .NET SDK è ora in disponibilità generale.The current version of the Azure Search .NET SDK is now generally available. Per fornire a Microsoft commenti e suggerimenti da incorporare nella prima versione successiva, visitare la pagina dei commenti.If you would like to provide feedback for us to incorporate in the next version, please visit our feedback page.

.NET SDK supporta la versione 2016-09-01 dell'API REST di Ricerca di Azure.The .NET SDK supports version 2016-09-01 of the Azure Search REST API. Questa versione include ora il supporto per gli analizzatori personalizzati, BLOB di Azure e l'Indicizzatore di tabelle di Azure.This version now includes support for custom analyzers and Azure Blob and Azure Table indexer support. Le funzionalità di anteprima che non sono incluse in questa versione, ad esempio il supporto per l'indicizzazione di file JSON e CSV, sono disponibili in anteprima e tramite la versione 4.0.1-preview di .NET SDK.Preview features that are not part of this version, such as support for indexing JSON and CSV files, are in preview and available via 4.0.1-preview version of the .NET SDK.

Questo SDK non supporta operazioni di gestione, come la creazione e la scalabilità di servizi di ricerca e la gestione delle chiavi API.This SDK does not support Management Operations such as creating and scaling Search services and managing API keys. Se è necessario gestire le risorse di ricerca da un'applicazione .NET, è possibile utilizzare l'SDK di gestione .NET di Ricerca di Azure.If you need to manage your Search resources from a .NET application, you can use the Azure Search .NET Management SDK.

Aggiornamento alla versione più recente dell'SDKUpgrading to the latest version of the SDK

Se si usa già una versione precedente di Azure Search .NET SDK e si vuole eseguire l'aggiornamento alla nuova versione in disponibilità generale, seguire le indicazioni in questo articolo .If you're already using an older version of the Azure Search .NET SDK and you'd like to upgrade to the new generally available version, this article explains how.

Requisiti per l'SDKRequirements for the SDK

  1. Visual Studio 2017.Visual Studio 2017.
  2. Un servizio di Ricerca di Azure.Your own Azure Search service. Per utilizzare l'SDK, è necessario il nome del servizio e una o più chiavi API.In order to use the SDK, you will need the name of your service and one or more API keys. Creare un servizio nel portale per eseguire facilmente questi passaggi.Create a service in the portal will help you through these steps.
  3. Scaricare il pacchetto NuGet di SDK .NET di Ricerca di Azure tramite "Gestisci pacchetti NuGet" in Visual Studio.Download the Azure Search .NET SDK NuGet package by using "Manage NuGet Packages" in Visual Studio. Cercare il nome del pacchetto Microsoft.Azure.Search su NuGet.org.Just search for the package name Microsoft.Azure.Search on NuGet.org.

Azure Search .NET SDK supporta applicazioni destinate a .NET Framework 4.6 e .NET Core.The Azure Search .NET SDK supports applications targeting the .NET Framework 4.6 and .NET Core.

Scenari chiaveCore scenarios

Esistono diverse operazioni che è necessario eseguire nell'applicazione di ricerca.There are several things you'll need to do in your search application. In questa esercitazione saranno illustrati questi scenari chiave:In this tutorial, we'll cover these core scenarios:

  • Creazione di un indiceCreating an index
  • Popolamento dell’indice con i documentiPopulating the index with documents
  • Ricerca di documenti mediante filtri e ricerca con testo completoSearching for documents using full-text search and filters

Il codice di esempio che segue illustra ciascuna di queste operazioni.The sample code that follows illustrates each of these. È possibile utilizzare i frammenti di codice nell'applicazione.Feel free to use the code snippets in your own application.

OverviewOverview

L'applicazione di esempio crea un nuovo indice denominato "hotels", vi inserisce alcuni documenti, quindi esegue alcune query di ricerca.The sample application we'll be exploring creates a new index named "hotels", populates it with a few documents, then executes some search queries. Ecco il programma principale che mostra il flusso generale: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);

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

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

    ISearchIndexClient indexClient = serviceClient.Indexes.GetClient("hotels");

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

Nota

Il codice sorgente completo dell'applicazione di esempio utilizzata è disponibile in questa procedura dettagliata su GitHub.You can find the full source code of the sample application used in this walk through on GitHub.

La procedura verrà illustrata passo per passo.We'll walk through this step by step. In primo luogo è necessario creare un nuovo SearchServiceClient.First we need to create a new SearchServiceClient. Questo oggetto consente di gestire gli indici.This object allows you to manage indexes. Per creare uno, è necessario fornire il nome del servizio Ricerca di Azure, nonché una chiave API di amministrazione.In order to construct one, you need to provide your Azure Search service name as well as an admin API key. È possibile immettere queste informazioni nel file appsettings.json dell'applicazione di esempio.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;
}

Nota

Se si fornisce una chiave non corretta (ad esempio una chiave di query dove era richiesta una chiave di amministrazione), SearchServiceClient invierà un CloudException con il messaggio di errore "Non consentito" la prima volta che si chiama un metodo di operazione, ad esempio Indexes.Create.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. In questo caso, eseguire una doppia verifica della chiave API.If this happens to you, double-check our API key.

Le righe successive chiamano i metodi per creare un indice denominato "hotels", eliminandolo prima se esiste già.The next few lines call methods to create an index named "hotels", deleting it first if it already exists. Tali metodi saranno illustrati più avanti.We will walk through these methods a little later.

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

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

Successivamente, l'indice deve essere popolato.Next, the index needs to be populated. A tale scopo, è necessario un SearchIndexClient.To do this, we will need a SearchIndexClient. Esistono due modi per ottenere uno: creandolo oppure chiamando Indexes.GetClient sul SearchServiceClient.There are two ways to obtain one: by constructing it, or by calling Indexes.GetClient on the SearchServiceClient. Per comodità, sarà utilizzato il secondo.We use the latter for convenience.

ISearchIndexClient indexClient = serviceClient.Indexes.GetClient("hotels");

Nota

In un'applicazione di ricerca tipica, il popolamento e la gestione degli indici viene gestita da un componente separato dalle query di ricerca.In a typical search application, index management and population is handled by a separate component from search queries. Indexes.GetClient è utile per il popolamento di un indice perché evita di specificare un altro SearchCredentials.Indexes.GetClient is convenient for populating an index because it saves you the trouble of providing another SearchCredentials. Questa operazione viene eseguita passando la chiave di amministrazione utilizzata per creare il SearchServiceClient al nuovo SearchIndexClient.It does this by passing the admin key that you used to create the SearchServiceClient to the new SearchIndexClient. Tuttavia, nella parte dell'applicazione che esegue le query, è preferibile creare il SearchIndexClient direttamente in modo che sia possibile passare una chiave di query anziché una chiave di amministrazione.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 instead of an admin key. Questo procedimento è coerente con il principio del privilegio minimo e consente di rendere più sicura l'applicazione.This is consistent with the principle of least privilege and will help to make your application more secure. È possibile trovare ulteriori informazioni su chiavi di amministrazione e chiavi di query qui.You can find out more about admin keys and query keys here.

Ora che è disponibile un SearchIndexClient, è possibile popolare l'indice.Now that we have a SearchIndexClient, we can populate the index. A tal fine verrà utilizzato un altro metodo che verrà descritto più avanti.This is done by another method that we will walk through later.

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

Infine, saranno eseguite alcune query di ricerca e visualizzati i risultati.Finally, we execute a few search queries and display the results. In questo caso viene utilizzato un SearchIndexClient diverso:This time we use a different SearchIndexClient:

ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(configuration);

RunQueries(indexClientForQueries);

Esamineremo il metodo RunQueries più da vicino in un secondo momento.We will take a closer look at the RunQueries method later. Ecco il codice per creare il nuovo SearchIndexClient:Here is the code to create the new SearchIndexClient:

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

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

In questo caso è utilizzata una chiave di query poiché non è necessario l'accesso in scrittura all'indice.This time we use a query key since we do not need write access to the index. È possibile immettere queste informazioni nel file appsettings.json dell'applicazione di esempio.You can enter this information in the appsettings.json file of the sample application.

Se si esegue questa applicazione con un nome di servizio valido e le chiavi API, l'output dovrebbe essere simile al seguente:If you run this application with a valid service name and API keys, the output should look like this:

Deleting index...

Creating index...

Uploading documents...

Waiting for documents to be indexed...

Search the entire index for the term 'budget' and return only the hotelName field:

Name: Roach Motel

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

ID: 2   Description: Cheapest hotel in town
ID: 3   Description: Close to town hall and the river

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

Search the entire index for the term 'motel':

ID: 2   Base rate: 79.99        Description: Cheapest hotel in town     Description (French): Hôtel le moins cher en ville      Name: Roach Motel       Category: Budget        Tags: [motel, budget]   Parking included: yes   Smoking allowed: yes    Last renovated on: 4/28/1982 12:00:00 AM +00:00 Rating: 1/5     Location: Latitude 49.678581, longitude -122.131577

Complete.  Press any key to end application...

Alla fine di questo articolo viene fornito il codice sorgente completo dell'applicazione.The full source code of the application is provided at the end of this article.

Successivamente, verrà esaminato ognuno dei metodi chiamati da Main.Next, we will take a closer look at each of the methods called by Main.

Creazione di un indiceCreating an index

Dopo aver creato un SearchServiceClient, l'operazione successiva Main è l’eliminazione dell’indice "hotel" se esiste già.After creating a SearchServiceClient, the next thing Main does is delete the "hotels" index if it already exists. Questa operazione viene eseguita con il metodo seguente:That is done by the following method:

private static void DeleteHotelsIndexIfExists(SearchServiceClient serviceClient)
{
    if (serviceClient.Indexes.Exists("hotels"))
    {
        serviceClient.Indexes.Delete("hotels");
    }
}

Questo metodo utilizza il SearchServiceClient fornito per verificare l'esistenza dell'indice ed eventualmente eliminarlo.This method uses the given SearchServiceClient to check if the index exists, and if so, delete it.

Nota

Il codice di esempio riportato in questo articolo utilizza i metodi sincroni di .NET SDK di Ricerca di Azure per motivi di semplicità.The example code in this article uses the synchronous methods of the Azure Search .NET SDK for simplicity. È consigliabile utilizzare i metodi asincroni nelle proprie applicazioni per mantenerle scalabili e reattive.We recommend that you use the asynchronous methods in your own applications to keep them scalable and responsive. Ad esempio, nel metodo precedente è possibile utilizzare ExistsAsync e DeleteAsync anziché Exists e Delete.For example, in the method above you could use ExistsAsync and DeleteAsync instead of Exists and Delete.

Successivamente, Main crea un nuovo indice "hotel" chiamando questo metodo:Next, Main creates a new "hotels" index by calling this method:

private static void CreateHotelsIndex(SearchServiceClient serviceClient)
{
    var definition = new Index()
    {
        Name = "hotels",
        Fields = FieldBuilder.BuildForType<Hotel>()
    };

    serviceClient.Indexes.Create(definition);
}

Questo metodo crea un nuovo oggetto Index con un elenco di oggetti Field che definisce lo schema del nuovo indice.This method creates a new Index object with a list of Field objects that defines the schema of the new index. Ogni campo ha un nome, un tipo di dati e diversi attributi che definiscono il comportamento della ricerca.Each field has a name, data type, and several attributes that define its search behavior. La classe FieldBuilder utilizza la reflection per creare un elenco di oggetti Field per l'indice esaminando le proprietà pubbliche e gli attributi della classe modello Hotel specificata.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. Esamineremo la classe Hotel più da vicino in un secondo momento.We'll take a closer look at the Hotel class later on.

Nota

Se necessario, è inoltre possibile creare l'elenco di oggetti Field direttamente anziché utilizzando FieldBuilder.You can always create the list of Field objects directly instead of using FieldBuilder if needed. Ad esempio, è possibile evitare di utilizzare una classe modello o potrebbe essere necessario utilizzare una classe modello esistente che non si desidera modificare aggiungendo attributi.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.

Oltre ai campi, è possibile aggiungere anche profili di punteggio, suggerimenti di alternative o opzioni CORS per l'indice. Per motivi di brevità nell’esempio questi elementi sono stati omessi.In addition to fields, you can also add scoring profiles, suggesters, or CORS options to the Index (these are omitted from the sample for brevity). Altre informazioni sull'oggetto indice e le relative parti costituenti sono disponibili nella guida di riferimento dell'SDK e nella guida di riferimento dell'interfaccia API REST di Ricerca di Azure.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.

Popolamento dell'indicePopulating the index

Il passaggio successivo in Main è il popolamento dell'indice appena creato.The next step in Main is to populate the newly-created index. Questa operazione viene eseguita nel metodo seguente:This is done in the following method:

private static void UploadDocuments(ISearchIndexClient indexClient)
{
    var hotels = new Hotel[]
    {
        new Hotel()
        { 
            HotelId = "1", 
            BaseRate = 199.0, 
            Description = "Best hotel in town",
            DescriptionFr = "Meilleur hôtel en ville",
            HotelName = "Fancy Stay",
            Category = "Luxury", 
            Tags = new[] { "pool", "view", "wifi", "concierge" },
            ParkingIncluded = false, 
            SmokingAllowed = false,
            LastRenovationDate = new DateTimeOffset(2010, 6, 27, 0, 0, 0, TimeSpan.Zero), 
            Rating = 5, 
            Location = GeographyPoint.Create(47.678581, -122.131577)
        },
        new Hotel()
        { 
            HotelId = "2", 
            BaseRate = 79.99,
            Description = "Cheapest hotel in town",
            DescriptionFr = "Hôtel le moins cher en ville",
            HotelName = "Roach Motel",
            Category = "Budget",
            Tags = new[] { "motel", "budget" },
            ParkingIncluded = true,
            SmokingAllowed = true,
            LastRenovationDate = new DateTimeOffset(1982, 4, 28, 0, 0, 0, TimeSpan.Zero),
            Rating = 1,
            Location = GeographyPoint.Create(49.678581, -122.131577)
        },
        new Hotel() 
        { 
            HotelId = "3", 
            BaseRate = 129.99,
            Description = "Close to town hall and the river"
        }
    };

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

Questo metodo è costituito da quattro parti.This method has four parts. La prima crea una matrice di oggetti Hotel che verranno utilizzati come dati di input per caricare l'indice.The first creates an array of Hotel objects that will serve as our input data to upload to the index. Questi dati sono hardcoded per motivi di semplicità.This data is hard-coded for simplicity. Nell'applicazione, i dati probabilmente proverranno da un'origine dati esterna, ad esempio un database SQL.In your own application, your data will likely come from an external data source such as a SQL database.

La seconda parte crea un oggetto IndexBatch contenente i documenti.The second part creates an IndexBatch containing the documents. Specificare l'operazione che si vuole applicare al batch al momento della creazione, in questo caso chiamando 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. Il batch viene quindi caricato nell'indice di Ricerca di Azure dal metodo Documents.Index .The batch is then uploaded to the Azure Search index by the Documents.Index method.

Nota

In questo esempio, verranno semplicemente caricati i documenti.In this example, we are just uploading documents. Se invece si vogliono unire le modifiche in documenti esistenti o eliminare documenti, creare batch chiamando IndexBatch.Merge, IndexBatch.MergeOrUpload o 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. È inoltre possibile combinare diverse operazioni in un singolo batch chiamando IndexBatch.New, che accetta una raccolta di oggetti IndexAction, ognuno dei quali indica a Ricerca di Azure di eseguire una particolare operazione su un documento.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. È possibile creare ogni oggetto IndexAction con la propria operazione chiamando il metodo corrispondente, ad esempio IndexAction.Merge, IndexAction.Upload e così via.You can create each IndexAction with its own operation by calling the corresponding method such as IndexAction.Merge, IndexAction.Upload, and so on.

La terza parte di questo metodo è un blocco catch che gestisce un caso di errore importante per l'indicizzazione.The third part of this method is a catch block that handles an important error case for indexing. Se il servizio Ricerca di Azure non riesce a indicizzare alcuni dei documenti nel batch, viene generato un IndexBatchException da Documents.Index.If your Azure Search service fails to index some of the documents in the batch, an IndexBatchException is thrown by Documents.Index. Questa situazione può verificarsi se l'indicizzazione dei documenti avviene mentre il servizio è sovraccarico.This can happen if you are indexing documents while your service is under heavy load. Si consiglia di gestire in modo esplicito questo caso nel codice.We strongly recommend explicitly handling this case in your code. È possibile ritardare e quindi ritentare l'indicizzazione di documenti, accedere e continuare come nell'esempio, oppure eseguire altre attività a seconda dei requisiti di coerenza di dati dell'applicazione.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.

Nota

È possibile utilizzare il metodo FindFailedActionsToRetry per costruire un nuovo batch contenente solo le azioni non riuscite in una precedente chiamata a Index.You can use the FindFailedActionsToRetry method to construct a new batch containing only the actions that failed in a previous call to Index. Il metodo è documentato qui ed è una discussione su come utilizzarlo in modo corretto su StackOverflow.The method is documented here and there is a discussion of how to properly use it on StackOverflow.

Infine, il metodo UploadDocuments ritarda per due secondi.Finally, the UploadDocuments method delays for two seconds. L'indicizzazione avviene in modo asincrono nel servizio Ricerca di Azure, pertanto l'applicazione di esempio deve attendere un breve periodo per garantire che i documenti siano disponibili per la ricerca.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. Ritardi come questi in genere sono necessari solo in applicazioni di esempio, test e demo.Delays like this are typically only necessary in demos, tests, and sample applications.

Modalità di gestione dei documenti in .NET SDKHow the .NET SDK handles documents

Per capire in che modo .NET SDK di Ricerca di Azure è in grado di caricare le istanze di una classe definita dall'utente come Hotel nell'indice,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. è possibile esaminare la classe 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;

// The SerializePropertyNamesAsCamelCase attribute is defined in the Azure Search .NET SDK.
// It ensures that Pascal-case property names in the model class are mapped to camel-case
// field names in the index.
[SerializePropertyNamesAsCamelCase]
public partial class Hotel
{
    [System.ComponentModel.DataAnnotations.Key]
    [IsFilterable]
    public string HotelId { get; set; }

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

    [IsSearchable]
    public string Description { get; set; }

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

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

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

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

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

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

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

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

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

La prima cosa da notare è che ogni proprietà pubblica di Hotel corrisponde a un campo nella definizione dell'indice, ma con una differenza fondamentale: il nome di ogni campo inizia con una lettera minuscola ("convenzione camel"), mentre il nome di ogni proprietà pubblica di Hotel inizia con una lettera maiuscola ("convenzione Pascal").The first thing to notice is that each public property of Hotel corresponds to a field in the index definition, but with one crucial difference: The name of each field starts with a lower-case letter ("camel case"), while the name of each public property of Hotel starts with an upper-case letter ("Pascal case"). Si tratta di uno scenario comune in applicazioni .NET che consentono di eseguire l'associazione dati in cui lo schema di destinazione è fuori dal controllo dello sviluppatore dell'applicazione.This is a common scenario in .NET applications that perform data-binding where the target schema is outside the control of the application developer. Anziché dover violare linee guida sulla denominazione di .NET seguendo la convenzione camel per i nomi delle proprietà, è possibile indicare a SDK di eseguire automaticamente il mapping dei nomi delle proprietà alla convenzione camel con l’attributo [SerializePropertyNamesAsCamelCase] .Rather than having to violate the .NET naming guidelines by making property names camel-case, you can tell the SDK to map the property names to camel-case automatically with the [SerializePropertyNamesAsCamelCase] attribute.

Nota

Azure Search .NET SDK usa la libreria NewtonSoft JSON.NET per serializzare e deserializzare gli oggetti modello personalizzati in e da JSON.The Azure Search .NET SDK uses the NewtonSoft JSON.NET library to serialize and deserialize your custom model objects to and from JSON. Se necessario, è possibile personalizzare questa serializzazione.You can customize this serialization if needed. Per altre informazioni, vedere Serializzazione personalizzata con JSON.NET.For more details, see Custom Serialization with JSON.NET.

La seconda cosa da notare sono gli attributi, ad esempio IsFilterable, IsSearchable, Key e Analyzer, che decorano ogni proprietà pubblica.The second thing to notice are the attributes such as IsFilterable, IsSearchable, Key, and Analyzer that decorate each public property. Questi attributi eseguono il mapping direttamente agli attributi corrispondenti dell'indice di Ricerca di Azure.These attributes map directly to the corresponding attributes of the Azure Search index. La classe FieldBuilder li utilizza per creare definizioni di campo per l'indice.The FieldBuilder class uses these to construct field definitions for the index.

Il terzo aspetto importante della classe Hotel sono i tipi di dati delle proprietà pubbliche.The third important thing about the Hotel class are the data types of the public properties. I tipi .NET di queste proprietà eseguono il mapping ai tipi di campi equivalenti nella definizione dell'indice.The .NET types of these properties map to their equivalent field types in the index definition. Ad esempio, viene eseguito il mapping della proprietà stringa Category al campo category, che è di tipo Edm.String.For example, the Category string property maps to the category field, which is of type Edm.String. Esistono mapping di tipi simili tra bool? e Edm.Boolean, DateTimeOffset? e Edm.DateTimeOffset ecc. Le regole specifiche per il mapping dei tipi sono documentate con il metodo Documents.Get nella documentazione di riferimento relativa a .NET SDK di Ricerca di Azure.There are similar type mappings between bool? and Edm.Boolean, DateTimeOffset? and Edm.DateTimeOffset, etc. The specific rules for the type mapping are documented with the Documents.Get method in the Azure Search .NET SDK reference. La classe FieldBuilder si occupa di questo mapping automaticamente, ma la comprensione può comunque essere utile nel caso in cui sia necessario risolvere eventuali problemi di serializzazione.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.

La possibilità di utilizzare le proprie classi come documenti funziona in entrambe le direzioni; è possibile, inoltre, recuperare i risultati della ricerca e far sì che SDK li deserializzi automaticamente in un tipo di propria scelta, come sarà illustrato nella prossima sezione.This ability to use your own classes as documents 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.

Nota

.NET SDK di Ricerca di Azure supporta anche documenti tipizzati in modo dinamico utilizzando la classe Document, un mapping chiave/valore dei campi e valori dei campi.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. Ciò è utile negli scenari in cui non si conosce lo schema di indice in fase di progettazione o quando l’associazione a classi di modello specifico non sarebbe conveniente.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. Tutti i metodi in SDK che gestiscono documenti dispongono di overload che funzionano con la classe Document , nonché overload fortemente tipizzati che accettano un parametro di tipo generico.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. Solo questi ultimi vengono utilizzati nell'esempio di codice fornito in questa esercitazione.Only the latter are used in the sample code in this tutorial. La classe Document eredita da Dictionary<string, object>.The Document class inherits from Dictionary<string, object>. Per informazioni più dettagliate, vedere qui.You can find other details here.

Perché usare tipi di dati nullableWhy you should use nullable data types

Quando si progettano classi di modello personalizzate per eseguire il mapping a un indice di Ricerca di Azure, è consigliabile dichiarare le proprietà dei tipi di valore, ad esempio bool e int da rendere nullable (ad esempio, bool? invece di 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). Se si usa una proprietà che non ammette i valori Null, è necessario garantire che nessun documento nell'indice contenga un valore Null per il campo corrispondente.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. Né l'SDK né il servizio di Ricerca di Azure consentiranno di applicare questo valore.Neither the SDK nor the Azure Search service will help you to enforce this.

Non è solo un problema ipotetico: si pensi a uno scenario in cui si aggiunge un nuovo campo a un indice esistente di tipo 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. Dopo l'aggiornamento della definizione dell'indice, tutti i documenti avranno un valore Null per il nuovo campo (perché tutti i tipi sono nullable in Ricerca di Azure).After updating the index definition, all documents will have a null value for that new field (since all types are nullable in Azure Search). Se quindi si usa una classe di modelli con una proprietà int che non ammette i valori Null per tale campo, verrà restituita un'eccezione JsonSerializationException, come questa, quando si cercherà di recuperare i documenti: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'.

Per questo motivo, è consigliabile usare tipi nullable nelle classi di modelli.For this reason, we recommend that you use nullable types in your model classes as a best practice.

Serializzazione personalizzata con JSON.NETCustom Serialization with JSON.NET

L'SDK usa JSON.NET per serializzare e deserializzare i documenti.The SDK uses JSON.NET for serializing and deserializing documents. Se necessario, è possibile personalizzare la serializzazione e deserializzazione definendo il proprio JsonConverter o IContractResolver (vedere la documentazione di JSON.NET per altri dettagli).You can customize serialization and deserialization if needed by defining your own JsonConverter or IContractResolver (see the JSON.NET documentation for more details). Ciò può risultare utile quando si vuole adattare una classe modello esistente dell'applicazione per usarla con Ricerca di Azure e in altri scenari più avanzati.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. Con la serializzazione personalizzata, ad esempio, è possibile:For example, with custom serialization you can:

  • Includere o escludere determinate proprietà della classe modello da archiviare come campi di un documento.Include or exclude certain properties of your model class from being stored as document fields.
  • Eseguire il mapping tra i nomi di proprietà nel codice e i nomi di campo nell'indice.Map between property names in your code and field names in your index.
  • Creare gli attributi personalizzati che possono essere utilizzati per il mapping di proprietà ai campi di documento.Create custom attributes that can be used for mapping properties to document fields.

Esempi di implementazione della serializzazione personalizzata sono disponibili negli unit test per Azure Search .NET SDK in GitHub.You can find examples of implementing custom serialization in the unit tests for the Azure Search .NET SDK on GitHub. È consigliabile iniziare da questa cartella,A good starting point is this folder. che contiene le classi usate dai test di serializzazione personalizzata.It contains classes that are used by the custom serialization tests.

Ricerca di documenti nell'indiceSearching for documents in the index

L'ultimo passaggio nell'applicazione di esempio è cercare alcuni documenti nell'indice.The last step in the sample application is to search for some documents in the index. A tale scopo viene utilizzato il metodo seguente:The following method does this:

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

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

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

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

    WriteDocuments(results);

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

    parameters =
        new SearchParameters()
        {
            Filter = "baseRate lt 150",
            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 'motel':\n");

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

    WriteDocuments(results);
}

Ogni volta che viene eseguita una query, questo metodo crea prima un nuovo oggetto SearchParameters.Each time it executes a query, this method first creates a new SearchParameters object. Consente di specificare opzioni aggiuntive per la query di ordinamento, filtro, impaginazione e faceting.This is used to specify additional options for the query such as sorting, filtering, paging, and faceting. In questo metodo, impostiamo le proprietà Filter, Select, OrderBy e Top per diverse query.In this method, we're setting the Filter, Select, OrderBy, and Top property for different queries. Tutte le proprietà SearchParameters sono documentate qui.All the SearchParameters properties are documented here.

Il passaggio successivo consiste nell’esecuzione effettiva della query di ricerca.The next step is to actually execute the search query. Questa operazione viene effettuata con il metodo Documents.Search .This is done using the Documents.Search method. Per ogni query, è possibile passare il testo di ricerca da utilizzare come stringa (o "*" se non è presente il testo di ricerca), oltre ai parametri di ricerca creati in precedenza.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. Viene inoltre specificato Hotel come parametro di tipo per Documents.Search, che indica all'SDK di deserializzare i documenti nei risultati della ricerca in oggetti di tipo Hotel.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.

Nota

È possibile trovare ulteriori informazioni sulla sintassi delle espressioni di query di ricerca qui.You can find more information about the search query expression syntax here.

Infine, dopo ogni query questo metodo scorre tutte le corrispondenze nei risultati della ricerca, stampando tutti i documenti nella console: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();
}

Queste query saranno ora esaminate in maggiore dettaglio.Let's take a closer look at each of the queries in turn. Ecco il codice per eseguire la prima query:Here is the code to execute the first query:

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

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

WriteDocuments(results);

In questo caso, viene eseguita la ricerca degli hotel che corrispondono alla parola "budget". Verranno visualizzati solo i nomi degli hotel, come specificato dal parametro Select.In this case, we're searching for hotels that match the word "budget", and we want to get back only the hotel names, as specified by the Select parameter. Ecco i risultati:Here are the results:

Name: Roach Motel

Poi, si desidera trovare gli hotel con una tariffa notturna inferiore a 150 dollari e visualizzare solo l'ID dell'hotel e la descrizione:Next, we want to find the hotels with a nightly rate of less than $150, and return only the hotel ID and description:

parameters =
    new SearchParameters()
    {
        Filter = "baseRate lt 150",
        Select = new[] { "hotelId", "description" }
    };

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

WriteDocuments(results);

Questa query utilizza un'espressione $filter OData, baseRate lt 150, per filtrare i documenti nell'indice.This query uses an OData $filter expression, baseRate lt 150, to filter the documents in the index. È possibile trovare informazioni sulla sintassi OData supportata da Ricerca di Azure qui.You can find out more about the OData syntax that Azure Search supports here.

Di seguito sono riportati i risultati della query:Here are the results of the query:

ID: 2   Description: Cheapest hotel in town
ID: 3   Description: Close to town hall and the river

Ora, si desidera individuare i due hotel ristrutturati più di recente e visualizzare il nome dell'hotel e la data dell'ultima ristrutturazione.Next, we want to find the top two hotels that have been most recently renovated, and show the hotel name and last renovation date. Il codice è il seguente: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);

In questo caso, viene nuovamente utilizzata la sintassi di OData per specificare il parametro OrderBy come lastRenovationDate desc.In this case, we again use OData syntax to specify the OrderBy parameter as lastRenovationDate desc. È inoltre possibile impostare Top su 2 per avere la certezza che la ricerca restituisca solo i 2 documenti principali.We also set Top to 2 to ensure we only get the top two documents. Come in precedenza, viene impostato Select per specificare quali campi devono essere visualizzati.As before, we set Select to specify which fields should be returned.

Ecco i risultati: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

Si desidera infine trovare tutti gli hotel che corrispondono alla parola "motel":Finally, we want to find all hotels that match the word "motel":

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

WriteDocuments(results);

Di seguito sono riportati i risultati, che includono tutti i campi, poiché non è stata specificata la proprietà Select:And here are the results, which include all fields since we did not specify the Select property:

ID: 2   Base rate: 79.99        Description: Cheapest hotel in town     Description (French): Hôtel le moins cher en ville      Name: Roach Motel       Category: Budget        Tags: [motel, budget]   Parking included: yes   Smoking allowed: yes    Last renovated on: 4/28/1982 12:00:00 AM +00:00 Rating: 1/5     Location: Latitude 49.678581, longitude -122.131577

Questo passaggio completa l'esercitazione.This step completes the tutorial, but don't stop here. Passaggi successivi vengono fornite risorse aggiuntive per ottenere ulteriori informazioni su Ricerca di Azure.Next steps provides additional resources for learning more about Azure Search.

Passaggi successiviNext steps