Combine TOKENLISTs

Esta página descreve como concatenar TOKENLISTs num índice de pesquisa quando configura o seu esquema ou numa consulta de pesquisa quando faz uma pesquisa de texto integral no Spanner.

Combine TOKENLISTs num índice de pesquisa

Por vezes, precisa que a sua aplicação pesquise em campos individuais. Noutras ocasiões, a aplicação tem de pesquisar em todos os campos. Por exemplo, numa tabela com duas colunas de strings, pode querer que a sua aplicação pesquise em ambas as colunas sem distinguir a coluna de origem das correspondências.

No Spanner, existem duas formas de o fazer:

  1. Tokenize as palavras separadamente e concatene os TOKENLISTs resultantes (recomendado).
  2. Concatenar strings e tokenizar o resultado.

Com a segunda abordagem, existem dois problemas:

  1. Se quiser indexar Title ou Studio individualmente, além de os indexar num TOKENLIST combinado, o mesmo texto é tokenizado duas vezes. Isto faz com que as transações usem mais recursos.
  2. Uma pesquisa de expressão abrange ambos os campos. Por exemplo, se @p estiver definido como "Blue Note", corresponde a uma linha que contém Title="Big Blue Note" e Studio="Blue Note Studios".

A primeira abordagem resolve estes problemas porque uma expressão só corresponde a um campo e cada campo de string só é tokenizado uma vez se os TOKENLISTs individuais e combinados forem indexados. Embora cada campo de string seja tokenizado apenas uma vez, os TOKENLIST resultantes são armazenados separadamente no índice.

Tokenize palavras separadamente e concatene TOKENLISTs

O exemplo seguinte 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 a concatenação. O parâmetro de consulta $1 está associado 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);

Tenha em atenção que tokenlist_concat não chama title_tokens nem studio_tokens, mas chama spanner.tokenize_fulltext(title) e spanner.tokenize_fulltext(studio). Isto deve-se ao facto de o PostgreSQL não suportar referências a colunas geradas que estejam dentro de outras colunas geradas. spanner.tokenlist_concat tem de chamar funções de tokenização e não fazer referência diretamente a colunas de listas de tokens.

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

TOKENLIST_CONCAT é suportado para pesquisas de texto completo e de subcadeias de carateres. 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 das colunas de texto TOKENLIST pode ser alterada em colunas não armazenadas para adicionar colunas adicionais. Isto é útil quando quer adicionar uma coluna adicional a TOKENLIST_CONCAT. A alteração da expressão da coluna gerada não preenche as linhas existentes no índice.

Concatenar strings e tokenizar o resultado

O exemplo seguinte 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 TOKENLIST do lado da consulta

A contrapartida da indexação do TOKENLIST concatenado é que aumenta o custo de armazenamento e gravação. Agora, cada token é armazenado no disco duas vezes: uma vez numa lista de publicações do respetivo TOKENLIST original e uma vez numa lista de publicações do TOKENLIST combinado. A concatenação do lado da consulta de colunas TOKENLIST evita este custo, mas a consulta usa mais recursos de computação.

Para concatenar vários TOKENLISTs, use a função TOKENLIST_CONCAT na consulta SEARCH. Para esta secção, estamos a usar 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 seguinte consulta pesquisa linhas que tenham os tokens "blue" e "note" em qualquer parte das colunas Title e Studio. Isto inclui linhas com "azul" e "nota" na coluna Title, "azul" e "nota" na coluna Studio, e "azul" na coluna Title e "nota" na coluna Studio, ou o contrário.

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 do lado da gravação e do lado da consulta produz resultados idênticos.TOKENLIST A escolha entre as duas opções é um equilíbrio entre o custo do disco e o custo da consulta.

Em alternativa, uma aplicação pode pesquisar várias colunas TOKENLIST e usar OR juntamente 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, tem uma semântica diferente. Não corresponde a álbuns em que AlbumTitle_Tokens tem "azul", mas não "nota", e Studio_Tokens tem "nota", mas não "azul".

O que se segue?