Analizador Fonético con Azure Search

Azure Search permite definir analizadores (análisis léxico sobre los términos de consulta) sobre las propiedades que vamos a indexar, existen muchas variedades (algoritmos) de analizadores, existe un analizador fonético que como su nombre lo dice busca coincidencias basadas en fonemas. Ejemplo: zapato y “sapato” coinciden fonéticamente.

Vamos a crear un indice basado en este tipo de analizadores con el SDK de Azure Search (.NET Core C#).

Azure Search

Este servicio de búsqueda que se ofrece dentro de Azure PaaS permite realizar indices (colecciones) de documentos (entidades/ objetos) para realizar búsquedas aplicando full-text search sobre las propiedades de nuestros documentos. La forma de tokenizar el contenido de las propiedades varia según el analizador. Es una buena alternativa para realizar búsquedas cuando se ocupa SQL Azure o algún otro motor de persistencia que no tiene búsquedas basadas en full-text search.

Internamente se “asume” que ocupa ElasticSearch y Lucene.

Los pasos para crear el servicio desde el portal de Azure aquí.

Índice

La definición de nuestro indice se basa en la definición de nuestra clase que vamos a indexar (o agregar a la colección) por medio de atributos indicamos con que criterios se debe generar el índice.

Nuestro índice se refiere a un catalogo de productos con 4 propiedades: id, name, description y country. La clase que lo define se ve así:

Los atributos que vemos sobre las propiedades establecen la definición del índice, que en principio debe coincidir con las entidades que vamos a indexar. Veamos a que se refiere cada uno de los atributos.

IsSearchable: Este atributo indica que la propiedad debe ser tratada para realizar búsquedas sobre full-texte search. Esto implica que el valor que contenga la propiedad se va a tokenizar aplicando un analizador. Entonces si la propiedad tuviera el valor “Blog de Azure” internamente lo tokeniza (separa) formando “Blog” y “Azure”. Esto permite aplicar mejores condiciones de búsqueda.

IsFilterable: Este atributo establece que la propiedad que lo implementa no se realizaran búsquedas por medio de full-text search. Es decir, sobre el ejemplo anterior, si yo busco con la condición “Blog” == “Blog de Azure” no va haber coincidencia, las búsquedas deben ser por frases completas/exactas.

IsSortable: El resultado de cada búsqueda queda ordenado por un valor que va de mayor a menor coincidencia (se genera por medio de TF/IDF), llamado score. Este atributo indica si la propiedad debe afectar el score de los resultados.

Analyzer: Este atributo nos permite indicar que analizador va aplicar a cada propiedad. Como podemos ver estamos aplicando 2 tipos de analizadores. AnalyzerName.AsString.EnLucene este analizador ya esta predefinido y se refiere a que va aplicar un analizador léxico de palabras en ingles de Lucene“PhoneticCustomnAnalyzer” se refiere al analizador fonético que vamos a aplicar, este no es un analizador ya predefinido, este es un analizador customizado que se declara en otro momento.

Key: Solo para definir cual es la llave única para nuestro indice (tiene que ser string), esto es importante pues se ocupa para hacer actualizaciones o eliminar un documento en la colección.

Analizador Fonético

Ahora veamos como crear nuestro analizador personalizado. Como ya habíamos comentado antes, un analizador internamente aplica distintos algoritmos al momento de tokenizar el texto, lo que debemos hacer en nuestro propio analizador es agregar un tipo de tokenizador (tokenizer) y filtros. Aquí la lista.

Un analizador léxico esta definido por:

A tokenizer: divide el texto de entrada en tokens, esto es separar todas la palabras de una frase y remueve palabras que no afectan las búsquedas (stopwords).

A token filters: al momento de generar los tokens se aplican filtros por ejemplo convertir todo los caracteres a lowercase.

Podemos definir cualquiera que se nos ocurra de acuerdo a los resultados que queremos lograr. Definimos un nombre en la propiedad Name y es el que podemos utilizar sobre el atributo Analyzer en la definición de nuestro indice como ya lo vimos.

Creando el Índice

Ocupando los objetos que expone la librería de Azure Search (nuget: Microsoft.Azure.Search) creamos el indice con todo lo que hicimos previamente.

Primero con el método CreateIndexDefinition creamos la definición de nuestro índice, esto incluye obtener la definición de nuestro analizador fonético. Debemos indicar el nombre del índice en la propiedad Name, que lo obtenemos desde la configuración del proyecto, debemos indicar las propiedades del índice con sus atributos en Fields y finalmente indicamos que debe crear un analizador en la propiedad Analyzers.

El método CreateIndexAndGetClient invoca los métodos necesarios para crear el índice. Requerimos primero el nombre del servicio y una key que obtenemos desde el portal de Azure. Validamos si existe previamente el índice, si no, lo creamos.

Una vez que se crea el índice ya no es posible modificar su definición. Para hacer eso hay que crear uno nuevo (o eliminar) y volver a cargar los datos.

Este método también genera el objeto cliente con el que realizaremos las operaciones de búsqueda.

Datos

Necesitamos datos para poder probar nuestro índice, tenemos un archivo json donde tenemos 1,000 objetos que corresponden a la misma estructura del objeto que define nuestro índice.

Estos datos son los que vamos a insertar de forma masiva o en batch. Esto lo permite también el API de Azure Search.

Lo que hacemos con el método CreateBatchData es crear grupos de documentos/entidades para insertar, esto es porque el tamaño máximo de documentos por batch es de 1,000. Aquí el detalle.

Después en el método UploadData creamos la carga de estos grupos en paralelo.

Aquí esta el repositorio del código para ver mas detalle.

Realizando Búsquedas

Aplicar un analizador fonético para nuestras búsquedas hace sentido cuando queremos encontrar resultados aun con faltas de ortografía o probablemente con omisión de letras. También existen mas alternativas para lograr el mismo resultado como ocupar el operador Fuzzy de Lucene o establecer sinónimos.

Dentro de los datos que cargamos tenemos dos propiedades que contienen el mismo valor Name y NamePhonetic, a la primera establecimos un simple analizador léxico natural del lenguaje y en la otra un analizador fonético. El valor que estamos agregando a estas propiedades son marcas de automóviles (Kia, Honda, Mitsubishi, Dodge, Mercedes-Benz, etc).

Vamos a realizar búsquedas y veremos como hace diferencia los analizadores que colocamos.

El método con el que realizamos las búsquedas es SearchPhrase, internamente tiene algunas condiciones para poder ver las diferencias de resultados. PrintResult solo imprime el resultado.

Hagamos el primer ejercicio con la búsqueda de “kia” y “qia” sobre la columna Name.

En el código de arriba invocamos el método SearchPhrase una vez con la frase “kia” y después con la frase “qia” para comparar los resultados. En los datos no existe ningún registro con el valor “qia”. Estos son los resultados:

Sin analizador fonético

Podemos ver que para la frase “qia” no encontró ningún resultado. Ahora hagamos la prueba apuntando hacia la propiedad que tiene el analizador fonético NamePhonetic.

Con analizador fonético

Podemos ver que ahora la frase “qia” si obtuvo el resultados. Esto es porque las frases “kia” y “qia” son iguales fonéticamente.

Los analizadores léxicos de lenguaje si dependen del idioma, es decir cambia entre ingles y español. Para el analizador fonético no en necesario indicar el idioma en el que esta el texto pues Azure Search utiliza un analizador “genérico”.

Como ya lo había dicho antes, esto se podría resolver también con el operador Fuzzy con Lucene. Este operador aplica un algoritmo de aproximación sobre las frases (basado en Damerau-Levenshtein Distance), esto se logra agregando “~” a la frase. Aquí el ejemplo.

Con Fuzzy

Esta vez se hizo con la frase “Jeep” y “Geep” sobre el propiedad Name, y podemos ver que se encontraron los mismos resultados, sin embargo, sobre la búsqueda de “Geep~” encontró un ultimo resultado que fue “Geo” con un score menor, que hace sentido.

Podemos beneficiarnos de ambos analizadores:

 

El código completo de lo que se hace lo pueden ver aqui.

Troubleshooting

Por alguna razón en .NET Core da problemas la versión de 11.* de Newtonsoft.Json al momento de indexar documentos. Hay que agregar la versión 10.* de Newtonsoft.Json y agregar también Microsoft.Rest.ClientRuntime.Azure.

2 thoughts on “Analizador Fonético con Azure Search”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *