Cómo encontrar coincidencias aproximadas con la búsqueda aproximada

En esta página, se describe cómo usar una búsqueda aproximada como parte de una búsqueda de texto completo.

Además de realizar búsquedas de tokens exactas con las funciones SEARCH y SEARCH_SUBSTRING, Spanner también admite búsquedas aproximadas (o difusas). Las búsquedas aproximadas encuentran documentos coincidentes a pesar de las pequeñas diferencias entre la consulta y el documento.

Spanner admite los siguientes tipos de búsqueda aproximada:

  • Búsqueda aproximada basada en n-gramas
  • Búsqueda fonética con Soundex

La búsqueda difusa basada en n-gramas se basa en la misma segmentación de subcadena que requiere una búsqueda de subcadena. La configuración del analizador es importante, ya que afecta la calidad y el rendimiento de la búsqueda. En el siguiente ejemplo, se muestra cómo crear una consulta con palabras con errores ortográficos o escritas de forma diferente para encontrar coincidencias aproximadas en el índice de búsqueda.

Esquema

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (
    TOKENIZE_SUBSTRING(AlbumTitle, ngram_size_min=>2, ngram_size_max=>3,
                       relative_search_types=>["word_prefix", "word_suffix"])) HIDDEN
) PRIMARY KEY(AlbumId);

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING (AlbumTitle);

Consulta

La siguiente consulta encuentra los álbumes con títulos más cercanos a "Hatel Kaliphorn", como "Hotel California".

SELECT AlbumId
FROM Albums
WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn")
ORDER BY SCORE_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn") DESC
LIMIT 10

Optimiza el rendimiento y la recuperación para una búsqueda aproximada basada en n-gramas

La consulta de ejemplo de la sección anterior realiza búsquedas en dos fases, con dos funciones diferentes:

  1. SEARCH_NGRAMS encuentra todos los álbumes candidatos que compartieron n-gramas con la búsqueda. Por ejemplo, los n-gramas de tres caracteres para “California” incluyen [cal, ali, lif, ifo, for, orn, rni, nia] y para “Kaliphorn” incluyen [kal, ali, lip, iph, pho, hor, orn]. Los n-gramas compartidos en estos conjuntos de datos son [ali, orn]. De forma predeterminada, SEARCH_NGRAMS coincide con todos los documentos que tienen al menos dos n-gramas compartidos, por lo que "Kaliphorn" coincide con "California".
  2. SCORE_NGRAMS clasifica las coincidencias por similitud. La similitud de dos cadenas se define como una proporción de n-gramas compartidos distintos a n-gramas no compartidos distintos:
$$ \frac{shared\_ngrams}{total\_ngrams_{index} + total\_ngrams_{query} - shared\_ngrams} $$

Por lo general, search_query es el mismo en las funciones SEARCH_NGRAMS y SCORE_NGRAMS. La forma recomendada de hacerlo es usar el argumento con parámetros de consulta en lugar de literales de cadena y especificar el mismo parámetro de consulta en las funciones SEARCH_NGRAMS y SCORE_NGRAMS.

Spanner tiene tres argumentos de configuración que se pueden usar con SEARCH_NGRAMS:

  1. El tamaño mínimo y máximo de los n-gramas especificados en TOKENIZE_SUBSTRING o TOKENIZE_NGRAMS. No recomendamos los n-gramas de un carácter porque coinciden con una cantidad muy grande de documentos. Por otro lado, los n-gramas largos hacen que SEARCH_NGRAMS omita las palabras cortas con errores ortográficos.
  2. Es la cantidad mínima de n-gramas que SEARCH_NGRAMS debe coincidir (establecida con los argumentos min_ngrams y min_ngrams_percent en SEARCH_NGRAMS). Por lo general, los números más altos hacen que la consulta sea más rápida, pero reducen la recuperación.

Para lograr un buen equilibrio entre el rendimiento y la recuperación, estos argumentos se pueden configurar para adaptarse a la consulta y la carga de trabajo específicas.

También te recomendamos que incluyas un LIMIT interno para evitar crear consultas muy costosas cuando se encuentra una combinación de n-gramas populares:

SELECT AlbumId
FROM (
  SELECT AlbumId,
         SCORE_NGRAMS(AlbumTitle_Tokens, @p) AS score
  FROM Albums
  WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, @p)
  LIMIT 10000  # inner limit
)
ORDER BY score DESC
LIMIT 10  # outer limit

Búsqueda parcial basada en n-gramas en comparación con el modo de búsqueda mejorado

Además de la búsqueda parcial basada en n-gramas, el modo de búsqueda mejorado también maneja algunas palabras con errores ortográficos. Por lo tanto, hay cierta superposición entre las dos funciones. En la siguiente tabla, se resumen las diferencias:

Búsqueda parcial basada en n-gramas Modo de consulta mejorado
Costo Requiere una tokenización de subcadena más costosa basada en n-gramas. Requiere una tokenización de texto completo menos costosa.
Tipos de búsquedas Funciona bien con documentos cortos de pocas palabras, como el nombre de una persona, una ciudad o un producto. Funciona igual de bien con documentos y consultas de búsqueda de cualquier tamaño.
Búsqueda de palabras parciales Realiza una búsqueda de substring que permite errores ortográficos. Solo admite la búsqueda de palabras completas (SEARCH_SUBSTRING no admite el argumento enhance_query).
Palabras con errores ortográficos Admite palabras mal escritas en el índice o la consulta. Solo admite palabras con errores ortográficos en la consulta.
Correcciones Encuentra coincidencias con errores ortográficos, incluso si la coincidencia no es una palabra real. Corrige los errores ortográficos de palabras comunes y conocidas

Realiza una búsqueda fonética con Soundex

Spanner proporciona la función SOUNDEX para encontrar palabras que se escriben de forma diferente, pero que suenan igual. Por ejemplo, SOUNDEX("steven"), SOUNDEX("stephen") y SOUNDEX("stefan") son todos "s315", mientras que SOUNDEX("stella") es "s340". SOUNDEX distingue mayúsculas de minúsculas y solo funciona para alfabetos basados en el latín.

La búsqueda fonética con SOUNDEX se puede implementar con una columna generada y un índice de búsqueda, como se muestra en el siguiente ejemplo:

CREATE TABLE Singers (
  SingerId INT64,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
  Name STRING(MAX),
  NameSoundex STRING(MAX) AS (LOWER(SOUNDEX(Name))),
  NameSoundex_Tokens TOKENLIST AS (TOKEN(NameSoundex)) HIDDEN
) PRIMARY KEY(SingerId);

CREATE SEARCH INDEX SingersPhoneticIndex ON Singers(AlbumTitle_Tokens, NameSoundex_Tokens);

La siguiente consulta hace coincidir "stefan" con "Steven" en SOUNDEX, junto con AlbumTitle que contiene "cat":

SELECT SingerId
FROM Singers
WHERE NameSoundex = LOWER(SOUNDEX("stefan")) AND SEARCH(AlbumTitle_Tokens, "cat")

¿Qué sigue?