Combinar TOKENLISTs

Esta página descreve como concatenar TOKENLISTs em um índice de pesquisa ao configurar o esquema ou em uma consulta de pesquisa ao realizar uma pesquisa de texto completo no Spanner.

Combinar TOKENLISTs em um índice de pesquisa

Às vezes, o aplicativo precisa pesquisar em campos individuais. Em outras ocasiões, o aplicativo precisa pesquisar em todos os campos. Por exemplo, em uma tabela com duas colunas de string, talvez você queira que o aplicativo pesquise nas duas colunas sem diferenciar de qual coluna as correspondências vêm.

No Spanner, há duas maneiras de fazer isso:

  1. Tokenize palavras separadamente e concatene as TOKENLISTs resultantes (recomendado).
  2. Concatene strings e tokeniza o resultado.

Com a segunda abordagem, há dois problemas:

  1. Se você quiser indexar Title ou Studio individualmente, além de indexá-los em uma TOKENLIST combinada, o mesmo texto será tokenizado duas vezes. Isso faz com que as transações usem mais recursos.
  2. Uma pesquisa de frase abrange os dois campos. Por exemplo, se @p estiver definido como "Blue Note", ele vai corresponder a uma linha que contém Title="Big Blue Note" e Studio="Blue Note Studios".

A primeira abordagem resolve esses problemas porque uma frase só corresponde a um campo e cada campo de string só é tokenizado uma vez se as TOKENLISTs individuais e combinadas forem indexadas. Embora cada campo de string seja tokenizado apenas uma vez, as TOKENLISTs resultantes são armazenadas separadamente no índice.

Tokenizar palavras separadamente e concatenar TOKENLISTs

O exemplo a seguir tokeniza cada palavra e usa TOKENLIST_CONCAT para concatenar as 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

O PostgreSQL usa spanner.tokenlist_concat para concatenação. O parâmetro de consulta $1 está vinculado a "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);

Observe que tokenlist_concat não chama title_tokens ou studio_tokens, mas chama spanner.tokenize_fulltext(title) e spanner.tokenize_fulltext(studio). Isso ocorre porque o PostgreSQL não oferece suporte à referência de colunas geradas que estão dentro de outras colunas geradas. spanner.tokenlist_concat precisa chamar funções de tokenização e não referenciar colunas de tokenlist diretamente.

A concatenação de TOKENLIST também pode ser implementada totalmente no lado da consulta. Para mais informações, consulte Concatenação TOKENLIST do lado da consulta.

TOKENLIST_CONCAT é compatível com pesquisas de texto completo e de substring searches. O Spanner não permite misturar tipos de tokenização, como TOKENIZE_FULLTEXT e TOKENIZE_SUBSTRING na mesma chamada TOKENLIST_CONCAT.

No GoogleSQL, a definição de colunas de TOKENLIST de texto pode ser alterada em colunas não armazenadas para adicionar outras colunas. Isso é útil quando você quer adicionar outra coluna a TOKENLIST_CONCAT. A alteração da expressão da coluna gerada não preenche as linhas atuais no índice.

Concatene strings e tokeniza o resultado

O exemplo a seguir concatena strings e tokeniza o resultado:

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

Concatenação de TOKENLIST do lado da consulta

A desvantagem de indexar a TOKENLIST concatenada é que ela aumenta o custo de armazenamento e gravação. Cada token agora é armazenado no disco duas vezes: uma em uma lista de postagens da TOKENLIST original e outra em uma lista de postagens da TOKENLIST combinada. A concatenação de colunas TOKENLIST do lado da consulta evita esse custo, mas a consulta usa mais recursos de computação.

Para concatenar várias TOKENLISTs, use a TOKENLIST_CONCAT função na SEARCH consulta. Para esta seção, estamos usando o seguinte esquema de amostra:

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

A consulta a seguir pesquisa linhas que têm os tokens "blue" e "note" em qualquer lugar nas colunas Title e Studio. Isso inclui linhas com "blue" e "note" na coluna Title, "blue" e "note" na coluna Studio e "blue" na coluna Title e "note" na coluna Studio, ou o oposto.

GoogleSQL

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

PostgreSQL

Este exemplo usa spanner.search com spanner.tokenlist_concat.

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

A concatenação de TOKENLIST do lado da gravação e da consulta produz resultados idênticos. A escolha entre os dois é uma compensação entre o custo do disco e o custo da consulta.

Como alternativa, um aplicativo pode pesquisar várias colunas TOKENLIST e usar OR com a função 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')

No entanto, isso tem semântica diferente. Ele não corresponde a álbuns em que AlbumTitle_Tokens tem "blue", mas não "note" e Studio_Tokens tem "note", mas não "blue".

A seguir