Aggiungere lo spostamento in base a facet a un'app di ricerca

L'esplorazione in base a facet viene usata per filtrare in modo automatico i risultati delle query in un'app di ricerca, in cui l'applicazione offre controlli modulo per la definizione dell'ambito della ricerca a gruppi di documenti (ad esempio categorie o marchi) e Ricerca cognitiva di Azure fornisce le strutture dei dati e i filtri per supportare l'esperienza.

In questo articolo vengono illustrati i passaggi di base per la creazione di una struttura di spostamento in base a facet in Ricerca cognitiva di Azure.

  • Impostare gli attributi del campo nell'indice
  • Strutturare la richiesta e la risposta
  • Aggiungere controlli di spostamento e filtri nel livello presentazione

Il codice nel livello presentazione comporta l'aumento elevato in un'esperienza di spostamento in base a facet. Le demo e gli esempi elencati alla fine di questo articolo forniscono codice funzionante che illustra come riunire tutti gli elementi.

Navigazione in base a facet in una pagina di ricerca

I facet sono dinamici e vengono restituiti in seguito a una query. Una risposta di ricerca include tutte le categorie di facet usate per esplorare i documenti nel risultato. La query viene eseguita per prima e quindi i facet vengono estratti dai risultati correnti e assemblati in una struttura di navigazione in base a facet.

In Ricerca cognitiva i facet sono un livello profondo e non possono essere gerarchici. Se non si ha familiarità con lo spostamento in base a facet strutturato, l'esempio seguente mostra uno a sinistra. I conteggi indicano il numero di corrispondenze per ogni facet. Lo stesso documento può essere rappresentato in più facet.

risultati della ricerca in facet

I facet aiutano a trovare ciò che si sta cercando con una percentuale di successo accertata. Per gli sviluppatori, i facet consentono di esporre i criteri di ricerca più utili per l’esplorazione dell’indice di ricerca.

Abilitare i facet nell'indice

Il facet è abilitato per campo in base al campo in una definizione di indice quando si imposta l'attributo "facetable" su true.

Anche se non è strettamente obbligatorio, è consigliabile impostare anche l'attributo "filtrabile" in modo da poter compilare i filtri necessari che supportino l'esperienza di spostamento in base a facet nell'applicazione di ricerca.

L'esempio seguente dell'indice di esempio "hotels" mostra "facetable" e "filtrabile" in campi con cardinalità bassa che contengono singoli valori o frasi brevi: "Category", "Tags", "Rating".

{
  "name": "hotels",  
  "fields": [
    { "name": "hotelId", "type": "Edm.String", "key": true, "searchable": false, "sortable": false, "facetable": false },
    { "name": "Description", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
    { "name": "HotelName", "type": "Edm.String", "facetable": false },
    { "name": "Category", "type": "Edm.String", "filterable": true, "facetable": true },
    { "name": "Tags", "type": "Collection(Edm.String)", "filterable": true, "facetable": true },
    { "name": "Rating", "type": "Edm.Int32", "filterable": true, "facetable": true },
    { "name": "Location", "type": "Edm.GeographyPoint" }
  ]
}

Scelta dei campi

I facet possono essere calcolati su campi a valore singolo e raccolte. I campi che funzionano meglio nell'esplorazione in base a facet hanno queste caratteristiche:

  • Cardinalità bassa (un numero ridotto di valori distinti che si ripetono in tutti i documenti nel corpus di ricerca)

  • Valori descrittivi brevi (una o due parole) che verranno visualizzati in modo ottimale in un albero di navigazione

Il contenuto di un campo e non del campo stesso produce i facet in una struttura di navigazione in base a facet. Se il facet è un campo stringa Color, i facet saranno blu, verde e qualsiasi altro valore per tale campo.

Come procedura consigliata, controllare i campi per valori Null, errori di ortografia o discrepanze tra maiuscole e minuscole e versioni singole e plurali della stessa parola. Per impostazione predefinita, i filtri e i facet non vengono sottoposti a analisi lessicale o controllo ortografico, il che significa che tutti i valori di un campo "facetable" sono potenziali facet, anche se le parole differiscono per un carattere. Facoltativamente, è possibile assegnare un normalizzatore a un campo "filtrabile" e "facetable" per uniformare le varianti di maiuscole e minuscole e caratteri.

Impostazioni predefinite negli SDK REST e Azure

Se si usa uno degli SDK di Azure, il codice deve impostare in modo esplicito gli attributi del campo. Al contrario, l'API REST ha le impostazioni predefinite per gli attributi di campo in base al tipo di dati. Per impostazione predefinita, i tipi di dati seguenti sono "filtrabili" e "facetable":

  • Edm.String
  • Edm.DateTimeOffset
  • Edm.Boolean
  • Edm.Int32, Edm.Int64, Edm.Double
  • Raccolte di uno dei tipi precedenti, ad esempio Collection(Edm.String) o Collection(Edm.Double)

Non è possibile usare Edm.GeographyPoint o Collection(Edm.GeographyPoint) campi nella struttura di spostamento in base a facet. I facet funzionano meglio nei campi con cardinalità bassa. A causa della risoluzione delle coordinate geografiche, è raro che due set di coordinate siano uguali in un determinato set di dati. Di conseguenza, i facet non sono supportati per le coordinate geografiche. È necessario un campo città o regione per esplorare una posizione in base a facet.

Suggerimento

Disattivare i facet per i campi che non devono essere usati come facet è una procedura consigliata per l'ottimizzazione delle prestazioni e dell'archiviazione. In particolare, i campi stringa per valori univoci, ad esempio un ID o un nome di prodotto, devono essere impostati su per evitare l'uso accidentale (e inefficace) nell'esplorazione in base a "facetable": false facet. Ciò vale soprattutto per l'API REST che abilita i filtri e i facet per impostazione predefinita.

Richiesta e risposta facet

I facet vengono specificati nella query e la struttura di navigazione in base a facet viene restituita nella parte superiore della risposta. La struttura di una richiesta e di una risposta è abbastanza semplice. In realtà, il lavoro reale dietro la navigazione in base a facet si trova nel livello presentazione, trattato in una sezione successiva.

L'esempio REST seguente è una query non qualificata ("search": "*") con ambito per l'intero indice (vedere l'esempio di hotel predefiniti). I facet sono in genere un elenco di campi, ma questa query mostra solo una per una risposta più leggibile di seguito.

POST https://{{service_name}}.search.windows.net/indexes/hotels/docs/search?api-version={{api_version}}
{
    "search": "*",
    "queryType": "simple",
    "select": "",
    "searchFields": "",
    "filter": "",
    "facets": [ "Category"], 
    "orderby": "",
    "count": true
}

È utile inizializzare una pagina di ricerca con una query aperta per compilare completamente la struttura di spostamento in base a facet. Non appena si passano termini di query nella richiesta, la struttura di navigazione in base a facet verrà definita come ambito solo per le corrispondenze nei risultati, anziché per l'intero indice.

La risposta per l'esempio precedente include la struttura di spostamento in base a facet nella parte superiore. La struttura è costituita da valori "Category" e da un conteggio degli hotel per ognuno di essi. È seguito dal resto dei risultati della ricerca, tagliati qui per brevità. Questo esempio funziona correttamente per diversi motivi. Il numero di facet per questo campo rientra nel limite (il valore predefinito è 10) in modo che tutti vengano visualizzati e ogni hotel nell'indice di 50 hotel è rappresentato esattamente in una di queste categorie.

{
    "@odata.context": "https://demo-search-svc.search.windows.net/indexes('hotels')/$metadata#docs(*)",
    "@odata.count": 50,
    "@search.facets": {
        "Category": [
            {
                "count": 13,
                "value": "Budget"
            },
            {
                "count": 12,
                "value": "Resort and Spa"
            },
            {
                "count": 9,
                "value": "Luxury"
            },
            {
                "count": 7,
                "value": "Boutique"
            },
            {
                "count": 5,
                "value": "Suite"
            },
            {
                "count": 4,
                "value": "Extended-Stay"
            }
        ]
    },
    "value": [
        {
            "@search.score": 1.0,
            "HotelId": "1",
            "HotelName": "Secret Point Motel",
            "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 Time's 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.",
            "Category": "Boutique",
            "Tags": [
                "pool",
                "air conditioning",
                "concierge"
            ],
            "ParkingIncluded": false,
  . . .

Sintassi facet

Un parametro di query facet è impostato su un elenco delimitato da virgole di campi "facetable" e, a seconda del tipo di dati, può essere ulteriormente parametrizzato per impostare conteggi, ordini e intervalli: count:<integer>, sort:<>, interval:<integer>e values:<list>. Per altre informazioni sui parametri facet, vedere "Parametri di query" nell'API REST.

POST https://{{service_name}}.search.windows.net/indexes/hotels/docs/search?api-version={{api_version}}
{
    "search": "*",
    "facets": [ "Category", "Tags,count:5", "Rating,values:1|2|3|4|5"],
    "count": true
}

Per ogni albero di spostamento in base a facet, esiste un limite predefinito dei primi dieci facet. Questa impostazione predefinita risulta utile per le strutture di navigazione perché mantiene l'elenco di valori a un livello gestibile. È possibile eseguire l'override dell'impostazione predefinita assegnando un valore a "count". Ad esempio, "Tags,count:5" riduce il numero di tag nella sezione Tag ai primi cinque.

Solo per i valori Numeric e DateTime, è possibile impostare in modo esplicito i valori nel campo facet (ad esempio facet=Rating,values:1|2|3|4|5) per separare i risultati in intervalli contigui (intervalli basati su valori numerici o periodi di tempo). In alternativa, è possibile aggiungere "interval", come in facet=Rating,interval:1.

Ogni intervallo viene compilato usando 0 come punto di partenza e un valore dall'elenco come endpoint e quindi privato dell'intervallo precedente per creare intervalli discreti.

Discrepanze nei conteggi dei facet

In determinate circostanze, è possibile che i conteggi dei facet non siano completamente accurati a causa dell'architettura di partizionamento orizzontale. Ogni indice di ricerca viene distribuito tra più partizioni e ogni partizione segnala i primi N facet in base al numero di documenti, che vengono quindi combinati in un singolo risultato. Poiché si tratta solo dei primi N facet per ogni partizione, è possibile perdere o sotto-conteggio i documenti corrispondenti nella risposta facet.

Per garantire l'accuratezza, è possibile aumentare artificialmente il valore count:<number> a un numero elevato per forzare la creazione di report completi da ogni partizione. È possibile specificare "count": "0" per facet illimitati. In alternativa, è possibile impostare "count" su un valore maggiore o uguale al numero di valori univoci del campo in base a facet. Ad esempio, se si esegue il facet in base a un campo "size" con cinque valori univoci, è possibile impostare "count:5" per assicurarsi che tutte le corrispondenze siano rappresentate nella risposta del facet.

Il compromesso con questa soluzione alternativa è maggiore la latenza delle query, quindi usarlo solo quando necessario.

Livello di presentazione

Nel codice dell'applicazione, il modello consiste nell'usare parametri di query di facet per restituire la struttura di navigazione in base a facet con i risultati di facet, oltre a un'espressione $filter. L'espressione di filtro gestisce l'evento click e restringe ulteriormente il risultato della ricerca in base alla selezione del facet.

Combinazione di facet e filtro

Il frammento di codice seguente del JobsSearch.cs file nella demo NYCJobs aggiunge il titolo business selezionato al filtro se si seleziona un valore dal facet Titolo business.

if (businessTitleFacet != "")
  filter = "business_title eq '" + businessTitleFacet + "'";

Ecco un altro esempio dell'esempio hotels. Il frammento di codice seguente aggiunge categoyrFacet al filtro se un utente seleziona un valore dal facet della categoria.

if (!String.IsNullOrEmpty(categoryFacet))
    filter = $"category eq '{categoryFacet}'";

HTML per lo spostamento in base a facet

L'esempio seguente, tratto dal index.cshtml file dell'applicazione di esempio NYCJobs, mostra la struttura HTML statica per visualizzare lo spostamento in base a facet nella pagina dei risultati della ricerca. L'elenco di facet viene compilato o ricompilato in modo dinamico quando si invia un termine di ricerca oppure si seleziona o deseleziona un facet.

<div class="widget sidebar-widget jobs-filter-widget">
  <h5 class="widget-title">Filter Results</h5>
    <p id="filterReset"></p>
    <div class="widget-content">

      <h6 id="businessTitleFacetTitle">Business Title</h6>
      <ul class="filter-list" id="business_title_facets">
      </ul>

      <h6>Location</h6>
      <ul class="filter-list" id="posting_type_facets">
      </ul>

      <h6>Posting Type</h6>
      <ul class="filter-list" id="posting_type_facets"></ul>

      <h6>Minimum Salary</h6>
      <ul class="filter-list" id="salary_range_facets">
      </ul>

  </div>
</div>

Compilare CODICE HTML in modo dinamico

Il frammento di codice seguente della index.cshtml demo (anche dalla demo NYCJobs) compila dinamicamente il codice HTML per visualizzare il primo facet, Business Title. Funzioni simili compilano in modo dinamico il codice HTML per gli altri facet. Ogni facet contiene un'etichetta e un conteggio, che visualizza il numero di elementi trovati per questo risultato di facet.

function UpdateBusinessTitleFacets(data) {
  var facetResultsHTML = '';
  for (var i = 0; i < data.length; i++) {
    facetResultsHTML += '<li><a href="javascript:void(0)" onclick="ChooseBusinessTitleFacet(\'' + data[i].Value + '\');">' + data[i].Value + ' (' + data[i].Count + ')</span></a></li>';
  }

  $("#business_title_facets").html(facetResultsHTML);
}

Suggerimenti per l'uso di facet

Questa sezione è una raccolta di suggerimenti e soluzioni alternative che potrebbero essere utili.

Mantenere una struttura di esplorazione in base a facet in modo asincrono per i risultati filtrati

Una delle sfide dell'esplorazione in base a facet in Ricerca cognitiva di Azure è che i facet esistono solo per i risultati correnti. Nella pratica, si conserva in genere un set statico di facet in modo che l'utente possa spostarsi in ordine inverso, ripercorrendo i passaggi per esplorare percorsi alternativi nel contenuto della ricerca.

Anche se questo è un caso d'uso comune, non è qualcosa che la struttura di spostamento in base a facet attualmente fornisce predefinita. Gli sviluppatori che intendono usare i facet statici aggirandone i limiti eseguono due query filtrate: una limitata ai risultati, l'altra usata per creare un elenco statico di facet per l'esplorazione.

Cancella facet

Quando si progetta la pagina dei risultati di ricerca, ricordarsi di aggiungere un meccanismo per la cancellazione dei facet. Se si aggiungono caselle di controllo, gli utenti possono facilmente intuire come cancellare i filtri. Per altri layout potrebbe essere necessario un modello di navigazione o un altro approccio creativo. Nell'esempio C# hotels è possibile inviare una ricerca vuota per reimpostare la pagina. Al contrario, l'applicazione di esempio NYCJobs fornisce un elemento selezionabile [X] dopo un facet selezionato per cancellare il facet, ovvero una coda visiva più forte per l'utente.

Ridurre i risultati di facet con altri filtri

I risultati facet sono documenti trovati nei risultati della ricerca che corrispondono a un termine di facet. Nell'esempio seguente, nei risultati della ricerca per il cloud computing, i 254 elementi dispongono inoltre di una specifica interna come tipo di contenuto. Gli elementi non si escludono necessariamente a vicenda. Se un elemento soddisfa i criteri di entrambi i filtri, viene conteggiato in ognuno di essi. Questa duplicazione è possibile quando si esegue l'esplorazione in base a facet su campi Collection(Edm.String) che vengono spesso usati per implementare l'aggiunta di tag nel documento.

Search term: "cloud computing"
Content type
   Internal specification (254)
   Video (10)

In generale, se si ritiene che i risultati dei facet siano costantemente troppo elevati, è consigliabile che aggiungere altri filtri per offrire agli utenti più opzioni per restringere la ricerca.

Un'esperienza di ricerca solo facet

Se l'applicazione usa esclusivamente l'esplorazione in base a facet (ovvero nessuna casella di ricerca), è possibile contrassegnare il campo come searchable=false, filterable=truefacetable=true per produrre un indice più compatto. L'indice non includerà indici invertiti e non sarà presente alcuna analisi del testo o tokenizzazione. I filtri vengono eseguiti in base alle corrispondenze esatte a livello di carattere.

Convalidare gli input in fase di query

Se si compila l'elenco di facet dinamicamente in base all'input dell'utente non attendibile, verificare che i nomi dei campi in base ai facet siano validi. In alternativa, escluderne i nomi durante la creazione degli URL usando Uri.EscapeDataString() in .NET o l'equivalente nella piattaforma preferita.

Demo ed esempi

Alcuni esempi includono la navigazione in base a facet. In questa sezione sono disponibili collegamenti agli esempi e vengono anche indicati la libreria client e il linguaggio usati per ognuno di essi.

Creare la prima app in C# (Razor)

Questa esercitazione e serie di esempio in C# include una lezione incentrata sulla navigazione in base a facet. La soluzione è un'app MVC ASP.NET e il livello presentazione usa le librerie client Razor.

Aggiungere la ricerca alle app Web (React)

Esercitazioni ed esempi in C#, Python e JavaScript includono navigazione in base a facet, nonché filtri, suggerimenti e completamento automatico. Questi esempi usano React per il livello presentazione.

Codice di esempio nycJobs e demo (Ajax)

L'esempio NYCJobs è un'applicazione MVC ASP.NET che usa Ajax nel livello presentazione. È disponibile come app demo live e come codice sorgente nel repository Azure-Samples in GitHub.