Mantieni tutto organizzato con le raccolte
Salva e classifica i contenuti in base alle tue preferenze.
Questa pagina descrive come concatenare i valori TOKENLIST in un
indice di ricerca quando configuri lo schema o in una query di ricerca quando esegui una ricerca a testo intero in Spanner.
Combinare gli elenchi TOKENLIST in un indice di ricerca
A volte, è necessario che l'applicazione esegua ricerche in singoli campi. Altre volte, l'applicazione deve eseguire ricerche in tutti i campi. Ad esempio, in
una tabella con due colonne di stringhe, potresti volere che la tua applicazione esegua la ricerca
in entrambe le colonne senza distinguere da quale colonna provengono le corrispondenze.
Con il secondo approccio si verificano due problemi:
Se vuoi indicizzare Title o Studio singolarmente, oltre a
indicizzarli in un TOKENLIST combinato, lo stesso testo viene tokenizzato due volte.
Di conseguenza, le transazioni utilizzano più risorse.
Una ricerca di frasi interessa entrambi i campi. Ad esempio, se @p è impostato su
"Blue Note", corrisponde a una riga che contiene sia Title="Big Blue Note"
che Studio="Blue Note Studios".
Il primo approccio risolve questi problemi perché una frase corrisponde a un solo campo
e ogni campo di stringa viene tokenizzato una sola volta se sono indicizzati sia i singoli sia i TOKENLIST combinati. Anche se ogni campo di stringa viene tokenizzato una sola volta,
i TOKENLIST risultanti vengono archiviati separatamente nell'indice.
Tokenizza le parole separatamente e concatena le TOKENLIST
L'esempio seguente tokenizza ogni parola e utilizza
TOKENLIST_CONCAT
per concatenare i TOKENLIST:
Tieni presente che tokenlist_concat non chiama title_tokens o studio_tokens, ma chiama spanner.tokenize_fulltext(title) e
spanner.tokenize_fulltext(studio). Questo perché PostgreSQL
non supporta i riferimenti alle colonne generate all'interno di altre colonne
generate. spanner.tokenlist_concat deve chiamare le funzioni di tokenizzazione e non fare riferimento direttamente alle colonne della lista di token.
La concatenazione TOKENLIST può essere implementata anche interamente a livello di query.
Per ulteriori informazioni, consulta la sezione Concatenazione TOKENLIST lato query.
TOKENLIST_CONCAT è supportato sia per le ricerche a testo intero sia per quelle con sottostringa.
Spanner non ti consente di combinare tipi di tokenizzazione, ad esempio TOKENIZE_FULLTEXT e TOKENIZE_SUBSTRING nella stessa chiamata TOKENLIST_CONCAT.
In GoogleSQL, la definizione delle colonne di testo TOKENLIST può essere modificata nelle colonne non archiviate per aggiungere altre colonne. Questa operazione è utile quando vuoi aggiungere un'altra colonna a TOKENLIST_CONCAT. La modifica dell'espressione della colonna generata non esegue il backfill delle righe esistenti nell'indice.
Concatena le stringhe e tokenizza il risultato
L'esempio seguente concatena le stringhe e tokenizza il risultato:
Il compromesso con l'indicizzazione del TOKENLIST concatenato è che aumenta il costo di archiviazione e scrittura. Ora ogni token viene archiviato sul disco due volte:
una volta in un elenco di post del TOKENLIST originale e una volta in un elenco di post
del TOKENLIST combinato. La concatenazione lato query delle colonne TOKENLIST consente di evitare questo costo, ma la query utilizza più risorse di calcolo.
Per concatenare più TOKENLIST, utilizza la funzione
TOKENLIST_CONCAT
nella query
SEARCH. Per questa sezione utilizziamo lo schema di esempio seguente:
La seguente query cerca le righe che contengono i token "blu"
e "nota" in qualsiasi posizione nelle colonne Title e Studio. Sono incluse
le righe con "blu" e "nota" nella colonna Title, "blu" e "nota" nella colonna
Studio, "blu" nella colonna Title e "nota" nella colonna Studio
o viceversa.
La concatenazione TOKENLIST lato scrittura e lato query produce risultati identici.
La scelta tra i due è un compromesso tra il costo del disco e il costo delle query.
In alternativa, un'applicazione potrebbe cercare in più colonne TOKENLIST e utilizzare
OR insieme alla funzione SEARCH:
[[["Facile da capire","easyToUnderstand","thumb-up"],["Il problema è stato risolto","solvedMyProblem","thumb-up"],["Altra","otherUp","thumb-up"]],[["Difficile da capire","hardToUnderstand","thumb-down"],["Informazioni o codice di esempio errati","incorrectInformationOrSampleCode","thumb-down"],["Mancano le informazioni o gli esempi di cui ho bisogno","missingTheInformationSamplesINeed","thumb-down"],["Problema di traduzione","translationIssue","thumb-down"],["Altra","otherDown","thumb-down"]],["Ultimo aggiornamento 2025-09-03 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)."]]