Come modellare tipi di dati complessi in Ricerca di AzureHow to model complex data types in Azure Search

I set di dati esterni usati per popolare un indice di Ricerca di Azure includono talvolta sottostrutture gerarchiche o annidate che non vengono suddivise con precisione in un set di righe tabulare.External datasets used to populate an Azure Search index sometimes include hierarchical or nested substructures that do not break down neatly into a tabular rowset. Gli esempi di strutture di questo tipo possono includere più ubicazioni e numeri di telefono per un singolo cliente, più colori e dimensioni per un singolo SKU, più autori di un singolo libro e così via.Examples of such structures might include multiple locations and phone numbers for a single customer, multiple colors and sizes for a single SKU, multiple authors of a single book, and so on. In termini di modellazione, queste strutture possono essere definite, ad esempio, tipi di dati complessi, tipi di dati composti, tipi di dati compositi o tipi di dati aggregati.In modeling terms, you might see these structures referred to as complex data types, compound data types, composite data types, or aggregate data types, to name a few.

Ricerca di Azure non offre supporto nativo per i tipi di dati complessi. Una soluzione alternativa collaudata include tuttavia un processo in due passaggi con cui la struttura viene resa flat e quindi viene usato un tipo di dati Collection per ricostituire la struttura interna.Complex data types are not natively supported in Azure Search, but a proven workaround includes a two-step process of flattening the structure and then using a Collection data type to reconstitute the interior structure. Seguendo la tecnica descritta in questo articolo, il contenuto risulta disponibile per l'esecuzione di ricerche, facet, l'applicazione di filtri e l'ordinamento.Following the technique described in this article allows the content to be searched, faceted, filtered, and sorted.

Esempio di struttura di dati complessaExample of a complex data structure

I dati in questione si trovano in genere come set di documenti JSON o XML oppure come elementi di un archivio NoSQL, ad esempio Azure Cosmos DB.Typically, the data in question resides as a set of JSON or XML documents, or as items in a NoSQL store such as Azure Cosmos DB. Strutturalmente, il problema deriva dalla presenza di più elementi figlio su cui è necessario eseguire ricerche e applicare filtri.Structurally, the challenge stems from having multiple child items that need to be searched and filtered. Come punto di partenza per illustrare la soluzione alternativa verrà usato il documento JSON seguente che elenca una serie di contatti di esempio:As a starting point for illustrating the workaround, take the following JSON document that lists a set of contacts as an example:

[
  {
    "id": "1",
    "name": "John Smith",
    "company": "Adventureworks",
    "locations": [
      {
        "id": "1",
        "description": "Adventureworks Headquarters"
      },
      {
        "id": "2",
        "description": "Home Office"
      }
    ]
  }, 
  {
    "id": "2",
    "name": "Jen Campbell",
    "company": "Northwind",
    "locations": [
      {
        "id": "3",
        "description": "Northwind Headquarter"
      },
      {
        "id": "4",
        "description": "Home Office"
      }
    ]
}]

Mentre per i campi denominati "id", "name" e "company" è possibile eseguire facilmente il mapping uno-a-uno come campi in un indice di Ricerca di Azure, il campo "locations" contiene una matrice di ubicazioni che include sia un set di ID ubicazione sia le descrizioni delle ubicazioni.While the fields named ‘id’, ‘name’ and ‘company’ can easily be mapped one-to-one as fields within an Azure Search index, the ‘locations’ field contains an array of locations, having both a set of location IDs as well as location descriptions. Dato che Ricerca di Azure non ha un tipo di dati che supporta questa caratteristica, è necessario modellare i dati in modo diverso in Ricerca di Azure.Given that Azure Search does not have a data type that supports this, we need a different way to model this in Azure Search.

Nota

Questa tecnica è descritto da Kirk Evans in un post di blog indicizzazione Azure Cosmos DB con ricerca di Azure, che illustra una tecnica denominata "rendere bidimensionali i dati", che è un campo denominato locationsID e locationsDescription che sono entrambi raccolte (o una matrice di stringhe).This technique is also described by Kirk Evans in a blog post Indexing Azure Cosmos DB with Azure Search, which shows a technique called "flattening the data", whereby you would have a field called locationsID and locationsDescription that are both collections (or an array of strings).

Parte 1: Rendere flat la matrice convertendola in singoli campiPart 1: Flatten the array into individual fields

Per creare un indice di Ricerca di Azure appropriato per questo set di dati, creare singoli campi per la sottostruttura annidata, locationsID e locationsDescription, con il tipo di dati delle raccolte, ovvero una matrice di stringhe.To create an Azure Search index that accommodates this dataset, create individual fields for the nested substructure: locationsID and locationsDescription with a data type of collections (or an array of strings). In questi campi verranno indicizzati i valori "1" e "2" nel campo locationsID per John Smith e i valori "3" e "4" nel campo locationsID per Jen Campbell.In these fields you would index the values ‘1’ and ‘2’ into the locationsID field for John Smith and the values ‘3’ & ‘4’ into the locationsID field for Jen Campbell.

I dati in Ricerca di Azure si presenteranno come segue:Your data within Azure Search would look like this:

Dati di esempio, 2 righe

Parte 2: Aggiungere un campo di tipo raccolta nella definizione dell'indicePart 2: Add a collection field in the index definition

Nello schema dell'indice, le definizioni dei campi possono avere un aspetto simile al seguente.In the index schema, the field definitions might look similar to this example.

var index = new Index()
{
    Name = indexName,
    Fields = new[]
    {
        new Field("id", DataType.String) { IsKey = true },
        new Field("name", DataType.String) { IsSearchable = true, IsFilterable = false, IsSortable = false, IsFacetable = false },
        new Field("company", DataType.String) { IsSearchable = true, IsFilterable = false, IsSortable = false, IsFacetable = false },
        new Field("locationsId", DataType.Collection(DataType.String)) { IsSearchable = true, IsFilterable = true, IsFacetable = true },
        new Field("locationsDescription", DataType.Collection(DataType.String)) { IsSearchable = true, IsFilterable = true, IsFacetable = true }
    }
};

Convalidare i comportamenti di ricerca ed estendere facoltativamente l'indiceValidate search behaviors and optionally extend the index

Supponendo che l'indice sia stato creato e che i dati siano stati caricati, è ora possibile testare la soluzione per verificare l'esecuzione di query di ricerca sul set di dati.Assuming you created the index and loaded the data, you can now test the solution to verify search query execution against the dataset. Ogni campo di tipo raccolta dovrà essere ricercabile, filtrabile e con facet.Each collection field should be searchable, filterable and facetable. Dovrebbe essere possibile eseguire query come le seguenti:You should be able to run queries like:

  • Trovare tutte le persone che lavorano presso "Adventureworks Headquarters".Find all people who work at the ‘Adventureworks Headquarters’.
  • Ottenere il conteggio del numero di persone che lavorano in "Home Office".Get a count of the number of people who work in a ‘Home Office’.
  • In relazione alle persone che lavorano in "Home Office", visualizzare gli altri uffici in cui lavorano con un conteggio delle persone in ogni ubicazione.Of the people who work at a ‘Home Office’, show what other offices they work along with a count of the people in each location.

Questa tecnica si rivela inadeguata quando è necessario eseguire una ricerca che combina sia l'ID che la descrizione dell'ubicazione,Where this technique falls apart is when you need to do a search that combines both the location id as well as the location description. Ad esempio: For example:

  • Trovare tutte le persone con "Home Office" E con ID ubicazione 4.Find all people where they have a Home Office AND has a location ID of 4.

Come si ricorderà, il contenuto originale si presentava come segue:If you recall the original content looked like this:

   {
        id: '4',
        description: 'Home Office'
   }

Ora che i dati sono stati separati in campi distinti, tuttavia, non è possibile sapere se il valore "Home Office" per Jen Campbell è correlato a locationsID 3 o locationsID 4.However, now that we have separated the data into separate fields, we have no way of knowing if the Home Office for Jen Campbell relates to locationsID 3 or locationsID 4.

Per gestire questo caso, definire un altro campo dell'indice che combina tutti i dati in una singola raccolta.To handle this case, define another field in the index that combines all of the data into a single collection. In questo esempio, il campo verrà denominato locationsCombined e il contenuto verrà separato con ||. È tuttavia possibile scegliere qualsiasi separatore che si ritiene possa essere una serie di caratteri univoca per il contenuto specifico.For our example, we will call this field locationsCombined and we will separate the content with a || although you can choose any separator that you think would be a unique set of characters for your content. Ad esempio: For example:

Dati di esempio, 2 righe con separatore

Usando il campo locationsCombined , è ora possibile supportare query aggiuntive, ad esempio:Using this locationsCombined field, we can now accommodate even more queries, such as:

  • Visualizzare un conteggio delle persone che lavorano in "Home Office" con ID ubicazione "4".Show a count of people who work at a ‘Home Office’ with location Id of ‘4’.
  • Cercare le persone che lavorano in "Home Office" con ID ubicazione "4".Search for people who work at a ‘Home Office’ with location Id ‘4’.

LimitazioniLimitations

Questa tecnica è utile per diversi scenari, ma non è applicabile in tutti i casi.This technique is useful for a number of scenarios, but it is not applicable in every case. Ad esempio: For example:

  1. Se il tipo di dati complesso non include un set statico di campi e non è stato possibile eseguire il mapping di tutti i tipi possibili a un singolo campo.If you do not have a static set of fields in your complex data type and there was no way to map all the possible types to a single field.
  2. L'aggiornamento degli oggetti annidati richiede operazioni aggiuntive per determinare esattamente gli elementi da aggiornare nell'indice di Ricerca di Azure.Updating the nested objects requires some extra work to determine exactly what needs to be updated in the Azure Search index

Codice di esempioSample code

Un esempio dell'indicizzazione di un set di dati JSON complesso in Ricerca di Azure e dell'esecuzione di diverse query su tale set di dati è disponibile in questo repository GitHub.You can see an example on how to index a complex JSON data set into Azure Search and perform a number of queries over this dataset at this GitHub repo.

Passaggio successivoNext step

Votare per il supporto nativo di tipi di dati complessi nella pagina UserVoice relativa a Ricerca di Azure e fornire eventuali input aggiuntivi da prendere in considerazione in merito all'implementazione di questa funzionalità.Vote for native support for complex data types on the Azure Search UserVoice page and provide any additional input that you’d like us to consider regarding feature implementation. È anche possibile contattare direttamente @liamca su Twitter.You can also reach out to me directly on Twitter at @liamca.