TOKENLISTs kombinieren

Auf dieser Seite wird beschrieben, wie Sie TOKENLISTs entweder in einem Suchindex beim Einrichten des Schemas oder in einer Suchanfrage bei der Volltextsuche in Spanner verketten.

TOKENLISTs in einem Suchindex kombinieren

Manchmal muss Ihre Anwendung in einzelnen Feldern suchen. In anderen Fällen muss die Anwendung alle Felder durchsuchen. In einer Tabelle mit zwei Stringspalten soll 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:

  1. Wörter separat tokenisieren und die resultierenden TOKENLISTs verketten (empfohlen):
  2. Strings verketten und das Ergebnis tokenisieren:

Beim zweiten Ansatz gibt es zwei Probleme:

  1. Wenn Sie Title oder Studio einzeln indexieren möchten, zusätzlich zur Indexierung in einem kombinierten TOKENLIST, wird derselbe Text zweimal in Tokens zerlegt. Dadurch werden für Transaktionen mehr Ressourcen benötigt.
  2. Eine Suche nach einer Wortgruppe umfasst beide Felder. Wenn @p beispielsweise auf "Blue Note" festgelegt ist, wird eine Zeile gefunden, die sowohl Title="Big Blue Note" als auch Studio="Blue Note Studios" enthält.

Beim ersten Ansatz werden diese Probleme behoben, da eine Wortgruppe nur einem Feld entspricht und jedes Stringfeld nur einmal tokenisiert wird, wenn sowohl die einzelnen als auch die kombinierten TOKENLISTs indexiert werden. Obwohl jedes Stringfeld nur einmal tokenisiert wird, werden die resultierenden TOKENLISTs separat im Index gespeichert.

Wörter separat tokenisieren und TOKENLISTs verketten

Im folgenden Beispiel wird jedes Wort tokenisiert und TOKENLIST_CONCAT verwendet, um die TOKENLIST-Werte zu verketten:

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 verwendet spanner.tokenlist_concat für die Verkettung. Der Abfrageparameter $1 ist an „Hatel Kaliphorn“ gebunden.

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);

Beachten Sie, dass tokenlist_concat nicht title_tokens oder studio_tokens aufruft, sondern stattdessen spanner.tokenize_fulltext(title) und spanner.tokenize_fulltext(studio). Das liegt daran, dass PostgreSQL keine Referenzierung von generierten Spalten in anderen generierten Spalten unterstützt. spanner.tokenlist_concat muss Tokenize-Funktionen aufrufen und darf nicht direkt auf Tokenlist-Spalten verweisen.

Die TOKENLIST-Verkettung kann auch vollständig auf der Abfrageseite implementiert werden. Weitere Informationen finden Sie unter TOKENLIST-Verkettung auf Abfrageseite.

TOKENLIST_CONCAT wird sowohl für Volltext- als auch für Teilstring-Suchen unterstützt. In Spanner können Sie Tokenisierungstypen wie TOKENIZE_FULLTEXT und TOKENIZE_SUBSTRING nicht im selben TOKENLIST_CONCAT-Aufruf mischen.

In GoogleSQL kann die Definition von TOKENLIST-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 einer generierten Spalte ändern, werden vorhandene Zeilen im Index nicht neu gefüllt.

Strings verketten und das Ergebnis tokenisieren

Im folgenden Beispiel werden Strings verkettet und das Ergebnis wird tokenisiert:

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);

TOKENLIST-Verkettung auf Abfrageseite

Der Nachteil bei der Indexierung des verketteten TOKENLIST besteht darin, dass die Speicher- und Schreibkosten steigen. Jedes Token wird jetzt zweimal auf der Festplatte gespeichert: einmal in einer Posting-Liste des ursprünglichen TOKENLIST und einmal in einer Posting-Liste des kombinierten TOKENLIST. Durch die Verkettung von TOKENLIST-Spalten auf der Abfrageseite werden diese Kosten vermieden, aber für die Abfrage werden mehr Rechenressourcen benötigt.

Wenn Sie mehrere TOKENLIST-Elemente verketten möchten, verwenden Sie die Funktion TOKENLIST_CONCAT in der SEARCH-Abfrage. In diesem Abschnitt wird das folgende Beispielschema verwendet:

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);

Mit der folgenden Abfrage wird nach Zeilen gesucht, die die Tokens „blue“ und „note“ an beliebiger Stelle in den Spalten Title und Studio enthalten. Das umfasst Zeilen mit „blue“ und „note“ in der Spalte Title, „blue“ und „note“ in der Spalte Studio sowie „blue“ in der Spalte Title und „note“ in der Spalte Studio oder umgekehrt.

GoogleSQL

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

PostgreSQL

In diesem Beispiel wird spanner.search mit spanner.tokenlist_concat verwendet.

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

Die TOKENLIST-Verkettung auf der Schreib- und Abfrageseite führt zu identischen Ergebnissen. Die Wahl zwischen den beiden ist ein Kompromiss zwischen Festplatten- und Abfragekosten.

Alternativ kann eine Anwendung mehrere TOKENLIST-Spalten durchsuchen und OR zusammen mit der Funktion SEARCH verwenden:

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')

Dies hat jedoch eine andere Semantik. Sie entspricht nicht Alben, bei denen AlbumTitle_Tokens „blue“, aber nicht „note“ enthält und Studio_Tokens „note“, aber nicht „blue“ enthält.

Nächste Schritte