Condividi tramite


Esercitazione: Creare un analizzatore personalizzato per numeri di telefono

Nelle soluzioni di ricerca le stringhe con modelli complessi o caratteri speciali possono risultare difficili da usare perché l'analizzatore predefinito rimuove o interpreta erroneamente parti significative di un criterio. Ciò comporta una scarsa esperienza di ricerca in cui gli utenti non riescono a trovare le informazioni previste. I numeri di telefono sono un esempio classico di stringhe difficili da analizzare. Sono disponibili in vari formati e includono caratteri speciali ignorati dall'analizzatore predefinito.

Con i numeri di telefono come oggetto, questa esercitazione illustra come risolvere i problemi dei dati basati su modelli usando un analizzatore personalizzato. Questo approccio può essere usato come per i numeri di telefono o adattato per i campi con le stesse caratteristiche (con caratteri speciali), ad esempio URL, messaggi di posta elettronica, codici postali e date.

In questa esercitazione, si usano un client REST e le API REST di Azure AI Search per:

  • Informazioni sul problema
  • Sviluppare un analizzatore personalizzato iniziale per gestire numeri di telefono
  • Testare l'analizzatore personalizzato
  • Iterare sul design dell'analizzatore personalizzato per migliorare ulteriormente i risultati

Prerequisiti

Scaricare i file

Il codice sorgente per questa esercitazione si trova nel file custom-analyzer.rest nel repository GitHub Azure-Samples/azure-search-rest-samples .

Copiare una chiave di amministratore e un URL

Le chiamate REST in questa esercitazione richiedono un endpoint del servizio di ricerca e una chiave API amministratore. È possibile ottenere questi valori dal portale di Azure.

  1. Accedere al portale di Azure, passare alla pagina Panoramica e copiare l'URL. Un endpoint di esempio potrebbe essere simile a https://mydemo.search.windows.net.

  2. In Impostazioni>Chiavi, copiare una chiave amministratore. Le chiavi amministratore vengono usate per aggiungere, modificare ed eliminare oggetti. Sono disponibili due chiavi amministratore intercambiabili. Copiarne una.

    Screenshot delle chiavi URL e API nel portale di Azure.

Una chiave API valida stabilisce un trust, per ogni richiesta, tra l'applicazione che invia la richiesta e il servizio di ricerca che la gestisce.

Creare un indice iniziale

  1. Aprire un nuovo file di testo in Visual Studio Code.

  2. Impostare le variabili sull'endpoint di ricerca e sulla chiave API raccolta nella sezione precedente.

    @baseUrl = PUT-YOUR-SEARCH-SERVICE-URL-HERE
    @apiKey = PUT-YOUR-ADMIN-API-KEY-HERE
    
  3. Salvare il file con un'estensione file .rest.

  4. Incollare l'esempio seguente per creare un indice di piccole dimensioni denominato phone-numbers-index con due campi: id e phone_number. Non è ancora stato definito un analizzatore, quindi l'analizzatore standard.lucene viene usato per impostazione predefinita.

    ### Create a new index
    POST {{baseUrl}}/indexes?api-version=2024-07-01  HTTP/1.1
      Content-Type: application/json
      api-key: {{apiKey}}
    
      {
        "name": "phone-numbers-index",  
        "fields": [
          {
            "name": "id",
            "type": "Edm.String",
            "key": true,
            "searchable": true,
            "filterable": false,
            "facetable": false,
            "sortable": true
          },
          {
            "name": "phone_number",
            "type": "Edm.String",
            "sortable": false,
            "searchable": true,
            "filterable": false,
            "facetable": false
          }
        ]
      }
    
  5. Selezionare Invia richiesta. È necessario avere una HTTP/1.1 201 Created risposta e il corpo della risposta deve includere la rappresentazione JSON dello schema dell'indice.

  6. Caricare dati nell'indice usando documenti che contengono vari formati di numero di telefono. Si tratta di dati di test.

    ### Load documents
    POST {{baseUrl}}/indexes/phone-numbers-index/docs/index?api-version=2024-07-01  HTTP/1.1
      Content-Type: application/json
      api-key: {{apiKey}}
    
      {
        "value": [
          {
            "@search.action": "upload",  
            "id": "1",
            "phone_number": "425-555-0100"
          },
          {
            "@search.action": "upload",  
            "id": "2",
            "phone_number": "(321) 555-0199"
          },
          {  
            "@search.action": "upload",  
            "id": "3",
            "phone_number": "+1 425-555-0100"
          },
          {  
            "@search.action": "upload",  
            "id": "4",  
            "phone_number": "+1 (321) 555-0199"
          },
          {
            "@search.action": "upload",  
            "id": "5",
            "phone_number": "4255550100"
          },
          {
            "@search.action": "upload",  
            "id": "6",
            "phone_number": "13215550199"
          },
          {
            "@search.action": "upload",  
            "id": "7",
            "phone_number": "425 555 0100"
          },
          {
            "@search.action": "upload",  
            "id": "8",
            "phone_number": "321.555.0199"
          }
        ]  
      }
    
  7. Provare a eseguire query simili a quanto potrebbe essere digitato da un utente. Ad esempio, un utente potrebbe cercare (425) 555-0100 in un numero qualsiasi di formati e prevedere comunque la restituzione dei risultati. Iniziare eseguendo una ricerca in (425) 555-0100.

    ### Search for a phone number
    GET {{baseUrl}}/indexes/phone-numbers-index/docs/search?api-version=2024-07-01&search=(425) 555-0100  HTTP/1.1
      Content-Type: application/json
      api-key: {{apiKey}}
    

    La query restituisce tre dei quattro risultati previsti, ma restituisce anche due risultati imprevisti.

    {
        "value": [
            {
                "@search.score": 0.05634898,
                "phone_number": "+1 425-555-0100"
            },
            {
                "@search.score": 0.05634898,
                "phone_number": "425 555 0100"
            },
            {
                "@search.score": 0.05634898,
                "phone_number": "425-555-0100"
            },
            {
                "@search.score": 0.020766128,
                "phone_number": "(321) 555-0199"
            },
            {
                "@search.score": 0.020766128,
                "phone_number": "+1 (321) 555-0199"
            }
        ]
    }
    
  8. Riprovare senza formattazione: 4255550100.

     ### Search for a phone number
     GET {{baseUrl}}/indexes/phone-numbers-index/docs/search?api-version=2024-07-01&search=4255550100  HTTP/1.1
       Content-Type: application/json
       api-key: {{apiKey}}
    

    Le prestazioni di questa query sono ancora peggiori, dato che restituisce solo una delle quattro corrispondenze corrette.

    {
        "value": [
            {
                "@search.score": 0.6015292,
                "phone_number": "4255550100"
            }
        ]
    }
    

Se trovi questi risultati confusi, non sei il solo. La sezione successiva spiega perché si ottengono questi risultati.

Esaminare il funzionamento degli analizzatori

Per comprendere questi risultati della ricerca, è necessario comprendere le operazioni dell'analizzatore. Da qui è possibile testare l'analizzatore predefinito usando l'API Analizza, fornendo una base per la progettazione di un analizzatore che soddisfi meglio le proprie esigenze.

Un analizzatore è un componente del motore di ricerca full-text responsabile dell'elaborazione del testo nelle stringhe di query e nei documenti indicizzati. Analizzatori diversi modificano il testo in modi diversi a seconda dello scenario. Per questo scenario è necessario creare un analizzatore mirato per i numeri di telefono.

Gli analizzatori sono costituiti da tre componenti:

  • I filtri di caratteri che consentono di rimuovere o sostituire singoli caratteri dal testo di input.
  • Tokenizer che suddivide il testo di input in token, che diventano chiavi nell'indice di ricerca.
  • I filtri di token che modificano i token generati dal tokenizer.

Il diagramma seguente mostra come questi tre componenti interagiscono per tokenizzare una frase.

Diagramma del processo dell'analizzatore per suddividere in token una frase

Questi token vengono quindi archiviati in un indice invertito, che consente ricerche full-text rapide. Un indice invertito abilita la ricerca full-text eseguendo il mapping di tutti i termini univoci estratti durante l'analisi lessicale ai documenti che li contengono. È possibile visualizzare un esempio nel diagramma seguente:

Esempio di indice invertito

L'intero processo di ricerca consiste nel cercare i termini archiviati nell'indice invertito. Quando un utente esegue una query:

  1. La query viene analizzata, così come i termini della query.
  2. L'indice invertito viene analizzato alla ricerca di documenti con termini corrispondenti.
  3. L'algoritmo di assegnazione dei punteggi classifica i documenti recuperati.

Diagramma del processo dell'analizzatore per la classificazione della somiglianza

Se i termini della query non corrispondono a quelli nell'indice invertito, non vengono restituiti risultati. Per ulteriori informazioni sul funzionamento delle query, consultare la ricerca testuale completa in Azure AI Search.

Nota

Le query con termini parziali sono un'importante eccezione a questa regola. A differenza delle query di termini normali, queste query (query con prefisso, query con caratteri jolly, query espressione regolare) ignorano il processo di analisi lessicale. I termini parziali sono resi in minuscolo prima di essere confrontati con i termini nell'indice. Se un analizzatore non è configurato per supportare questi tipi di query, spesso si ricevono risultati imprevisti perché i termini corrispondenti non esistono nell'indice.

Analizzatori di test che usano l'API Analizza

Azure AI Search fornisce un'API Analizza che consente di testare gli analizzatori per comprendere il modo in cui elaborano il testo.

Chiamare l'API Analyze usando la richiesta seguente:

POST {{baseUrl}}/indexes/phone-numbers-index/analyze?api-version=2024-07-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}}

  {
    "text": "(425) 555-0100",
    "analyzer": "standard.lucene"
  }

L'API restituisce i token estratti dal testo usando l'analizzatore specificato. L'analizzatore Lucene standard suddivide il numero di telefono in tre token separati.

{
    "tokens": [
        {
            "token": "425",
            "startOffset": 1,
            "endOffset": 4,
            "position": 0
        },
        {
            "token": "555",
            "startOffset": 6,
            "endOffset": 9,
            "position": 1
        },
        {
            "token": "0100",
            "startOffset": 10,
            "endOffset": 14,
            "position": 2
        }
    ]
}

Il numero di telefono 4255550100 formattato senza punteggiatura viene invece tokenizzato in un singolo token.

{
  "text": "4255550100",
  "analyzer": "standard.lucene"
}

Risposta:

{
    "tokens": [
        {
            "token": "4255550100",
            "startOffset": 0,
            "endOffset": 10,
            "position": 0
        }
    ]
}

Tenere presente che vengono analizzati sia i termini della query che i documenti indicizzati. Pensando ai risultati della ricerca del passaggio precedente, è possibile iniziare a vedere perché tali risultati vengono restituiti.

Nella prima query vengono restituiti numeri di telefono imprevisti perché uno dei relativi token, 555, corrisponde a uno dei termini cercati. Nella seconda query viene restituito solo il numero uno perché è l'unico record con un token corrispondente a 4255550100.

Creare un analizzatore personalizzato

Dopo aver compreso i risultati visualizzati, creare un analizzatore personalizzato per migliorare la logica di tokenizzazione.

L'obiettivo è fornire una ricerca intuitiva dei numeri di telefono, indipendentemente dal formato della query o della stringa indicizzata. Per ottenere questo risultato, specificare un filtro di caratteri, un tokenizer e un filtro token.

Filtri di caratteri

I filtri di caratteri elaborano il testo prima che venga inserito nel tokenizer. Gli usi comuni dei filtri di caratteri filtrano gli elementi HTML e sostituiscono caratteri speciali.

Per i numeri di telefono, si desidera rimuovere spazi vuoti e caratteri speciali perché non tutti i formati di numeri di telefono contengono gli stessi caratteri speciali e spazi.

"charFilters": [
    {
      "@odata.type": "#Microsoft.Azure.Search.MappingCharFilter",
      "name": "phone_char_mapping",
      "mappings": [
        "-=>",
        "(=>",
        ")=>",
        "+=>",
        ".=>",
        "\\u0020=>"
      ]
    }
  ]

Il filtro rimuove -()+. e spazi dall'input.

Input Output
(321) 555-0199 3215550199
321.555.0199 3215550199

Tokenizer

I tokenizer suddividono il testo in token eliminando al contempo alcuni caratteri, ad esempio la punteggiatura. In molti casi, l'obiettivo della tokenizzazione è suddividere una frase in singole parole.

Per questo scenario, usare un tokenizer di parole chiave, keyword_v2, per acquisire il numero di telefono come singolo termine. Questo non è l'unico modo per risolvere questo problema, come illustrato nella sezione Approcci alternativi .

I tokenizzatori di parole chiave remettono sempre lo stesso testo fornito come singolo termine.

Input Risultato
The dog swims. [The dog swims.]
3215550199 [3215550199]

Filtri di token

I filtri dei token modificano o filtrano i token generati dal tokenizer. Un uso comune di un filtro token è di convertire in minuscolo tutti i caratteri utilizzando un filtro per lettere minuscole. Un altro uso comune consiste nel filtrare parole non significative, ad esempio the, and o is.

Anche se non è necessario usare uno di questi filtri per questo scenario, usare un filtro di token nGram per consentire ricerche parziali dei numeri di telefono.

"tokenFilters": [
  {
    "@odata.type": "#Microsoft.Azure.Search.NGramTokenFilterV2",
    "name": "custom_ngram_filter",
    "minGram": 3,
    "maxGram": 20
  }
]

NGramTokenFilterV2

Il filtro di token nGram_v2 suddivide i token in n-grammi di una determinata dimensione in base ai parametri minGram e maxGram.

Per l'analizzatore del telefono, minGram è impostato su 3 perché è la sottostringa più breve che gli utenti devono cercare. maxGram è impostato su 20 per garantire che tutti i numeri di telefono, anche con estensioni, si adattino a un singolo n-grammo.

Il lato negativo degli n-grammi è che vengono restituiti anche alcuni falsi positivi. È possibile risolvere questo problema in un passaggio successivo creando un analizzatore separato per le ricerche che non includono il filtro token n-gram.

Input Output
[12345] [123, 1234, 12345, 234, 2345, 345]
[3215550199] [321, 3215, 32155, 321555, 3215550, 32155501, 321555019, 3215550199, 215, 2155, 21555, 215550, ... ]

Analizzatore

Con i filtri dei caratteri, il tokenizer e i filtri token disponibili, puoi procedere a definire l'analizzatore.

"analyzers": [
  {
    "@odata.type": "#Microsoft.Azure.Search.CustomAnalyzer",
    "name": "phone_analyzer",
    "tokenizer": "keyword_v2",
    "tokenFilters": [
      "custom_ngram_filter"
    ],
    "charFilters": [
      "phone_char_mapping"
    ]
  }
]

Dall'API Analizza, in base agli input seguenti, gli output dell'analizzatore personalizzato sono i seguenti:

Input Output
12345 [123, 1234, 12345, 234, 2345, 345]
(321) 555-0199 [321, 3215, 32155, 321555, 3215550, 32155501, 321555019, 3215550199, 215, 2155, 21555, 215550, ... ]

Tutti i token nella colonna di output sono presenti nell'indice. Se la query include uno di questi termini, viene restituito il numero di telefono.

Ricompilare usando il nuovo analizzatore

  1. Eliminare l'indice corrente.

     ### Delete the index
     DELETE {{baseUrl}}/indexes/phone-numbers-index?api-version=2024-07-01 HTTP/1.1
         api-key: {{apiKey}}
    
  2. Ricreare l'indice usando il nuovo analizzatore. Questo schema di indice aggiunge una definizione dell'analizzatore personalizzata e un'assegnazione di analizzatore personalizzato nel campo numero di telefono.

    ### Create a new index
    POST {{baseUrl}}/indexes?api-version=2024-07-01  HTTP/1.1
      Content-Type: application/json
      api-key: {{apiKey}}
    
    {
        "name": "phone-numbers-index-2",  
        "fields": [
          {
              "name": "id",
              "type": "Edm.String",
              "key": true,
              "searchable": true,
              "filterable": false,
              "facetable": false,
              "sortable": true
          },
          {
              "name": "phone_number",
              "type": "Edm.String",
              "sortable": false,
              "searchable": true,
              "filterable": false,
              "facetable": false,
              "analyzer": "phone_analyzer"
          }
        ],
        "analyzers": [
            {
              "@odata.type": "#Microsoft.Azure.Search.CustomAnalyzer",
              "name": "phone_analyzer",
              "tokenizer": "keyword_v2",
              "tokenFilters": [
              "custom_ngram_filter"
            ],
            "charFilters": [
              "phone_char_mapping"
              ]
            }
          ],
          "charFilters": [
            {
              "@odata.type": "#Microsoft.Azure.Search.MappingCharFilter",
              "name": "phone_char_mapping",
              "mappings": [
                "-=>",
                "(=>",
                ")=>",
                "+=>",
                ".=>",
                "\\u0020=>"
              ]
            }
          ],
          "tokenFilters": [
            {
              "@odata.type": "#Microsoft.Azure.Search.NGramTokenFilterV2",
              "name": "custom_ngram_filter",
              "minGram": 3,
              "maxGram": 20
            }
          ]
        }
    

Testare l'analizzatore personalizzato

Dopo aver ricreato l'indice, testare l'analizzatore usando la richiesta seguente:

POST {{baseUrl}}/indexes/tutorial-first-analyzer/analyze?api-version=2024-07-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}} 

  {
    "text": "+1 (321) 555-0199",
    "analyzer": "phone_analyzer"
  }

Verrà ora visualizzata la raccolta di token risultanti dal numero di telefono.

{
    "tokens": [
        {
            "token": "132",
            "startOffset": 1,
            "endOffset": 17,
            "position": 0
        },
        {
            "token": "1321",
            "startOffset": 1,
            "endOffset": 17,
            "position": 0
        },
        {
            "token": "13215",
            "startOffset": 1,
            "endOffset": 17,
            "position": 0
        },
        ...
        ...
        ...
    ]
}

Rivedere l'analizzatore personalizzato per gestire i falsi positivi

Dopo aver usato l'analizzatore personalizzato per eseguire query di esempio sull'indice, dovresti vedere che la capacità di recupero è migliorata e che vengono restituiti tutti i numeri di telefono corrispondenti. Tuttavia, il filtro token n-gram causa anche la restituzione di alcuni falsi positivi. È un effetto collaterale comune di questo tipo di filtro.

Per evitare falsi positivi, creare un analizzatore separato per l'esecuzione di query. Questo analizzatore è identico a quello precedente, ad eccezione del fatto che omette .custom_ngram_filter

    {
      "@odata.type": "#Microsoft.Azure.Search.CustomAnalyzer",
      "name": "phone_analyzer_search",
      "tokenizer": "custom_tokenizer_phone",
      "tokenFilters": [],
      "charFilters": [
        "phone_char_mapping"
      ]
    }

Nella definizione dell'indice specificare sia un oggetto indexAnalyzer che un oggetto searchAnalyzer.

    {
      "name": "phone_number",
      "type": "Edm.String",
      "sortable": false,
      "searchable": true,
      "filterable": false,
      "facetable": false,
      "indexAnalyzer": "phone_analyzer",
      "searchAnalyzer": "phone_analyzer_search"
    }

Dopo questa modifica, è tutto pronto. Di seguito sono elencati i passaggi successivi:

  1. Eliminare l'indice.

  2. Ricreare l'indice dopo aver aggiunto il nuovo analizzatore personalizzato (phone_analyzer-search) e assegnare tale analizzatore alla phone-number proprietà del searchAnalyzer campo.

  3. Ricaricare i dati.

  4. Eseguire il nuovo test delle query per verificare che la ricerca funzioni come previsto. Se si sta usando il file campione, questo passaggio crea il terzo indice denominato phone-number-index-3.

Approcci alternativi

L'analizzatore descritto nella sezione precedente è progettato per ottimizzare la flessibilità per la ricerca. Il costo di questo risultato è però l'archiviazione di molti termini potenzialmente irrilevanti nell'indice.

L'esempio seguente mostra un analizzatore alternativo più efficiente nella tokenizzazione, ma presenta svantaggi.

Dato un input di 14255550100, l'analizzatore non può suddividere logicamente in blocchi il numero di telefono. Ad esempio, non può separare il prefisso internazionale, 1, dal prefisso della località, 425. Questa discrepanza comporta la mancata restituzione del numero di telefono se un utente non include un codice paese nella ricerca.

"analyzers": [
  {
    "@odata.type": "#Microsoft.Azure.Search.CustomAnalyzer",
    "name": "phone_analyzer_shingles",
    "tokenizer": "custom_tokenizer_phone",
    "tokenFilters": [
      "custom_shingle_filter"
    ]
  }
],
"tokenizers": [
  {
    "@odata.type": "#Microsoft.Azure.Search.StandardTokenizerV2",
    "name": "custom_tokenizer_phone",
    "maxTokenLength": 4
  }
],
"tokenFilters": [
  {
    "@odata.type": "#Microsoft.Azure.Search.ShingleTokenFilter",
    "name": "custom_shingle_filter",
    "minShingleSize": 2,
    "maxShingleSize": 6,
    "tokenSeparator": ""
  }
]

Nell'esempio seguente il numero di telefono viene suddiviso in blocchi che normalmente si prevede che un utente stia cercando.

Input Output
(321) 555-0199 [321, 555, 0199, 321555, 5550199, 3215550199]

A seconda delle esigenze, questo può essere una soluzione più efficiente al problema.

Risultati

Questa esercitazione ha illustrato il processo di compilazione e test di un analizzatore personalizzato. È stato creato un indice, sono stati indicizzati i dati e quindi è stata eseguita una query sull'indice per controllare i risultati della ricerca restituiti. Successivamente, è stata usata l'API Analizza per vedere il processo di analisi lessicale in azione.

Sebbene l'analizzatore definito in questa esercitazione offra una soluzione semplice per la ricerca di numeri di telefono, questo stesso processo può essere usato per creare un analizzatore personalizzato per qualsiasi scenario con caratteristiche simili.

Pulire le risorse

Quando si lavora nella propria sottoscrizione, una volta terminato un progetto è opportuno rimuovere le risorse che non sono più necessarie. L'esecuzione continua delle risorse può avere un costo. È possibile eliminare risorse singole oppure gruppi di risorse per eliminare l'intero set di risorse.

È possibile trovare e gestire le risorse nella portale di Azure, usando il collegamento Tutte le risorse o Gruppi di risorse nel riquadro di spostamento a sinistra.

Passaggi successivi

Ora che si è appreso come creare un analizzatore personalizzato, esaminare tutti i diversi filtri, tokenizer e analizzatori disponibili per creare un'esperienza di ricerca avanzata: