Restez organisé à l'aide des collections
Enregistrez et classez les contenus selon vos préférences.
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:
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.
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:
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:
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:
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.
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:
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".
Sauf indication contraire, le contenu de cette page est régi par une licence Creative Commons Attribution 4.0, et les échantillons de code sont régis par une licence Apache 2.0. Pour en savoir plus, consultez les Règles du site Google Developers. Java est une marque déposée d'Oracle et/ou de ses sociétés affiliées.
Dernière mise à jour le 2025/09/09 (UTC).
[[["Facile à comprendre","easyToUnderstand","thumb-up"],["J'ai pu résoudre mon problème","solvedMyProblem","thumb-up"],["Autre","otherUp","thumb-up"]],[["Difficile à comprendre","hardToUnderstand","thumb-down"],["Informations ou exemple de code incorrects","incorrectInformationOrSampleCode","thumb-down"],["Il n'y a pas l'information/les exemples dont j'ai besoin","missingTheInformationSamplesINeed","thumb-down"],["Problème de traduction","translationIssue","thumb-down"],["Autre","otherDown","thumb-down"]],["Dernière mise à jour le 2025/09/09 (UTC)."],[],[],null,["# Combine TOKENLISTs\n\n| **Note:** This feature is available with the Spanner Enterprise edition and Enterprise Plus edition. For more information, see the [Spanner editions overview](/spanner/docs/editions-overview).\n\n\u003cbr /\u003e\n\nThis page describes how to concatenate `TOKENLIST`s in either a\n[search index](/spanner/docs/full-text-search) when you set up your schema\nor in a search query when performing a full-text search in Spanner.\n\nCombine TOKENLISTs in a search index\n------------------------------------\n\nSometimes, you need your application to search across individual fields. At\nother times, the application needs to search across all fields. For example, in\na table with two string columns, you might want your application to search\nacross both columns without differentiating which column the matches come from.\n\nIn Spanner, there are two ways to achieve this:\n\n1. [Tokenize words separately and concatenate the resulting `TOKENLIST`s\n (recommended).](#tokenize-separately)\n2. [Concatenate strings and tokenize the result.](#concatenate-strings)\n\nWith the second approach, there are two problems:\n\n1. If you want to index `Title` or `Studio` individually, in addition to indexing them in a combined `TOKENLIST`, the same text is tokenized twice. This causes transactions to use more resources.\n2. A phrase search spans both fields. For example, if `@p` is set to `\"Blue Note\"`, it matches a row that contains both `Title`=\"Big Blue Note\" and `Studio`=\"Blue Note Studios\".\n\nThe first approach solves these problems because a phrase only matches one field\nand each string field is only tokenized once if both the individual and combined\n`TOKENLIST`s are indexed. Even though each string field is only tokenized once,\nthe resulting `TOKENLIST`s are stored separately in the index.\n\n### Tokenize words separately and concatenate `TOKENLIST`s\n\nThe following example tokenizes each word and uses\n[`TOKENLIST_CONCAT`](/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat)\nto concatenate the `TOKENLIST`s: \n\n### GoogleSQL\n\n CREATE TABLE Albums (\n AlbumId STRING(MAX) NOT NULL,\n Title STRING(MAX),\n Studio STRING(MAX),\n Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,\n Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN,\n Combined_Tokens TOKENLIST AS (TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens])) HIDDEN,\n ) PRIMARY KEY(AlbumId);\n\n CREATE SEARCH INDEX AlbumsIndex ON Albums(Combined_Tokens);\n\n SELECT AlbumId FROM Albums WHERE SEARCH(Combined_Tokens, @p);\n\n### PostgreSQL\n\nPostgreSQL uses\n[`spanner.tokenlist_concat`](/spanner/docs/reference/postgresql/functions-and-operators#search_functions)\nfor concatenation. The query parameter `$1` is bound to 'Hatel Kaliphorn'. \n\n CREATE TABLE albums (\n albumid character varying NOT NULL,\n title character varying,\n studio character varying,\n title_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(title)) VIRTUAL HIDDEN,\n studio_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(studio)) VIRTUAL HIDDEN,\n combined_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenlist_concat(ARRAY[spanner.tokenize_fulltext(title), spanner.tokenize_fulltext(studio)])) VIRTUAL HIDDEN,\n PRIMARY KEY(albumid));\n\n CREATE SEARCH INDEX albumsindex ON albums(combined_tokens);\n\n SELECT albumid FROM albums WHERE spanner.search(combined_tokens, $1);\n\nNote that `tokenlist_concat` doesn't call `title_tokens` or `studio_tokens`,\nbut instead calls `spanner.tokenize_fulltext(title)` and\n`spanner.tokenize_fulltext(studio)`. This is because PostgreSQL\ndoesn't support referencing generated columns that are within other generated\ncolumns. `spanner.tokenlist_concat` needs to call tokenize functions and not\nreference tokenlist columns directly.\n\n`TOKENLIST` concatenation can also be implemented entirely on the query side.\nFor more information, see [Query-side `TOKENLIST`\nconcatenation](#concatenate-tokenlists-query).\n\n`TOKENLIST_CONCAT` is supported for both full-text and\n[substring](/spanner/docs/full-text-search/substring-search) searches.\nSpanner doesn't let you mix tokenization types, such as\n`TOKENIZE_FULLTEXT` and `TOKENIZE_SUBSTRING` in the same `TOKENLIST_CONCAT`\ncall.\n\nIn GoogleSQL, the definition of text `TOKENLIST` columns can be\nchanged in non-stored columns to add additional columns. This is useful when you\nwant to add an additional column to `TOKENLIST_CONCAT`. Changing the generated\ncolumn expression doesn't backfill existing rows in the index.\n\n### Concatenate strings and tokenize the result\n\nThe following example concatenates strings and tokenizes the result: \n\n### GoogleSQL\n\n CREATE TABLE Albums (\n AlbumId STRING(MAX) NOT NULL,\n Title STRING(MAX),\n Studio STRING(MAX),\n Combined_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title || \" \" || Studio)) HIDDEN,\n ) PRIMARY KEY(AlbumId);\n\n CREATE SEARCH INDEX AlbumsIndex ON Albums(Combined_Tokens);\n\n SELECT AlbumId FROM Albums WHERE SEARCH(Combined_Tokens, @p);\n\n### PostgreSQL\n\n CREATE TABLE albums (\n albumid character varying NOT NULL,\n title character varying,\n studio character varying,\n combined_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(title || ' ' || studio)) VIRTUAL HIDDEN,\n PRIMARY KEY(albumid));\n\n CREATE SEARCH INDEX albumsindex ON albums(combined_tokens);\n\n SELECT albumid FROM albums WHERE spanner.search(combined_tokens, $1);\n\nQuery-side `TOKENLIST` concatenation\n------------------------------------\n\nThe tradeoff with indexing the concatenated `TOKENLIST` is that it increases\nstorage and write cost. Each token is now stored on the disk twice:\nonce in a posting list of its original `TOKENLIST`, and once in a posting list\nof the combined `TOKENLIST`. Query-side concatenation of `TOKENLIST` columns\navoids this cost but the query uses more compute resources.\n\nTo concatenate multiple `TOKENLIST`s, use the\n[`TOKENLIST_CONCAT`](/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat)\nfunction in the\n[`SEARCH`](/spanner/docs/reference/standard-sql/search_functions#search_fulltext)\nquery. For this section, we're using the following sample schema: \n\n### GoogleSQL\n\n CREATE TABLE Albums (\n AlbumId STRING(MAX) NOT NULL,\n Title STRING(MAX),\n Studio STRING(MAX),\n Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,\n Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN,\n ) PRIMARY KEY(AlbumId);\n\n CREATE SEARCH INDEX AlbumsIndex ON Albums(Title_Tokens, Studio_Tokens);\n\n### PostgreSQL\n\n CREATE TABLE albums (\n albumid character varying NOT NULL,\n title character varying,\n studio character varying,\n title_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(title)) VIRTUAL HIDDEN,\n studio_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(studio)) VIRTUAL HIDDEN,\n PRIMARY KEY(albumid));\n\n CREATE SEARCH INDEX albumsindex ON albums(title_tokens, studio_tokens);\n\nThe following query searches for rows that have the tokens \"blue\"\nand \"note\" anywhere in the `Title` and `Studio` columns. This includes\nrows with both \"blue\" and \"note\" in the `Title` column, \"blue\" and \"note\" in the\n`Studio` column, and \"blue\" in the `Title` column and \"note\" in the `Studio`\ncolumn, or the opposite. \n\n### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(TOKENLIST_CONCAT([AlbumTitle_Tokens, Studio_Tokens]), 'blue note')\n\n### PostgreSQL\n\nThis example uses\n[`spanner.search`](/spanner/docs/reference/postgresql/functions-and-operators#search_functions)\nwith\n[`spanner.tokenlist_concat`](/spanner/docs/reference/postgresql/functions-and-operators#search_functions). \n\n SELECT albumid\n FROM albums\n WHERE spanner.search(spanner.tokenlist_concat(ARRAY[albumtitle_tokens, studio_tokens]), 'blue note')\n\nWrite-side and query-side `TOKENLIST` concatenation produce identical results.\nThe choice between the two is a trade-off between disk cost and query cost.\n\nAlternatively, an application could search multiple `TOKENLIST` columns and use\n`OR` along with the `SEARCH` function: \n\n### GoogleSQL\n\n SEARCH(AlbumTitle_Tokens, 'Blue Note') OR SEARCH(Studio_Tokens, 'Blue Note')\n\n### PostgreSQL\n\n spanner.search(albumtitle_tokens, 'Blue Note') OR spanner.search(studio_tokens, 'Blue Note')\n\nThis, however, has different semantics. It doesn't match albums where\n`AlbumTitle_Tokens` has \"blue\", but not \"note\" and `Studio_Tokens` has\n\"note\", but not \"blue\".\n\nWhat's next\n-----------\n\n- Learn about [full-text search queries](/spanner/docs/full-text-search/query-overview).\n- Learn about [search indexes](/spanner/docs/full-text-search/search-indexes)."]]