Classer les résultats de recherche

Cette page explique comment classer les résultats de recherche pour les recherches en texte intégral dans Spanner.

Spanner est compatible avec le calcul d'un score de pertinence thématique, qui constitue un composant de base pour la création de fonctions de classement sophistiquées. Ces scores calculent la pertinence d'un résultat par rapport à une requête, en fonction de la fréquence des termes de la requête et d'autres options personnalisables.

L'exemple suivant montre comment effectuer une recherche classée à l'aide de la SCORE fonction :

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
ORDER BY SCORE(AlbumTitle_Tokens, "fifth symphony") DESC

PostgreSQL

Cet exemple utilise spanner.search avec spanner.score.

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')
ORDER BY spanner.score(albumtitle_tokens, 'fifth symphony') DESC

Attribuer un score aux termes de requête avec la fonction SCORE

La SCORE fonction calcule un score pour chaque terme de requête, puis combine les scores. Le score par terme est basé sur la fréquence des termes et la fréquence inverse des documents (TF/IDF). Le score est un composant du classement final d'un enregistrement. La requête le combine avec d'autres signaux, tels que la fraîcheur qui module le score de pertinence thématique.

Dans l'implémentation actuelle, la partie IDF de TF/IDF n'est disponible que lorsque enhance_query=>true est utilisé. Elle calcule la fréquence relative des mots en fonction du corpus Web complet utilisé par la recherche Google, plutôt que d'un index de recherche spécifique. Si l'amélioration de la requête n'est pas activée, la notation n'utilise que le composant de fréquence des termes (TF) (c'est-à-dire que le terme IDF est défini sur 1).

La fonction SCORE renvoie des valeurs qui servent de scores de pertinence que Spanner utilise pour établir un ordre de tri. Elles n'ont pas de signification autonome. Plus le score est élevé, plus il correspond à la requête.

En règle générale, les arguments tels que query et enhance_query sont les mêmes dans les fonctions SEARCH et SCORE pour garantir la cohérence de la récupération et du classement.

La méthode recommandée consiste à utiliser ces arguments avec des paramètres de requête plutôt que des littéraux de chaîne, et à spécifier les mêmes paramètres de requête dans les fonctions SEARCH et SCORE.

Attribuer un score à plusieurs colonnes

Spanner utilise la fonction SCORE pour attribuer un score à chaque champ individuellement. La requête combine ensuite ces scores individuels. Une méthode courante consiste à additionner les scores individuels, puis à les augmenter en fonction des pondérations de champ fournies par l'utilisateur (qui sont fournies à l'aide de paramètres de requête SQL).

Par exemple, la requête suivante combine la sortie de deux fonctions SCORE :

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(Title_Tokens, @p1) OR SEARCH(Studio_Tokens, @p2)
ORDER BY SCORE(Title_Tokens, @p1) * @titleweight + SCORE(Studio_Tokens, @p2) * @studioweight
LIMIT 25

PostgreSQL

Cet exemple utilise les paramètres de requête $1 et $2, qui sont liés respectivement à "fifth symphony" et "blue note".

SELECT albumid
FROM albums
WHERE spanner.search(title_tokens, $1) OR spanner.search(studio_tokens, $2)
ORDER BY spanner.score(title_tokens, $1) * $titleweight
        + spanner.score(studio_tokens, $2) * $studioweight
LIMIT 25

L'exemple suivant ajoute deux paramètres d'amélioration :

  • La fraîcheur (FreshnessBoost) augmente le score avec (1 + @freshnessweight * GREATEST(0, 30 - DaysOld) / 30).
  • La popularité(PopularityBoost) augmente le score en le multipliant par le facteur (1 + IF(HasGrammy, @grammyweight, 0).

Pour plus de lisibilité, la requête utilise l'opérateur WITH.

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(Title_Tokens, @p1) OR SEARCH(Studio_Tokens, @p2)
ORDER BY WITH(
  TitleScore AS SCORE(Title_Tokens, @p1) * @titleweight,
  StudioScore AS SCORE(Studio_Tokens, @p2) * @studioweight,
  DaysOld AS (UNIX_MICROS(CURRENT_TIMESTAMP()) - ReleaseTimestamp) / 8.64e+10,
  FreshnessBoost AS (1 + @freshnessweight * GREATEST(0, 30 - DaysOld) / 30),
  PopularityBoost AS (1 + IF(HasGrammy, @grammyweight, 0)),
  (TitleScore + StudioScore) * FreshnessBoost * PopularityBoost)
LIMIT 25

PostgreSQL

Cet exemple utilise les paramètres de requête $1, $2, $3, $4, $5 et $6, qui sont liés respectivement aux valeurs spécifiées pour titlequery, studioquery, titleweight, studioweight, grammyweight et freshnessweight.

SELECT albumid
FROM
  (
    SELECT
      albumid,
      spanner.score(title_tokens, $1) * $3 AS titlescore,
      spanner.score(studio_tokens, $2) * $4 AS studioscore,
      (extract(epoch FROM current_timestamp) * 10e+6 - releasetimestamp) / 8.64e+10 AS daysold,
      (1 + CASE WHEN hasgrammy THEN $5 ELSE 0 END) AS popularityboost
    FROM albums
    WHERE spanner.search(title_tokens, $1) OR spanner.search(studio_tokens, $2)
  ) AS subquery
ORDER BY (subquery.TitleScore + subquery.studioscore)
  * (1 + $6 * greatest(0, 30 - subquery.daysold) / 30) * subquery.popularityboost
LIMIT 25

TOKENLIST_CONCAT peut également être utilisé dans la recherche et la notation pour simplifier les requêtes, le cas échéant.

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens]), @p)
ORDER BY SCORE(TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens]), @p)
LIMIT 25

PostgreSQL

Cet exemple utilise spanner.tokenlist_concat. Le paramètre de requête $1 est lié à "blue note".

SELECT albumid
FROM albums
WHERE spanner.search(spanner.tokenlist_concat(ARRAY[title_tokens, studio_tokens]), $1)
ORDER BY spanner.score(spanner.tokenlist_concat(ARRAY[title_tokens, studio_tokens]), $1)
LIMIT 25

Améliorer les correspondances d'ordre de requête

Spanner applique une amélioration multiplicative à la sortie de la fonction SCORE pour les valeurs qui contiennent les termes de requête dans le même ordre que celui dans lequel ils apparaissent dans la requête. Il existe deux versions de cette amélioration : la correspondance partielle et la correspondance exacte. Une amélioration de la correspondance partielle est appliquée lorsque :

  1. La TOKENLIST contient tous les termes d'origine de la requête.
  2. Les jetons sont adjacents les uns aux autres et dans le même ordre que celui dans lequel ils apparaissent dans la requête.

Il existe certaines règles spéciales pour les conjonctions, les négations et les expressions :

  • Une requête avec une négation ne peut pas bénéficier d'une amélioration de la correspondance partielle.
  • Une requête avec une conjonction reçoit une amélioration si une partie de la conjonction apparaît aux emplacements appropriés.
  • Une requête avec une expression reçoit une amélioration si l'expression apparaît dans la TOKENLIST, et que le terme à gauche de l'expression dans la requête apparaît à gauche de l'expression dans la TOKENLIST, et qu'il en va de même pour le terme à droite de l'expression.

Spanner applique une amélioration de la correspondance exacte lorsque toutes les règles précédentes sont vraies, et que les premier et dernier jetons de la requête sont les premier et dernier jetons du document.

Exemple de document : Bridge Over Troubled Water

Requête Amélioration appliquée
Bridge Troubled aucune amélioration
Bridge Over - other water aucune amélioration
Bridge (Over OR Troubled) Water aucune amélioration
Bridge Over amélioration partielle
Bridge Over (Troubled OR Water) amélioration partielle
Bridge Over Troubled Water amélioration exacte
Bridge "Over Troubled" Water amélioration exacte
Bridge ("Over Troubled" OR missingterm) Water amélioration exacte

Versions de marqueur

L'algorithme de marqueur est mis à jour régulièrement. Chaque version regroupe un ensemble d'améliorations de l'algorithme de notation. Pour obtenir une liste détaillée des différences entre les versions, consultez Versions de marqueur.

Vous pouvez définir la version de marqueur par défaut pour votre base de données ou spécifier une version pour une requête spécifique.

Définir la version de marqueur par défaut de la base de données

Vous pouvez définir la version par défaut de l'algorithme de marqueur pour votre base de données. Cette option détermine la version de l'algorithme de notation que Spanner utilise lorsque la fonction SCORE est appelée sans option de score version explicite.

GoogleSQL

Définissez l'option de base de données score_version :

ALTER DATABASE database_name SET OPTIONS (score_version = 2)

PostgreSQL

Définissez l'option de base de données spanner.score_version :

ALTER DATABASE database_name SET "spanner.score_version" = 2

Les valeurs valides pour l'option de base de données sont 1 ou 2. Si le paramètre version est présent dans une requête, il remplace la version par défaut de la base de données.

Remplacer la version de marqueur par requête

Vous pouvez remplacer la version de marqueur par défaut pour une requête spécifique à l'aide du paramètre version dans l'argument options de la fonction SCORE. Si une requête définit une version de marqueur, elle remplace la version par défaut de la base de données.

L'exemple suivant remplace la version de marqueur par défaut en spécifiant version dans le paramètre options :

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, @query)
ORDER BY SCORE(
  AlbumTitle_Tokens,
  @query,
  options=>JSON '{"version": 2}'
) DESC

PostgreSQL

Cet exemple utilise le paramètre de requête $1, qui est lié à la chaîne de requête.

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, $1)
ORDER BY spanner.score(
  albumtitle_tokens,
  $1,
  options => '{"version": 2}'::jsonb
) DESC

Limiter la profondeur de récupération

Les index de recherche contiennent souvent des millions de documents. Pour les requêtes dont les prédicats ont une faible sélectivité, il est impossible de classer tous les résultats. Les requêtes de notation ont généralement deux limites :

  1. Limite de profondeur de récupération : nombre maximal de lignes à noter.
  2. Limite de taille de l'ensemble de résultats : nombre maximal de lignes que la requête doit renvoyer (généralement la taille de la page).

Les requêtes peuvent limiter la profondeur de récupération avec des sous-requêtes SQL :

GoogleSQL

SELECT AlbumId
FROM
  (
    SELECT AlbumId, SCORE(Title_Tokens, @p1) AS score
    FROM Albums
    WHERE SEARCH(Title_Tokens, @p1)
    ORDER BY ReleaseTimestamp DESC
    LIMIT @retrieval_limit
  )
ORDER BY score DESC
LIMIT @page_size

PostgreSQL

Cet exemple utilise les paramètres de requête $1, $2 et $3, qui sont liés respectivement aux valeurs spécifiées pour title_query, retrieval_limit et page_size.

SELECT albumid
FROM
  (
    SELECT albumid, spanner.score(title_tokens, $1) AS score
    FROM albums
    WHERE spanner.search(title_tokens, $1)
    ORDER BY releasetimestamp DESC
    LIMIT $2
  ) AS subquery
ORDER BY score DESC
LIMIT $3

Cela fonctionne particulièrement bien si Spanner utilise le signal de classement le plus important pour trier l'index.

Étape suivante