Combiner des TOKENLIST

Cette page explique comment concaténer des TOKENLIST dans un index de recherche lorsque vous configurez votre schéma ou dans une requête de recherche lorsque vous effectuez une recherche en texte intégral dans Spanner.

Combiner des TOKENLIST dans un index de recherche

Parfois, vous devez que votre application recherche dans des champs individuels. À d'autres moments, l'application doit effectuer une recherche dans tous les champs. Par exemple, dans un tableau comportant deux colonnes de chaîne, vous pouvez souhaiter que votre application recherche dans les deux colonnes sans différencier la colonne d'où proviennent les correspondances.

Dans Spanner, vous pouvez procéder de deux façons:

  1. Tokenisez les mots séparément et concatenatez les TOKENLIST générés (recommandé).
  2. Concaténer des chaînes et tokenizer le résultat

La deuxième approche présente deux problèmes:

  1. Si vous souhaitez indexer Title ou Studio individuellement, en plus de les indexer dans un TOKENLIST combiné, le même texte est tokenisé deux fois. Les transactions utilisent ainsi plus de ressources.
  2. Une recherche par expression couvre les deux champs. Par exemple, si @p est défini sur "Blue Note", il correspond à une ligne contenant à la fois Title="Big Blue Note" et Studio="Blue Note Studios".

La première approche résout ces problèmes, car une expression ne correspond qu'à un seul champ et chaque champ de chaîne n'est tokenisé qu'une seule fois si les TOKENLIST individuels et combinés sont indexés. Même si chaque champ de chaîne n'est tokenisé qu'une seule fois, les TOKENLIST générés sont stockés séparément dans l'index.

Tokeniser les mots séparément et concaténer des TOKENLIST

L'exemple suivant tokenize chaque mot et utilise TOKENLIST_CONCAT pour concaténer les TOKENLIST:

GoogleSQL

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  Title STRING(MAX),
  Studio STRING(MAX),
  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
  Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN,
  Combined_Tokens TOKENLIST AS (TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens])) HIDDEN,
) PRIMARY KEY(AlbumId);

CREATE SEARCH INDEX AlbumsIndex ON Albums(Combined_Tokens);

SELECT AlbumId FROM Albums WHERE SEARCH(Combined_Tokens, @p);

PostgreSQL

PostgreSQL utilise spanner.tokenlist_concat pour la concaténation. Le paramètre de requête $1 est lié à "Hatel Kaliphorn".

CREATE TABLE albums (
  albumid character varying NOT NULL,
  title character varying,
  studio character varying,
  title_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(title)) VIRTUAL HIDDEN,
  studio_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(studio)) VIRTUAL HIDDEN,
  combined_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenlist_concat(ARRAY[spanner.tokenize_fulltext(title), spanner.tokenize_fulltext(studio)])) VIRTUAL HIDDEN,
PRIMARY KEY(albumid));

CREATE SEARCH INDEX albumsindex ON albums(combined_tokens);

SELECT albumid FROM albums WHERE spanner.search(combined_tokens, $1);

Notez que tokenlist_concat n'appelle pas title_tokens ou studio_tokens, mais spanner.tokenize_fulltext(title) et spanner.tokenize_fulltext(studio). En effet, PostgreSQL n'est pas compatible avec le référencement de colonnes générées qui se trouvent dans d'autres colonnes générées. spanner.tokenlist_concat doit appeler des fonctions de tokenisation et non pas référencer directement les colonnes de la liste de jetons.

La concaténation TOKENLIST peut également être entièrement implémentée côté requête. Pour en savoir plus, consultez la section Concatenation TOKENLIST côté requête.

TOKENLIST_CONCAT est compatible avec les recherches en texte intégral et en sous-chaîne. Spanner ne vous permet pas de mélanger des types de tokenisation, tels que TOKENIZE_FULLTEXT et TOKENIZE_SUBSTRING dans le même appel TOKENLIST_CONCAT.

Dans GoogleSQL, la définition des colonnes de texte TOKENLIST peut être modifiée dans les colonnes non stockées pour ajouter des colonnes supplémentaires. Cette fonctionnalité est utile lorsque vous souhaitez ajouter une colonne supplémentaire à TOKENLIST_CONCAT. La modification de l'expression de la colonne générée ne remplit pas les lignes existantes de l'index.

Concaténer des chaînes et tokenizer le résultat

L'exemple suivant concatène des chaînes et tokenize le résultat:

GoogleSQL

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  Title STRING(MAX),
  Studio STRING(MAX),
  Combined_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title || " " || Studio)) HIDDEN,
) PRIMARY KEY(AlbumId);

CREATE SEARCH INDEX AlbumsIndex ON Albums(Combined_Tokens);

SELECT AlbumId FROM Albums WHERE SEARCH(Combined_Tokens, @p);

PostgreSQL

CREATE TABLE albums (
  albumid character varying NOT NULL,
  title character varying,
  studio character varying,
  combined_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(title || ' ' || studio)) VIRTUAL HIDDEN,
PRIMARY KEY(albumid));

CREATE SEARCH INDEX albumsindex ON albums(combined_tokens);

SELECT albumid FROM albums WHERE spanner.search(combined_tokens, $1);

Chaîne de TOKENLIST côté requête

L'inconvénient de l'indexation de l'TOKENLIST concaténé est qu'elle augmente les coûts de stockage et d'écriture. Chaque jeton est désormais stocké sur le disque deux fois : une fois dans une liste de publication de son TOKENLIST d'origine et une fois dans une liste de publication de l'TOKENLIST combinée. La concaténation côté requête des colonnes TOKENLIST évite ce coût, mais la requête utilise plus de ressources de calcul.

Pour concaténer plusieurs TOKENLIST, utilisez la fonction TOKENLIST_CONCAT dans la requête SEARCH. Pour cette section, nous utilisons l'exemple de schéma suivant:

GoogleSQL

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  Title STRING(MAX),
  Studio STRING(MAX),
  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
  Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN,
) PRIMARY KEY(AlbumId);

CREATE SEARCH INDEX AlbumsIndex ON Albums(Title_Tokens, Studio_Tokens);

PostgreSQL

CREATE TABLE albums (
  albumid character varying NOT NULL,
  title character varying,
  studio character varying,
  title_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(title)) VIRTUAL HIDDEN,
  studio_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(studio)) VIRTUAL HIDDEN,
 PRIMARY KEY(albumid));

CREATE SEARCH INDEX albumsindex ON albums(title_tokens, studio_tokens);

La requête suivante recherche les lignes contenant les jetons "blue" et "note" dans les colonnes Title et Studio. Cela inclut les lignes contenant à la fois "blue" et "note" dans la colonne Title, "blue" et "note" dans la colonne Studio, et "blue" dans la colonne Title et "note" dans la colonne Studio, ou l'inverse.

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(TOKENLIST_CONCAT([AlbumTitle_Tokens, Studio_Tokens]), 'blue note')

PostgreSQL

Cet exemple utilise spanner.search avec spanner.tokenlist_concat.

SELECT albumid
FROM albums
WHERE spanner.search(spanner.tokenlist_concat(ARRAY[albumtitle_tokens, studio_tokens]), 'blue note')

La concatenaison TOKENLIST côté écriture et côté requête produit des résultats identiques. Le choix entre les deux est un compromis entre le coût du disque et le coût des requêtes.

Une application peut également rechercher plusieurs colonnes TOKENLIST et utiliser OR avec la fonction SEARCH:

GoogleSQL

SEARCH(AlbumTitle_Tokens, 'Blue Note') OR SEARCH(Studio_Tokens, 'Blue Note')

PostgreSQL

spanner.search(albumtitle_tokens, 'Blue Note') OR spanner.search(studio_tokens, 'Blue Note')

Cependant, la sémantique est différente. Il ne correspond pas aux albums où AlbumTitle_Tokens contient "bleu", mais pas "note", et Studio_Tokens contient "note", mais pas "bleu".

Étape suivante