Mit Sammlungen den Überblick behalten
Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.
Auf dieser Seite wird beschrieben, wie Sie TOKENLISTs entweder in einem Suchindex beim Einrichten Ihres Schemas oder in einer Suchanfrage bei einer Volltextsuche in Spanner zusammenführen.
TOKENLISTs in einem Suchindex kombinieren
Manchmal muss Ihre Anwendung in einzelnen Feldern suchen. In anderen Fällen muss die Anwendung in allen Feldern suchen. In einer Tabelle mit zwei Stringspalten kann Ihre Anwendung beispielsweise in beiden Spalten suchen, ohne zu unterscheiden, aus welcher Spalte die Übereinstimmungen stammen.
In Spanner gibt es zwei Möglichkeiten, dies zu erreichen:
Wenn Sie Title oder Studio einzeln indexieren möchten, wird derselbe Text nicht nur in einem kombinierten TOKENLIST indexiert, sondern auch zweimal tokenisiert.
Dadurch werden für Transaktionen mehr Ressourcen benötigt.
Eine Wortgruppensuche umfasst beide Felder. Wenn @p beispielsweise auf "Blue Note" festgelegt ist, wird eine Zeile mit Title=„Big Blue Note“ und Studio=„Blue Note Studios“ gefunden.
Mit dem ersten Ansatz werden diese Probleme gelöst, da eine Wortgruppe nur mit einem Feld übereinstimmt und jedes Stringfeld nur einmal tokenisiert wird, wenn sowohl die einzelnen als auch die kombinierten TOKENLISTs indexiert werden. Auch wenn jedes Stringfeld nur einmal tokenisiert wird, werden die resultierenden TOKENLISTs separat im Index gespeichert.
Wörter separat tokenisieren und TOKENLISTs zusammenführen
Im folgenden Beispiel werden die einzelnen Wörter tokenisiert und die TOKENLISTs mit TOKENLIST_CONCAT zusammengefügt:
Beachten Sie, dass tokenlist_concat nicht title_tokens oder studio_tokens, sondern spanner.tokenize_fulltext(title) und spanner.tokenize_fulltext(studio) aufruft. Das liegt daran, dass PostgreSQL keine Verweise auf generierte Spalten unterstützt, die sich in anderen generierten Spalten befinden. spanner.tokenlist_concat muss Tokenisierungsfunktionen aufrufen und nicht direkt auf Tokenlistenspalten verweisen.
Die TOKENLIST-Konkatenierung kann auch vollständig auf Abfrageseite implementiert werden.
Weitere Informationen finden Sie unter Abfrageseitige TOKENLISTVerkettung.
TOKENLIST_CONCAT wird sowohl für die Volltextsuche als auch für die Suche nach einem Teilstring unterstützt.
In Spanner können Sie keine Tokenisierungstypen wie TOKENIZE_FULLTEXT und TOKENIZE_SUBSTRING in einem einzigen TOKENLIST_CONCAT-Aufruf mischen.
In GoogleSQL kann die Definition von TextTOKENLIST-Spalten in nicht gespeicherten Spalten geändert werden, um zusätzliche Spalten hinzuzufügen. Das ist nützlich, wenn Sie TOKENLIST_CONCAT eine zusätzliche Spalte hinzufügen möchten. Wenn Sie den Ausdruck der generierten Spalte ändern, werden vorhandene Zeilen im Index nicht aufgefüllt.
Strings zusammenführen und das Ergebnis tokenisieren
Im folgenden Beispiel werden Strings zusammengefügt und das Ergebnis tokenisiert:
Die Indexierung der zusammengesetzten TOKENLIST erhöht jedoch die Speicher- und Schreibkosten. Jedes Token wird jetzt zweimal auf dem Laufwerk gespeichert: einmal in einer Postingliste der ursprünglichen TOKENLIST und einmal in einer Postingliste der kombinierten TOKENLIST. Durch die abfrageseitige Konkatenierung von TOKENLIST-Spalten werden diese Kosten vermieden, die Abfrage verbraucht jedoch mehr Rechenressourcen.
Wenn Sie mehrere TOKENLIST-Werte verketten möchten, verwenden Sie die Funktion TOKENLIST_CONCAT in der Abfrage SEARCH. In diesem Abschnitt verwenden wir das folgende Beispielschema:
Mit der folgenden Abfrage wird in den Spalten Title und Studio nach Zeilen gesucht, die die Tokens „blau“ und „Notiz“ enthalten. Dazu gehören Zeilen mit „blau“ und „Hinweis“ in der Spalte Title, „blau“ und „Hinweis“ in der Spalte Studio, „blau“ in der Spalte Title und „Hinweis“ in der Spalte Studio oder umgekehrt.
Die Konkatenierung von TOKENLIST auf Schreib- und Abfrageseite führt zu identischen Ergebnissen.
Die Entscheidung zwischen den beiden Optionen ist ein Kompromiss zwischen Speicher- und Abfragekosten.
Alternativ kann eine Anwendung in mehreren TOKENLIST-Spalten suchen und OR zusammen mit der Funktion SEARCH verwenden:
Dieser hat jedoch eine andere Semantik. Dies gilt nicht für Alben, bei denen AlbumTitle_Tokens „blau“ und nicht „Notiz“ und Studio_Tokens „Notiz“ und nicht „blau“ enthält.
[[["Leicht verständlich","easyToUnderstand","thumb-up"],["Mein Problem wurde gelöst","solvedMyProblem","thumb-up"],["Sonstiges","otherUp","thumb-up"]],[["Schwer verständlich","hardToUnderstand","thumb-down"],["Informationen oder Beispielcode falsch","incorrectInformationOrSampleCode","thumb-down"],["Benötigte Informationen/Beispiele nicht gefunden","missingTheInformationSamplesINeed","thumb-down"],["Problem mit der Übersetzung","translationIssue","thumb-down"],["Sonstiges","otherDown","thumb-down"]],["Zuletzt aktualisiert: 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)."]]