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

La navigazione 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 ricerca in gruppi di documenti (ad esempio, categorie o marchi) e Ricerca di intelligenza artificiale di Azure fornisce le strutture dei dati e i filtri per supportare l'esperienza.

Questo articolo illustra i passaggi di base per la creazione di una struttura di spostamento in base a facet in Ricerca di intelligenza artificiale di Azure.

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

Il codice nel livello presentazione esegue l'aumento elevato di 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.

Spostamento 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 spostamento in base a facet.

In Ricerca di intelligenza artificiale di Azure i facet sono un livello profondo e non possono essere gerarchici. Se non si ha familiarità con le strutture di spostamento in base a facet, 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.

Screenshot of faceted search results.

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 faceting viene abilitato in base al campo in base al campo in una definizione di indice quando si imposta l'attributo "facetable" su true.

Anche se non è strettamente richiesto, è necessario 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" nei campi di 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 nella navigazione 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 bene in un albero di spostamento

I valori all'interno di un campo e non il nome del campo stesso producono i facet in una struttura di spostamento in base a facet. Se il facet è un campo stringa denominato Color, i facet saranno blu, verde e qualsiasi altro valore per tale campo.

Come procedura consigliata, controllare i campi per i valori Null, gli errori di ortografia o le discrepanze tra maiuscole e minuscole e le versioni singole e plurali della stessa parola. Per impostazione predefinita, i filtri e i facet non vengono sottoposti a analisi lessicali 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 variazioni di maiuscole e minuscole e caratteri.

Impostazioni predefinite in REST e AZURE SDK

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 dei campi in base al tipo di dati. I tipi di dati seguenti sono "filtrabili" e "facetable" per impostazione predefinita:

  • 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 nell'esplorazione 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 prodotto, devono essere impostati su "facetable": false per evitare l'uso accidentale (e inefficace) nella navigazione in base a 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 spostamento in base a facet viene restituita nella parte superiore della risposta. La struttura di una richiesta e di una risposta è piuttosto semplice. In effetti, il lavoro reale dietro la navigazione in base a facet si trova nel livello di 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 spostamento in base a facet avrà come ambito solo le corrispondenze nei risultati, anziché 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 "Categoria" e da un conteggio degli hotel per ognuno di essi. È seguito dal resto dei risultati della ricerca, tagliato qui per brevità. Questo esempio funziona correttamente per diversi motivi. Il numero di facet per questo campo è inferiore al limite (il valore predefinito è 10) in modo che vengano visualizzati tutti 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, ordinamenti e intervalli: count:<integer>, sort:<>interval:<integer>, e values:<list>. Per altri dettagli sui parametri di 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-contare i documenti corrispondenti nella risposta facet.

Per garantire l'accuratezza, è possibile aumentare artificialmente il conteggio:<numero> 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 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 Business Title.

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

Ecco un altro esempio dell'esempio di hotel. 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 la visualizzazione dello 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 demo (anche dalla index.cshtml 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 della navigazione in base a facet in Ricerca di intelligenza artificiale 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 si tratta di un caso d'uso comune, non è qualcosa che la struttura di spostamento in base a facet attualmente fornisce una configurazione 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.

Cancellare i 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 di hotel C# è possibile inviare una ricerca vuota per reimpostare la pagina. Al contrario, l'applicazione di esempio NYCJobs fornisce un'applicazione di esempio clickable [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 la navigazione 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 ci sarà 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

Diversi esempi includono lo spostamento in base a facet. Questa sezione contiene collegamenti agli esempi e annota anche la libreria client e la lingua usate per ognuna di esse.

Aggiungere la ricerca alle app Web (React)

Le esercitazioni e gli 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 di presentazione.

Codice di esempio nycJobs e demo (Ajax)

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