Ricerca full-text in Ricerca cognitiva di Azure

Questo articolo è destinato agli sviluppatori che necessitano di una conoscenza più approfondita del funzionamento della ricerca full-text di Apache Lucene in Ricerca cognitiva di Azure. Per le query di testo, Ricerca cognitiva di Azure fornirà i risultati previsti nella maggior parte degli scenari, ma in alcuni casi è possibile ottenere un risultato che sembrerà in qualche modo "strano". In questi casi, la presenza di uno sfondo nelle quattro fasi dell'esecuzione di query di Lucene (analisi delle query, analisi lessicale, abbinamento dei documenti, assegnazione dei punteggi) consente di identificare le modifiche specifiche per i parametri di query o per la configurazione di indice che garantirà il risultato desiderato.

Nota

Ricerca cognitiva di Azure usa Apache Lucene per la ricerca full-text, ma l'integrazione di Lucene non è esaustiva. La funzionalità Lucene viene esposta e estesa in modo selettivo per abilitare gli scenari importanti per Ricerca cognitiva di Azure.

Panoramica e diagramma dell'architettura

L'esecuzione di query ha quattro fasi:

  1. Analisi della query
  2. Analisi lessicale
  3. Recupero dei documenti
  4. Assegnazione dei punteggi

Una query di ricerca full-text inizia con l'analisi del testo della query per estrarre i termini e gli operatori di ricerca. Esistono due parser in modo che sia possibile scegliere tra velocità e complessità. Una fase di analisi è successiva, in cui i singoli termini di query sono talvolta suddivisi e ricostituiti in nuove forme per eseguire un cast più ampio su ciò che potrebbe essere considerato come una potenziale corrispondenza. Il motore di ricerca analizza quindi l'indice per trovare documenti con termini corrispondenti e punteggi ogni corrispondenza. Un set di risultati viene quindi ordinato sulla base di un punteggio di pertinenza assegnato a ogni singolo documento corrispondente. Quelli nella parte superiore dell'elenco di pertinenza vengono restituiti all'applicazione chiamante.

Il diagramma seguente illustra i componenti usati per elaborare una richiesta di ricerca.

Diagramma dell'architettura di query Lucene in Ricerca cognitiva di Azure.

Componenti chiave Descrizione funzionale
Parser della query Separare i termini della query dagli operatori della query e creare la struttura della query (un albero di query) da inviare al motore di ricerca.
Analizzatori Eseguire l'analisi lessicale sui termini della query. Questo processo può implicare la trasformazione, la rimozione o l'espansione dei termini della query.
Index Una struttura efficiente dei dati usata per archiviare e organizzare i termini ricercabili estratti da documenti indicizzati.
Motore di ricerca Recupera e assegna i punteggi di documenti corrispondenti in base al contenuto dell'indice invertito.

Anatomia di una richiesta di ricerca

Una richiesta di ricerca è una specifica completa di ciò che deve essere restituito in un set di risultati. In forma più semplice, è una query vuota senza criteri di qualsiasi tipo. Un esempio più realistico include parametri, termini di query diversi, forse con ambito in determinati campi, con eventualmente un'espressione filtro e regole di ordinamento.

L'esempio seguente è una richiesta di ricerca che è possibile inviare a Ricerca cognitiva di Azure usando l'API REST.

POST /indexes/hotels/docs/search?api-version=2020-06-30
{
    "search": "Spacious, air-condition* +\"Ocean view\"",
    "searchFields": "description, title",
    "searchMode": "any",
    "filter": "price ge 60 and price lt 300",
    "orderby": "geo.distance(location, geography'POINT(-159.476235 22.227659)')", 
    "queryType": "full" 
}

Per questa richiesta, il motore di ricerca esegue le operazioni seguenti:

  1. Filtra i documenti in cui il prezzo è almeno $ 60 e meno di $ 300.

  2. Esegue la query. In questo esempio la query di ricerca è costituita da frasi e termini: "Spacious, air-condition* +\"Ocean view\"" (gli utenti in genere non immettono segni di punteggiatura, ma includerli nell'esempio consente di spiegare come la gestiscono gli analizzatori).

    Per questa query, il motore di ricerca analizza i campi della descrizione e del titolo specificati in "searchFields" per i documenti che contengono "Ocean view", e inoltre sul termine "spacious"o sui termini che iniziano con il prefisso "air-condition". Il parametro "searchMode" viene usato per corrispondere a qualsiasi termine (impostazione predefinita) o a tutti, per i casi in cui un termine non è richiesto in modo esplicito (+).

  3. Ordina il set risultante di hotel in base alla prossimità a una determinata posizione geografica e quindi restituisce i risultati all'applicazione chiamante.

La maggior parte di questo articolo riguarda l'elaborazione della query di ricerca: "Spacious, air-condition* +\"Ocean view\"". Il filtro e l'ordinamento non appartengono all'ambito. Per altre informazioni, vedere la Documentazione di riferimento dell'API di Ricerca.

Fase 1: Analisi della query

Come accennato, la stringa della query è la prima riga della richiesta:

 "search": "Spacious, air-condition* +\"Ocean view\"", 

Il parser della query separa gli operatori (ad esempio * e + nell'esempio) dalla ricerca di termini e annulla la costruzione della query di ricerca in sottoquery di un tipo supportato:

  • query del termine per i termini singoli (ad esempio spazioso)
  • query della frase per i termini tra virgolette (ad esempio vista oceano)
  • query del prefisso per i termini seguiti da un operatore prefisso * (ad esempio, aria condizionata)

Per un elenco completo dei tipi di query supportati vedere Sintassi di query Lucene

Gli operatori associati a una sottoquery determinano se la query "deve essere" o "dovrebbe essere" soddisfatta al fine di considerare un documento come corrispondenza. Ad esempio, +"Ocean view" è "deve" in virtù dell'operatore +.

Il parser della query ristruttura le sottoquery in un albero della query (una struttura interna che rappresenta la query) e la trasmette al motore di ricerca. Nella prima fase di analisi della query, l'albero della query appare come segue.

Diagramma concettuale di una query booleana con searchmode impostata su qualsiasi.

Parser supportati: Lucene semplice e completa

Ricerca cognitiva di Azure espone due diversi linguaggi di query, simple (impostazione predefinita) e full. Impostando il parametro queryType con la richiesta di ricerca, si indica al parser della query quale linguaggio di query si sceglie in modo che sappia come interpretare gli operatori e la sintassi.

  • Il linguaggio semplice della query è intuitivo e potente, spesso adatto a interpretare l'input dell'utente così come si presenta, senza elaborazione dal lato client. Supporta operatori di query familiari dai motori di ricerca Web.

  • Il Linguaggio di query Lucene Full, che si ottiene impostando queryType=full, estende il linguaggio di query semplice di impostazione predefinita aggiungendo il supporto per più operatori e tipi di query quali carattere jolly, fuzzy, regex e query con ambito campo. Ad esempio, un'espressione regolare inviata nella sintassi di query semplice verrebbe interpretata come una stringa di query e non come un'espressione. La richiesta di esempio in questo articolo usa il linguaggio di query Lucene Full.

Impatto di searchMode sul parser

Un altro parametro della richiesta di ricerca che influisce sull'analisi è il parametro "searchMode". Questo parametro controlla l'operatore predefinito per le query booleane: qualsiasi (predefinita) o tutte.

Quando "searchMode=any", ovvero il valore predefinito, il delimitatore di spazio tra spazioso e aria condizionata è OR (||), rendendo il testo della query di esempio equivalente a:

Spacious,||air-condition*+"Ocean view" 

Gli operatori espliciti, ad esempio + in +"Ocean view", non sono ambigui nella costruzione di query booleana (il termine deve corrispondere). Come interpretare i termini rimanenti è meno ovvio: spazioso e aria condizionata. Il motore di ricerca dovrebbe trovare corrispondenze su vista sull'oceano and spazioso and aria condizionata? O dovrebbe trovare vista sull'oceano più uno dei termini rimanenti?

Per impostazione predefinita ("searchMode=any"), il motore di ricerca presuppone l'interpretazione più ampia. Uno dei campi dovrebbe essere associato, riflettendo la semantica di "or". L'albero della query iniziale illustrato in precedenza, con due operazioni "should", mostra il valore predefinito.

Si supponga di impostare "searchMode=all". In questo caso, lo spazio viene interpretato come un'operazione "and". Ciascuno dei termini rimanenti deve essere presente nel documento per essere considerato come corrispondenza. La query di esempio risultante verrebbe interpretata nel modo seguente:

+Spacious,+air-condition*+"Ocean view"

Un albero di query modificato per questa query sarà come segue, se un documento corrispondente è l'intersezione di tutte e tre le sottoquery:

Diagramma concettuale di una query booleana con searchmode impostato su tutti.

Nota

La scelta di "searchMode=any" su "searchMode=all" è una decisione migliore arrivata eseguendo query rappresentative. Gli utenti che potrebbero includere operatori (comuni durante la ricerca negli archivi documenti) potrebbero trovare risultati più intuitivi se "searchMode=all" informa i costrutti di query booleani. Per altre informazioni sull'interazione tra operatori e "searchMode", vedere Sintassi di query semplice.

Fase 2: Analisi lessicale

Gli analizzatori lessicali elaborano le query del termine e le query della frase dopo aver strutturato l'albero della query. Un analizzatore accetta gli input di testo assegnati dal parser, elabora il testo e quindi invia nuovamente in formato token i termini da incorporare nell'albero della query.

La forma più comune di analisi lessicale è *analisi linguistica che trasforma i termini di query in base alle regole specifiche di un determinato linguaggio:

  • Riduzione di un termine della query nella forma radice di una parola
  • Rimozione di parole non essenziali (parole non essenziali, ad esempio "the" o "and" in inglese)
  • Suddivisione di una parola composta nelle parti che la compongono
  • Trasformazione in lettere minuscole di una parola in lettere maiuscole

Tutte queste operazioni tendono a cancellare le differenze tra l'input del testo fornito dall'utente e i termini archiviati nell'indice. Tali operazioni vanno oltre l'elaborazione del testo e richiedono una conoscenza approfondita del linguaggio. Per aggiungere questo livello di consapevolezza linguistica, Ricerca cognitiva di Azure supporta un lungo elenco di analizzatori del linguaggio di Lucene e Microsoft.

Nota

I requisiti per l'analisi possono variare da minimi a elaborati a seconda dello scenario. È possibile controllare la complessità dell'analisi lessicale quando si seleziona uno degli analizzatori predefiniti o creando il proprio analizzatore personalizzato. Gli analizzatori sono limitati a campi ricercabili e vengono specificati come parte di una definizione di campo. Ciò consente di variare l'analisi lessicale in base al campo. Se non è specificato, viene usato l'analizzatore Lucene standard.

In questo esempio, prima dell'analisi, l'albero di query iniziale ha il termine "Spazioso", con una "S" maiuscola e una virgola che il parser di query interpreta come parte del termine di query (una virgola non è considerata un operatore del linguaggio di query).

Quando l'analizzatore predefinito elabora il termine, renderà con lettere minuscole "vista sull'oceano" e "spazioso" e rimuoverà il carattere di virgola. L'albero della query modificato avrà l'aspetto seguente:

Diagramma concettuale di una query booleana con termini analizzati.

Comportamenti dell'analizzatore del test

Il comportamento di un analizzatore può essere testato usando l'API Analyze. Fornire il testo da analizzare per vedere quali termini specificati saranno generati dall'analizzatore. Ad esempio, per visualizzare il modo in cui l'analizzatore standard elabora il testo "aria condizionata", è possibile eseguire la richiesta seguente:

{
    "text": "air-condition",
    "analyzer": "standard"
}

L'analizzatore standard suddivide il testo di input nei due token seguenti, annotandoli con attributi quali offset iniziale e finale (usati per l'evidenziazione dei risultati), nonché con la loro posizione (usata per la corrispondenza di una frase):

{
  "tokens": [
    {
      "token": "air",
      "startOffset": 0,
      "endOffset": 3,
      "position": 0
    },
    {
      "token": "condition",
      "startOffset": 4,
      "endOffset": 13,
      "position": 1
    }
  ]
}

Eccezioni all'analisi lessicale

L'analisi lessicale si applica solo ai tipi di query che richiedono termini completi, una query di termine o una query di frase. Non si applica ai tipi di query con termini incompleti: query di prefisso, query con caratteri jolly, query regex o query fuzzy. Questi tipi di query, tra cui la query di prefisso con il termine air-condition* nel nostro esempio, vengono aggiunti direttamente all'albero della query, ignorando la fase di analisi. L'unica trasformazione eseguita per i termini della query di queste tipologie è la conversione in lettere minuscole.

Fase 3: Recupero dei documenti

Il recupero dei documenti fa riferimento alla ricerca di documenti con termini corrispondenti all'indice. Questa fase viene spiegata meglio attraverso un esempio. Iniziamo con un indice degli hotel che presenta il seguente schema semplice:

{
    "name": "hotels",
    "fields": [
        { "name": "id", "type": "Edm.String", "key": true, "searchable": false },
        { "name": "title", "type": "Edm.String", "searchable": true },
        { "name": "description", "type": "Edm.String", "searchable": true }
    ] 
} 

Si supponga anche che l'indice contenga i seguenti quattro documenti:

{
    "value": [
        {
            "id": "1",
            "title": "Hotel Atman",
            "description": "Spacious rooms, ocean view, walking distance to the beach."
        },
        {
            "id": "2",
            "title": "Beach Resort",
            "description": "Located on the north shore of the island of Kauaʻi. Ocean view."
        },
        {
            "id": "3",
            "title": "Playa Hotel",
            "description": "Comfortable, air-conditioned rooms with ocean view."
        },
        {
            "id": "4",
            "title": "Ocean Retreat",
            "description": "Quiet and secluded"
        }
    ]
}

Come vengono indicizzati termini

Comprendere il recupero è utile per conoscere alcune nozioni di base sull'indicizzazione. L'unità di archiviazione è un indice invertito, uno per ogni campo ricercabile. In un indice invertito è presente un elenco ordinato di tutti i termini derivanti da tutti i documenti. Ogni termine esegue il mapping nell'elenco di termini in cui si verifica, come si evince nell'esempio seguente.

Per produrre i termini in un indice inverso, il motore di ricerca esegue l'analisi lessicale del contenuto dei documenti, analogamente a quanto accade durante l'elaborazione delle query:

  1. Gli input di testo vengono passati a un analizzatore, in lettere minuscole, prive di punteggiatura e così via, a seconda della configurazione dell'analizzatore.
  2. I token sono l'output dell'analisi lessicale.
  3. I termini vengono aggiunti all'indice.

È comune, ma non obbligatorio, usare gli stessi analizzatori per la ricerca e le operazioni di indicizzazione in modo che i termini di query assomiglino di più ai termini contenuti nell'indice.

Nota

Ricerca cognitiva di Azure consente di specificare analizzatori diversi per l'indicizzazione e la ricerca tramite parametri aggiuntivi indexAnalyzer e searchAnalyzer di campo. Se non è specificato, l'analizzatore impostato con la proprietà analyzer viene usato per l'indicizzazione e la ricerca.

Indice invertito per i documenti di esempio

Ritornando all'esempio precedente, per il campo titolo, l'indice invertito è simile al seguente:

Termine Elenco di documenti
atman 1
spiaggia 2
hotel 1, 3
oceano 4
playa 3
resort 3
rifugio 4

Nel campo del titolo, solo hotel viene visualizzato in due documenti: 1, 3.

Per il campo descrizione, l'indice è il seguente:

Termine Elenco di documenti
air 3
e 4
spiaggia 1
condizionata 3
comodo 3
distance 1
isola 2
kauaʻi 2
posizionato 2
nord 2
oceano 1, 2, 3
di 2
in 2
tranquillo 4
camere 1, 3
appartato 4
costa 2
spazioso 1
il 1, 2
to 1
vista 1, 2, 3
passeggiata 1
con 3

Corrispondenza dei termini di query con i termini indicizzati

Dati gli indici invertiti precedenti, tornare alla query di esempio e vedere come si trovano i documenti corrispondenti per la query di esempio. Tenere presente che l'albero della query finale ha il seguente aspetto:

Diagramma concettuale di una query booleana con termini analizzati.

Durante l'esecuzione delle query, le singole query vengono eseguite sulla base dei campi ricercabili in modo indipendente.

  • Il TermQuery "spazioso" corrisponde al documento 1 (Hotel Atman).

  • Il PrefixQuery "aria-condizionata*" non corrisponde a nessun documento.

    Si tratta di un comportamento che a volte confonde gli sviluppatori. Anche se il termine aria condizionata esiste nel documento, viene suddiviso in due termini per l'analizzatore predefinito. Tenere presente che le query con prefisso, che contengono termini parziali, non vengono analizzate. Di conseguenza termini con prefisso "aria condizionata" vengono cercati nell'indice invertito e non trovati.

  • La PhraseQuery "vista sull'oceano" cerca i termini "oceano" e "vista" e controlla la prossimità dei termini nel documento originale. I documenti 1, 2 e 3 abbinano questa query nel campo della descrizione. Si noti che il documento 4 contiene il termine oceano, ma non è considerata una corrispondenza poiché stiamo cercando la frase "vista sull'oceano" e non le singole parole.

Nota

Una query di ricerca viene eseguita in modo indipendente su tutti i campi ricercabili nell'indice Ricerca cognitiva di Azure, a meno che non si limitino i campi impostati con il searchFields parametro , come illustrato nella richiesta di ricerca di esempio. Vengono restituiti i documenti corrispondenti in uno dei campi selezionati.

Nel complesso, per la query in questione, i documenti corrispondenti sono 1, 2, 3.

Fase 4: Assegnazione dei punteggi

Ad ogni documento in un set di risultati di ricerca viene assegnato un punteggio di pertinenza. La funzione del punteggio di pertinenza è di posizionare in alto in classifica quei documenti che meglio rispondono a una domanda utente espressa dalla query di ricerca. Il punteggio viene calcolato in base alle proprietà statistiche dei termini che corrispondono. Alla base della formula del punteggio è presente la ponderazione TF/IDF (frequenza del termine - frequenza inversa del documento). Nelle query che contengono termini rari e comuni, il TF/IDF promuove i risultati contenenti il termine raro. Ad esempio, in un indice ipotetico con tutti gli articoli di Wikipedia, dai documenti che corrispondono alla query il presidente, i documenti che corrispondono a presidente sono considerati più pertinenti dei documenti corrispondenti con il.

Esempio di assegnazione dei punteggi

Richiamare i tre documenti che corrispondono alla query di esempio:

search=Spacious, air-condition* +"Ocean view"  
{
  "value": [
    {
      "@search.score": 0.25610128,
      "id": "1",
      "title": "Hotel Atman",
      "description": "Spacious rooms, ocean view, walking distance to the beach."
    },
    {
      "@search.score": 0.08951007,
      "id": "3",
      "title": "Playa Hotel",
      "description": "Comfortable, air-conditioned rooms with ocean view."
    },
    {
      "@search.score": 0.05967338,
      "id": "2",
      "title": "Ocean Resort",
      "description": "Located on a cliff on the north shore of the island of Kauai. Ocean view."
    }
  ]
}

Il documento 1 corrisponde alla query migliore poiché sia il termine spazioso sia la frase richiesta vista sull'oceano si verificano nel campo descrizione. I due documenti successivi corrispondono solo per la frase vista sull'oceano. È sorprendente constatare che il punteggio di pertinenza per i documenti 2 e 3 è diverso, anche se essi corrispondono alla query nello stesso modo. Ciò avviene perché la formula di assegnazione del punteggio include più componenti rispetto a TF/IDF. In questo caso al documento 3 è stato assegnato un punteggio leggermente maggiore perché la sua descrizione è più breve. Informazioni sulla formula Lucene per il punteggio pratico per comprendere in che modo la lunghezza del campo e altri fattori possono influenzare il punteggio di pertinenza.

Alcuni tipi di query (carattere jolly, prefisso, regex) contribuiscono sempre con un punteggio costante al punteggio complessivo del documento. Questo permette alle corrispondenze trovate tramite l'espansione della query di essere incluse nei risultati, ma senza modificare la classifica.

Un esempio illustra il motivo per cui questo risulta importante. Le ricerche con caratteri jolly, tra cui le ricerche con prefisso, sono ambigue per definizione poiché l'input è una stringa parziale con potenziali corrispondenze in un numero molto elevato di termini diversi (si consideri un input di "tour*", con corrispondenze trovate in "tour", "tourettes" e "tourmaline"). Data la natura di questi risultati, non esiste un modo per dedurre ragionevolmente quali termini sono più preziosi di altri. Per questo motivo, verranno ignorate le frequenze di termini durante l'assegnazione dei punteggi dei risultati nelle query di tipo con caratteri jolly, prefisso e regex. In una richiesta di ricerca a più parti che comprende termini parziali e completi, i risultati dall'input parziale sono incorporati con un punteggio costante per evitare la distorsione verso risultati potenzialmente imprevisti.

Ottimizzazione della pertinenza

Esistono due modi per ottimizzare i punteggi di pertinenza in Ricerca cognitiva di Azure:

  1. I profili di punteggio promuovono i documenti nell'elenco di pertinenza dei risultati in base a un set di regole. Nel nostro esempio è possibile considerare i documenti che corrispondono al campo del titolo più rilevanti rispetto ai documenti corrispondenti al campo della descrizione. In aggiunta, se l'indice dispone di un campo prezzo per ogni albergo, è possibile promuovere i documenti con prezzo inferiore. Altre informazioni sull'aggiunta di profili di punteggio a un indice di ricerca.

  2. Aumento priorità dei termini (disponibile solo nella sintassi di query Lucene Full) offre un aumento della priorità dell'operatore ^ che può essere applicato a qualsiasi parte dell'albero della query. In questo esempio, invece di cercare il prefisso aria condizionata*, è possibile cercare il termine esatto aria condizionata o il prefisso, ma i documenti che corrispondono al termine esatto vengono classificati in alto applicando boost al termine query: aria condizionata^2|| aria condizionata*. Altre informazioni sull'boosting dei termini in una query.

Assegnazione dei punteggi in un indice distribuito

Tutti gli indici in Ricerca cognitiva di Azure vengono suddivisi automaticamente in più partizioni, consentendo di distribuire rapidamente l'indice tra più nodi durante l'aumento o la riduzione delle prestazioni del servizio. Quando viene eseguita una richiesta di ricerca, viene generata in ogni partizione in modo indipendente. I risultati di ogni partizione vengono uniti e ordinati in base al punteggio (se non è definito nessun altro ordine). È importante sapere che la funzione di assegnazione dei punteggi pondera la frequenza dei termini di query rispetto alla frequenza dei documenti inversa in tutti i documenti all'interno della partizione, non in tutte le partizioni.

Ciò significa che un punteggio di pertinenza potrebbe essere diverso per documenti identici se si trovano in partizioni diverse. Fortunatamente, queste differenze tendono a scomparire man mano che aumenta il numero di documenti nell'indice a causa anche di altre distribuzioni del termine. Non è possibile presumere in quale partizione verrà inserito un documento specifico. Tuttavia, dando per assunto che una chiave del documento non cambia, verrà sempre assegnata alla stessa partizione.

In generale, il punteggio del documento non è l'attributo migliore per ordinare i documenti se la stabilità dell'ordine è importante. Ad esempio, dato due documenti con un punteggio identico, non esiste alcuna garanzia che venga visualizzata prima nelle esecuzioni successive della stessa query. Il punteggio del documento deve solo dare un'idea generale della pertinenza del documento relativo ad altri documenti nel set di risultati.

Conclusione

Il successo dei motori di ricerca commerciale ha sollevato le aspettative per la ricerca full-text sui dati privati. Per quasi tutti i tipi di esperienza di ricerca, è ora previsto che il motore comprenda il nostro obiettivo, anche quando i termini sono errati o incompleti. Si possono anche prevedere delle corrispondenze basate su termini quasi equivalenti o sinonimi che non abbiamo mai specificato.

Dal punto di vista tecnico, la ricerca full-text è estremamente complessa, richiede un'analisi linguistica sofisticata e un approccio sistematico all'elaborazione in modo tale da filtrare, espandere e trasformare i termini della query per fornire un risultato pertinente. Data la complessità intrinseca, esistono molti fattori che possono influire sul risultato di una query. Per questo motivo, investire del tempo per comprendere i meccanismi della ricerca full-text offre dei vantaggi tangibili quando si prova a lavorare con risultati imprevisti.

Questo articolo ha esaminato la ricerca full-text nel contesto di Ricerca cognitiva di Azure. Ci auguriamo che questo argomento offra un background sufficiente per riconoscere possibili cause e soluzioni per affrontare i problemi comuni della query.

Passaggi successivi

Vedi anche

Search Documents REST API (API REST di Ricerca di documenti)

Sintassi di query semplice

Full Lucene query syntax (Sintassi di query completa Lucene)

Gestire i risultati della ricerca