Combiner des TOKENLIST

Cette page explique comment concaténer des TOKENLISTs 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 permettre à votre application d'effectuer des recherches dans des champs individuels. Dans d'autres cas, l'application doit effectuer des recherches dans tous les champs. Par exemple, dans une table comportant deux colonnes de chaîne, vous pouvez souhaiter que votre application effectue des recherches dans les deux colonnes sans faire la distinction entre les colonnes d'où proviennent les correspondances.

Dans Spanner, il existe deux façons de procéder :

  1. Tokeniser les mots séparément et concaténer les TOKENLIST résultantes (recommandé).
  2. Concaténer les chaînes et tokeniser 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 une TOKENLIST combinée, le même texte est tokenisé deux fois. Cela entraîne une utilisation plus importante des ressources par les transactions.
  2. Une recherche d'expression s'étend sur 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 individuelles et combinées sont indexées. Même si chaque champ de chaîne n'est tokenisé qu'une seule fois, les TOKENLIST résultantes sont stockées séparément dans l'index.

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

L'exemple suivant tokenise chaque mot et utilise TOKENLIST_CONCAT pour concaténer les TOKENLISTs :

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 ni studio_tokens, mais appelle plutôt spanner.tokenize_fulltext(title) et spanner.tokenize_fulltext(studio). En effet, PostgreSQL ne permet pas de référencer des 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 référencer directement des colonnes de liste de jetons.

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

TOKENLIST_CONCAT est compatible avec les recherches en texte intégral et les recherches de sous-chaînes. Spanner ne vous permet pas de combiner 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 TOKENLIST de texte peut être modifiée dans les colonnes non stockées pour ajouter des colonnes supplémentaires. Cela 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 dans l'index.

Concaténer les chaînes et tokeniser le résultat

L'exemple suivant concatène des chaînes et tokenise 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);

Concaténation TOKENLIST côté requête

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

Pour concaténer plusieurs TOKENLIST, utilisez la TOKENLIST_CONCAT fonction dans la SEARCH requête. 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" n'importe où 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 concaténation 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 de la requête.

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, cela a une sémantique différente. Elle ne correspond pas aux albums où AlbumTitle_Tokens contient "blue", mais pas "note", et où Studio_Tokens contient "note", mais pas "blue".

Étape suivante