Combinar TOKENLISTs

Esta página descreve como concatenar TOKENLISTs em um índice de pesquisa ao configurar seu 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, é necessário que o aplicativo faça pesquisas em campos individuais. Em outros momentos, o aplicativo precisa pesquisar em todos os campos. Por exemplo, em uma tabela com duas colunas de string, talvez você queira que o app 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 concatenar os TOKENLISTs resultantes (recomendado).
  2. Concatenar strings e tokenizar o resultado.

Com a segunda abordagem, há dois problemas:

  1. Se você quiser indexar Title ou Studio individualmente, além de indexá-los em um TOKENLIST combinado, o mesmo texto será tokenizado duas vezes. Isso faz com que as transações usem mais recursos.
  2. A pesquisa de frase abrange os dois campos. Por exemplo, se @p for 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 corresponde apenas a um campo e cada campo de string é tokenizado apenas uma vez se os TOKENLISTs individuais e combinados forem indexados. Embora cada campo de string seja tokenizado apenas uma vez, os TOKENLISTs resultantes são armazenados separadamente no índice.

Tokenizar palavras separadamente e concatenar TOKENLISTs

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

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 em outras colunas geradas. O spanner.tokenlist_concat precisa chamar funções de tokenização e não referenciar colunas de lista de tokens diretamente.

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

O TOKENLIST_CONCAT é compatível com pesquisas de texto completo e substring. 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 texto TOKENLIST pode ser alterada em colunas não armazenadas para adicionar outras colunas. Isso é útil quando você quer adicionar outra coluna a TOKENLIST_CONCAT. Mudar a expressão da coluna gerada não preenche as linhas atuais no índice.

Concatenar strings e tokenizar 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 no lado da consulta

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

Para concatenar vários TOKENLISTs, use a função TOKENLIST_CONCAT na consulta SEARCH. Nesta seção, usamos o seguinte esquema de exemplo:

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 TOKENLIST do lado de gravação e da consulta produz resultados idênticos. A escolha entre os dois é um trade-off 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. 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