Trouver des correspondances proches avec la recherche proche

Cette page explique comment utiliser une recherche proche dans le cadre d'une recherche dans le texte complet.

En plus d'effectuer des recherches de jetons exactes à l'aide des fonctions SEARCH et SEARCH_SUBSTRING, Spanner prend également en charge les recherches approximatives (ou floues). Les recherches approximatives trouvent des documents correspondants malgré de légères différences entre la requête et le document.

Spanner est compatible avec les types de recherche floue suivants:

  • Recherche approximative basée sur les n-grammes
  • Recherche phonétique à l'aide de Soundex

La recherche floue basée sur les n-grammes repose sur la même tokenisation de sous-chaîne qu'une recherche de sous-chaîne. La configuration du tokenizer est importante, car elle affecte la qualité et les performances de recherche. L'exemple suivant montre comment créer une requête avec des mots mal orthographiés ou orthographiés différemment pour trouver des correspondances approximatives dans l'index de recherche.

Schéma

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);

Requête

La requête suivante recherche les albums dont les titres sont les plus proches de "Hatel Kaliphorn", comme "Hotel California".

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

Optimiser les performances et la capacité de rappel pour une recherche approximative basée sur des n-grammes

L'exemple de requête de la section précédente effectue une recherche en deux phases, à l'aide de deux fonctions différentes:

  1. SEARCH_NGRAMS recherche tous les albums candidats qui ont partagé des n-grammes avec la requête de recherche. Par exemple, les n-grammes de trois caractères pour "Californie" incluent [cal, ali, lif, ifo, for, orn, rni, nia] et pour "Kaliphorn" incluent [kal, ali, lip, iph, pho, hor, orn]. Les n-grammes partagés dans ces ensembles de données sont [ali, orn]. Par défaut, SEARCH_NGRAMS fait correspondre tous les documents avec au moins deux n-grammes partagés. Par conséquent, "Kaliphorn" correspond à "Californie".
  2. SCORE_NGRAMS classe les correspondances par similarité. La similarité de deux chaînes est définie comme un ratio de n-grammes partagés distincts par rapport aux n-grammes distincts non partagés:
$$ \frac{shared\_ngrams}{total\_ngrams_{index} + total\_ngrams_{query} - shared\_ngrams} $$

En règle générale, la requête de recherche est la même pour les fonctions SEARCH_NGRAMS et SCORE_NGRAMS. La méthode recommandée consiste à utiliser l'argument avec des paramètres de requête plutôt qu'un littéral de chaîne, et à spécifier le même paramètre de requête dans les fonctions SEARCH_NGRAMS et SCORE_NGRAMS.

Spanner dispose de trois arguments de configuration pouvant être utilisés avec SEARCH_NGRAMS:

  1. Taille minimale et maximale des n-grammes spécifiées dans TOKENIZE_SUBSTRING ou TOKENIZE_NGRAMS. Nous vous déconseillons d'utiliser des n-grammes à un seul caractère, car ils correspondent à un très grand nombre de documents. En revanche, les n-grammes longs font que SEARCH_NGRAMS ne détecte pas les mots courts mal orthographiés.
  2. Nombre minimal d'n-grammes que SEARCH_NGRAMS doit faire correspondre (défini avec les arguments min_ngrams et min_ngrams_percent dans SEARCH_NGRAMS). Des valeurs plus élevées accélèrent généralement la requête, mais réduisent le rappel.

Pour obtenir un bon équilibre entre performances et rappel, ces arguments peuvent être configurés pour s'adapter à la requête et à la charge de travail spécifiques.

Nous vous recommandons également d'inclure un LIMIT interne pour éviter de créer des requêtes très coûteuses lorsqu'une combinaison de n-grammes populaires est rencontrée:

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

Recherche floue basée sur les n-grammes par rapport au mode de requête amélioré

En plus de la recherche approximative basée sur les n-grammes, le mode de requête amélioré gère également certains mots mal orthographiés. Il existe donc un chevauchement entre les deux fonctionnalités. Le tableau suivant récapitule les différences:

Recherche de correspondance partielle basée sur des n-grammes Mode de requête amélioré
Coût Nécessite une tokenisation de sous-chaîne plus coûteuse basée sur les n-grammes Nécessite une tokenisation du texte complet moins coûteuse
Types de requêtes de recherche Fonctionne bien avec les documents courts de quelques mots, comme le nom d'une personne, d'une ville ou d'un produit Fonctionne aussi bien avec des documents et des requêtes de recherche de n'importe quelle taille
Recherche de mots partiels Effectue une recherche de sous-chaîne qui permet les fautes d'orthographe Ne prend en charge que la recherche de mots entiers (SEARCH_SUBSTRING n'est pas compatible avec l'argument enhance_query)
Mots mal orthographiés Prise en charge des mots mal orthographiés dans l'index ou la requête N'accepte que les mots mal orthographiés dans la requête
Corrections Recherche les correspondances mal orthographiées, même si elles ne correspondent pas à un mot réel Corrige les fautes d'orthographe pour les mots courants et connus

Effectuer une recherche phonétique avec Soundex

Spanner fournit la fonction SOUNDEX pour trouver des mots qui s'écrivent différemment, mais qui se prononcent de la même manière. Par exemple, SOUNDEX("steven"), SOUNDEX("stephen") et SOUNDEX("stefan") sont tous "s315", tandis que SOUNDEX("stella") est "s340". SOUNDEX est sensible à la casse et ne fonctionne que pour les alphabets latins.

La recherche phonétique avec SOUNDEX peut être implémentée avec une colonne générée et un index de recherche, comme illustré dans l'exemple suivant:

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 requête suivante fait correspondre "stefan" à "Steven" sur SOUNDEX, avec AlbumTitle contenant "cat":

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

Étape suivante