TOKENLIST 결합

이 페이지에서는 스키마를 설정할 때 검색 색인에서 또는 Spanner에서 전체 텍스트 검색을 실행할 때 검색어에서 TOKENLIST를 연결하는 방법을 설명합니다.

검색 색인에서 TOKENLIST 결합

때로는 애플리케이션에서 개별 필드를 검색해야 하는 경우가 있습니다. 반면에 애플리케이션에서 모든 필드를 검색해야 하는 경우도 있습니다. 예를 들어 두 개의 문자열 열이 있는 테이블에서 애플리케이션이 일치 항목이 어느 열에서 비롯되었는지 구분하지 않고 두 열을 모두 검색하도록 할 수 있습니다.

Spanner에서는 다음 두 가지 방법으로 이 작업을 수행할 수 있습니다.

  1. 단어를 개별적으로 토큰화하고 결과 TOKENLIST를 연결합니다(권장).
  2. 문자열을 연결하고 결과를 토큰화합니다.

두 번째 접근 방식에는 두 가지 문제가 있습니다.

  1. Title 또는 Studio를 개별적으로 색인화하려면 결합된 TOKENLIST에 색인화하는 것 외에도 동일한 텍스트가 두 번 토큰화됩니다. 이로 인해 트랜잭션에서 더 많은 리소스를 사용하게 됩니다.
  2. 구문 검색은 두 필드 모두에 적용됩니다. 예를 들어 @p"Blue Note"로 설정되면 Title='Big Blue Note' 및 Studio='Blue Note Studios'가 모두 포함된 행과 일치합니다.

첫 번째 접근 방식은 구문이 하나의 필드에만 일치하고 각 문자열 필드는 개별 및 결합된 TOKENLIST가 모두 색인 생성된 경우에만 한 번 토큰화되므로 이러한 문제를 해결합니다. 각 문자열 필드는 한 번만 토큰화되더라도 결과 TOKENLIST는 색인에 별도로 저장됩니다.

단어를 개별적으로 토큰화하여 TOKENLIST 연결

다음 예에서는 각 단어를 토큰화하고 TOKENLIST_CONCAT을 사용하여 TOKENLIST를 연결합니다.

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은 연결에 spanner.tokenlist_concat을 사용합니다. 쿼리 매개변수 $1은 '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_concattitle_tokens 또는 studio_tokens를 호출하지 않고 대신 spanner.tokenize_fulltext(title)spanner.tokenize_fulltext(studio)를 호출한다는 점에 유의하세요. 이는 PostgreSQL이 다른 생성된 열 내에 있는 생성된 열 참조를 지원하지 않기 때문입니다. spanner.tokenlist_concat는 tokenize 함수를 호출해야 하며 tokenlist 열을 직접 참조해서는 안 됩니다.

TOKENLIST 연결은 쿼리 측에서 전적으로 구현할 수도 있습니다. 자세한 내용은 쿼리 측 TOKENLIST 연결을 참조하세요.

TOKENLIST_CONCAT은 전체 텍스트 검색과 하위 문자열 검색 모두에서 지원됩니다. Spanner에서는 동일한 TOKENLIST_CONCAT 호출에서 TOKENIZE_FULLTEXTTOKENIZE_SUBSTRING 같은 토큰화 유형을 혼합할 수 없습니다.

GoogleSQL에서는 저장되지 않은 열에서 텍스트 TOKENLIST 열의 정의를 변경하여 열을 추가할 수 있습니다. TOKENLIST_CONCAT에 열을 추가하려는 경우에 유용합니다. 생성된 열 표현식을 변경해도 색인의 기존 행이 백필되지 않습니다.

문자열 연결 및 결과 토큰화

다음 예시에서는 문자열을 연결하고 결과를 토큰화합니다.

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 연결

연결된 TOKENLIST에 색인을 생성하면 스토리지 및 쓰기 비용이 증가한다는 단점이 있습니다. 이제 각 토큰은 원래 TOKENLIST의 게시 목록에 한 번, 결합된 TOKENLIST의 게시 목록에 한 번, 디스크에 두 번 저장됩니다. TOKENLIST 열의 쿼리 측 연결은 이 비용을 피할 수 있지만 쿼리에는 더 많은 컴퓨팅 리소스가 사용됩니다.

여러 개의 TOKENLIST를 연결하려면 SEARCH 쿼리에서 TOKENLIST_CONCAT 함수를 사용합니다. 이 섹션에서는 다음 샘플 스키마를 사용합니다.

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

다음 쿼리는 TitleStudio 열의 어느 곳에서나 'blue' 및 'note' 토큰이 있는 행을 검색합니다. 여기에는 Title 열에 'blue' 및 'note'가 모두 포함된 행, Studio 열에 'blue' 및 'note'가 모두 포함된 행, Title 열에 'blue'가 있고 Studio 열에 'note'가 있거나 그 반대인 행이 포함됩니다.

GoogleSQL

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

PostgreSQL

이 예시에서는 spanner.tokenlist_concat와 함께 spanner.search을 사용합니다.

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

쓰기 측과 쿼리 측 TOKENLIST 연결은 동일한 결과를 생성합니다. 두 가지 중에서 선택할 때는 디스크 비용과 쿼리 비용 간의 절충이 이루어집니다.

또는 애플리케이션이 여러 TOKENLIST 열을 검색하고 SEARCH 함수와 함께 OR을 사용할 수 있습니다.

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

그러나 시맨틱스는 다릅니다. AlbumTitle_Tokens에는 'blue'가 있지만 'note'는 없고 Studio_Tokens에는 'note'가 있지만 'blue'가 없는 앨범과는 일치하지 않습니다.

다음 단계