Caricare dati in Ricerca di Azure tramite .NET SDKUpload data to Azure Search using the .NET SDK

Questo articolo illustra come usare Azure Search .NET SDK per importare dati in un indice di Ricerca di Azure.This article will show you how to use the Azure Search .NET SDK to import data into an Azure Search index.

Prima di iniziare questa procedura dettagliata, è necessario avere creato un indice di Ricerca di Azure.Before beginning this walkthrough, you should already have created an Azure Search index. Questo articolo presuppone anche che sia già stato creato un oggetto SearchServiceClient , come illustrato nell'articolo relativo alla creazione di un indice di Ricerca di Azure con .NET SDK.This article also assumes that you have already created a SearchServiceClient object, as shown in Create an Azure Search index using the .NET SDK.

Nota

Tutto il codice di esempio in questo articolo è scritto in C#.All sample code in this article is written in C#. Il codice sorgente completo è disponibile su GitHub.You can find the full source code on GitHub. Per una descrizione più dettagliata del codice di esempio, vedere le informazioni relative a Azure Search .NET SDK.You can also read about the Azure Search .NET SDK for a more detailed walk through of the sample code.

Per eseguire il push di documenti nell'indice usando .NET SDK, è necessario:In order to push documents into your index using the .NET SDK, you will need to:

  1. Creare un oggetto SearchIndexClient da connettere all'indice di ricerca.Create a SearchIndexClient object to connect to your search index.
  2. Creare un oggetto IndexBatch contenente i documenti da aggiungere, modificare o eliminare.Create an IndexBatch containing the documents to be added, modified, or deleted.
  3. Chiamare il metodo Documents.Index dell'oggetto SearchIndexClient per inviare IndexBatch all'indice di ricerca.Call the Documents.Index method of your SearchIndexClient to send the IndexBatch to your search index.

Creare un'istanza della classe SearchIndexClientCreate an instance of the SearchIndexClient class

Per importare dati nell'indice usando Azure Search .NET SDK, è necessario creare un'istanza della classe SearchIndexClient .To import data into your index using the Azure Search .NET SDK, you will need to create an instance of the SearchIndexClient class. È possibile creare manualmente questa istanza, ma è più semplice se si ha già un'istanza di SearchServiceClient per chiamare il relativo metodo Indexes.GetClient.You can construct this instance yourself, but it's easier if you already have a SearchServiceClient instance to call its Indexes.GetClient method. Ad esempio, ecco come ottenere un oggetto SearchIndexClient per l'indice denominato "hotels" da un oggetto SearchServiceClient denominato serviceClient:For example, here is how you would obtain a SearchIndexClient for the index named "hotels" from a SearchServiceClient named serviceClient:

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. Altre informazioni sulle chiavi di amministrazione e sulle chiavi di query sono disponibili nella documentazione di riferimento relativa all'API REST di Ricerca di Azure.You can find out more about admin keys and query keys in the Azure Search REST API reference.

SearchIndexClient include una proprietà Documents.SearchIndexClient has a Documents property. Questa proprietà fornisce tutti i metodi necessari per aggiungere, modificare, eliminare o eseguire query sui documenti nell'indice.This property provides all the methods you need to add, modify, delete, or query documents in your index.

Decidere quale azione di indicizzazione usareDecide which indexing action to use

Per importare i dati usando .NET SDK, è necessario inserirli in un pacchetto in un oggetto IndexBatch .To import data using the .NET SDK, you will need to package up your data into an IndexBatch object. Un oggetto IndexBatch incapsula una raccolta di oggetti IndexAction, ognuno dei quali contiene un documento e una proprietà che indica a Ricerca di Azure quale azione eseguire sul documento (caricamento, unione, eliminazione e così via).An IndexBatch encapsulates a collection of IndexAction objects, each of which contains a document and a property that tells Azure Search what action to perform on that document (upload, merge, delete, etc). A seconda delle azioni scelte tra le seguenti, per ogni documento devono essere inclusi solo campi specifici:Depending on which of the below actions you choose, only certain fields must be included for each document:

AzioneAction DescriptionDescription Campi necessari per ogni documentoNecessary fields for each document NoteNotes
Upload L'azione Upload è simile a "upsert", in cui il documento viene inserito se è nuovo e aggiornato o sostituito se esiste già.An Upload action is similar to an "upsert" where the document will be inserted if it is new and updated/replaced if it exists. chiave, oltre a tutti gli altri campi da definirekey, plus any other fields you wish to define Quando si aggiorna o si sostituisce un documento esistente, qualsiasi campo non specificato nella richiesta avrà il campo impostato su null.When updating/replacing an existing document, any field that is not specified in the request will have its field set to null. Ciò si verifica anche quando il campo è stato precedentemente impostato su un valore diverso da null.This occurs even when the field was previously set to a non-null value.
Merge Aggiorna un documento esistente con i campi specificati.Updates an existing document with the specified fields. Se il documento non esiste nell'indice, l'unione non riuscirà.If the document does not exist in the index, the merge will fail. chiave, oltre a tutti gli altri campi da definirekey, plus any other fields you wish to define I campi specificati in un'azione di unione sostituiscono i campi esistenti nel documento.Any field you specify in a merge will replace the existing field in the document. Sono inclusi anche i campi di tipo DataType.Collection(DataType.String).This includes fields of type DataType.Collection(DataType.String). Ad esempio, se il documento contiene un campo tags con valore ["budget"] e si esegue un'unione con valore ["economy", "pool"] per tags, il valore finale del campo tags sarà ["economy", "pool"]For example, if the document contains a field tags with value ["budget"] and you execute a merge with value ["economy", "pool"] for tags, the final value of the tags field will be ["economy", "pool"]. e non ["budget", "economy", "pool"].It will not be ["budget", "economy", "pool"].
MergeOrUpload Questa azione si comporta come Merge se nell'indice esiste già un documento con la chiave specificata.This action behaves like Merge if a document with the given key already exists in the index. Se il documento non esiste, si comporta come Upload con un nuovo documento.If the document does not exist, it behaves like Upload with a new document. chiave, oltre a tutti gli altri campi da definirekey, plus any other fields you wish to define -
Delete Rimuove il documento specificato dall'indice.Removes the specified document from the index. solo campo chiavekey only Tutti i campi diversi dal campo chiave specificati verranno ignorati.Any fields you specify other than the key field will be ignored. Se si vuole rimuovere un singolo campo da un documento, usare invece Merge e impostare il campo su Null in modo esplicito.If you want to remove an individual field from a document, use Merge instead and simply set the field explicitly to null.

È possibile specificare l'azione da usare con i vari metodi statici delle classi IndexBatch e IndexAction, come mostrato nella sezione successiva.You can specify what action you want to use with the various static methods of the IndexBatch and IndexAction classes, as shown in the next section.

Costruire IndexBatchConstruct your IndexBatch

Dopo aver appreso quali azioni eseguire sui documenti, è possibile costruire IndexBatch.Now that you know which actions to perform on your documents, you are ready to construct the IndexBatch. L'esempio seguente illustra come creare un batch con alcune azioni.The example below shows how to create a batch with a few different actions. Si noti che questo esempio usa una classe personalizzata denominata Hotel che esegue il mapping a un documento nell'indice "hotels".Note that our example uses a custom class called Hotel that maps to a document in the "hotels" index.

var actions =
    new IndexAction<Hotel>[]
    {
        IndexAction.Upload(
            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)
            }),
        IndexAction.Upload(
            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)
            }),
        IndexAction.MergeOrUpload(
            new Hotel()
            {
                HotelId = "3",
                BaseRate = 129.99,
                Description = "Close to town hall and the river"
            }),
        IndexAction.Delete(new Hotel() { HotelId = "6" })
    };

var batch = IndexBatch.New(actions);

In questo caso vengono usate Upload, MergeOrUpload e Delete come azioni di ricerca, come specificato dai metodi chiamati nella classe IndexAction.In this case, we are using Upload, MergeOrUpload, and Delete as our search actions, as specified by the methods called on the IndexAction class.

Si supponga che l'indice "hotels" di esempio sia già popolato con alcuni documenti.Assume that this example "hotels" index is already populated with a number of documents. Si noti che non è stato necessario specificare tutti i possibili campi dei documenti quando si usa MergeOrUpload e come viene specificata solo la chiave del documento (HotelId) quando si usa Delete.Note how we did not have to specify all the possible document fields when using MergeOrUpload and how we only specified the document key (HotelId) when using Delete.

Si noti anche che è possibile includere solo fino a 1000 documenti in una singola richiesta di indicizzazione.Also, note that you can only include up to 1000 documents in a single indexing request.

Nota

In questo esempio vengono applicate azioni diverse a documenti diversi.In this example, we are applying different actions to different documents. Per eseguire le stesse azioni in tutti i documenti nel batch, invece di chiamare IndexBatch.New si possono usare gli altri metodi statici di IndexBatch.If you wanted to perform the same actions across all documents in the batch, instead of calling IndexBatch.New, you could use the other static methods of IndexBatch. Ad esempio, è possibile creare batch chiamando IndexBatch.Merge, IndexBatch.MergeOrUpload o IndexBatch.Delete.For example, you could create batches by calling IndexBatch.Merge, IndexBatch.MergeOrUpload, or IndexBatch.Delete. Questi metodi accettano una raccolta di documenti (oggetti di tipo Hotel in questo esempio) invece di oggetti IndexAction.These methods take a collection of documents (objects of type Hotel in this example) instead of IndexAction objects.

Importare dati nell'indiceImport data to the index

Dopo aver creato un oggetto IndexBatch inizializzato, è possibile inviarlo all'indice chiamando Documents.Index sull'oggetto SearchIndexClient.Now that you have an initialized IndexBatch object, you can send it to the index by calling Documents.Index on your SearchIndexClient object. L'esempio seguente mostra come chiamare Indexe illustra alcune operazioni aggiuntive che è necessario eseguire:The following example shows how to call Index, as well as some extra steps you will need to perform:

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

Si noti la presenza di try/Index attorno alla chiamata al metodo catch.Note the try/catch surrounding the call to the Index method. Il blocco catch gestisce un caso di errore importante per l'indicizzazione.The catch block 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.

Infine, il codice nell'esempio precedente prevede un ritardo di due secondi.Finally, the code in the example above 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. Si osservi la classe , che esegue il mapping allo schema di indice definito in HotelCreare un indice di Ricerca di Azure con .NET SDK:To help answer that question, let's look at the Hotel class, which maps to the index schema defined in Create an Azure Search index using the .NET SDK:

[SerializePropertyNamesAsCamelCase]
public partial class Hotel
{
    [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; }

    // ToString() method omitted for brevity...
}

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 altri dettagli, vedere Serializzazione personalizzata con JSON.NET.You can find more details in Custom Serialization with JSON.NET. Un esempio è rappresentato dall'uso dell'attributo [JsonProperty] sulla proprietà DescriptionFr nell'esempio di codice riportato sopra.One example of this is the use of the [JsonProperty] attribute on the DescriptionFr property in the sample code above.

Il secondo aspetto importante della classe Hotel è costituita dai tipi di dati delle proprietà pubbliche.The second important thing about the Hotel class are the data types of the public properties. Viene eseguito il mapping dei tipi .NET di queste proprietà 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 DataType.String.For example, the Category string property maps to the category field, which is of type DataType.String. Esistono mapping di tipi simili tra bool? e DataType.Boolean, DateTimeOffset? e DataType.DateTimeOffset e così via.There are similar type mappings between bool? and DataType.Boolean, DateTimeOffset? and DataType.DateTimeOffset, and so forth. 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.The specific rules for the type mapping are documented with the Documents.Get method in the Azure Search .NET SDK reference.

La possibilità di usare classi personalizzate come documenti funziona in entrambe le direzioni. È anche possibile recuperare i risultati della ricerca e fare in modo che l'SDK li deserializzi automaticamente nel tipo che si preferisce, come illustrato nell'articolo seguente.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 shown in the next article.

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. Nel codice di esempio in questo articolo vengono usati solo questi ultimi.Only the latter are used in the sample code in this article.

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

Passaggi successiviNext steps

Dopo il popolamento dell'indice di Ricerca di Azure, si potrà iniziare a eseguire una query per la ricerca di documenti.After populating your Azure Search index, you will be ready to start issuing queries to search for documents. Per informazioni dettagliate, vedere Eseguire query su un indice di Ricerca di Azure .See Query Your Azure Search Index for details.