패싯을 사용하여 검색 수행

이 페이지에서는 전체 텍스트 검색에서 패싯을 사용하는 방법을 설명합니다. 패싯은 상호작용 필터링의 일부로, 잠재적인 필터 값과 해당 필터에 일치하는 항목 수를 의미합니다. 이 기능이 있는 웹사이트에서는 검색어를 입력하면 탐색 메뉴에 결과 목록이 표시되며, 각 카테고리별 결과 수와 함께 검색 결과를 좁히는 데 사용할 수 있는 패싯 목록이 표시됩니다.

예를 들어 "foo"라는 쿼리를 사용해 앨범을 검색하면 수백 개의 결과가 반환될 수 있습니다. 장르 패싯이 있는 경우 "록(250)," "알앤비(50)," "팝(150)"과 같이 장르별로 선택할 수 있습니다.

Spanner 전체 텍스트 검색에서는 필터링과 필터링된 항목 수 계산 모두에 표준 SQL 표현식과 전체 텍스트 검색 함수를 사용할 수 있습니다. 패싯을 사용하기 위해 특수 구문을 사용할 필요는 없습니다.

전체 텍스트 검색에 사용할 패싯 추가

다음 예시는 앨범용 테이블을 만들고 각 앨범의 제목을 토큰화합니다. 이 테이블은 이 페이지의 예시에서 사용됩니다.

GoogleSQL

CREATE TABLE Albums (
  AlbumId INT64 NOT NULL,
  Title STRING(MAX),
  Rating INT64,
  Genres ARRAY<STRING(MAX)>,
  Likes INT64 NOT NULL,
  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
) PRIMARY KEY(AlbumId);

PostgreSQL

CREATE TABLE albums (
  albumid bigint NOT NULL,
  title text,
  rating bigint,
  genres text[],
  likes bigint NOT NULL,
  title_tokens spanner.TOKENLIST GENERATED ALWAYS AS (spanner.TOKENIZE_FULLTEXT(Title)) VIRTUAL HIDDEN,
PRIMARY KEY(albumid));

Title_Tokens에 검색 색인을 만듭니다. 선택적으로, 패싯을 계산하는 동안 기본 테이블로의 백조인을 방지하기 위해 검색 색인에 Title, Genres, Rating을 저장할 수 있습니다.

GoogleSQL

CREATE SEARCH INDEX AlbumsIndex
ON Albums (Title_Tokens)
STORING (Title, Genres, Rating)
ORDER BY Likes DESC;

PostgreSQL

CREATE SEARCH INDEX albumsindex
ON albums (title_tokens)
INCLUDE (title, genres, rating)
ORDER BY likes DESC

이 예시에서는 다음 데이터를 테이블에 삽입합니다.

GoogleSQL

INSERT INTO Albums (AlbumId, Title, Rating, Genres, Likes) VALUES
(1, "The Foo Strike Again", 5, ["Rock", "Alternative"], 600),
(2, "Who are the Who?", 5, ["Progressive", "Indie"], 200),
(3, "No Foo For You", 4, ["Metal", "Alternative"], 50)

PostgreSQL

INSERT INTO albums (albumid, title, rating, genres, likes) VALUES
(1, 'The Foo Strike Again', 5,'{"Rock", "Alternative"}', 600),
(2, 'Who are the Who?', 5,'{"Progressive", "Indie"}', 200),
(3, 'No Foo For You', 4,'{"Metal", "Alternative"}', 50)

단일 패싯의 카운트 값 검색

이 예시에서는 Rating 패시에 대해 패싯 카운트를 수행하는 방법을 보여줍니다. 이 예시에서는 Albums 테이블의 Title_Tokens 열에서 "foo"라는 텍스트를 검색합니다.

GoogleSQL

SELECT Rating, COUNT(*) AS result_count
FROM Albums
WHERE SEARCH(Title_Tokens, "foo")
GROUP BY Rating
ORDER BY Rating DESC

| Rating | result_count |
|--------|--------------|
| 5      | 1            |
| 4      | 1            |

PostgreSQL

SELECT rating, COUNT(*) AS result_count
FROM albums
WHERE spanner.SEARCH(title_tokens, 'foo')
GROUP BY rating
ORDER BY rating DESC;

| rating | result_count |
|--------|--------------|
| 5      | 1            |
| 4      | 1            |

여러 패싯의 카운트 값 검색

이 예시는 여러 패싯에 대해 패싯 카운트를 수행하는 단계를 보여줍니다. 이 예시는 다음 작업을 수행합니다.

  1. 초기 검색 결과를 검색합니다. Albums 테이블의 Title_Tokens 열에서 "foo"라는 텍스트를 검색합니다.
  2. 패싯 카운트를 계산합니다. 그런 다음 RatingGenres 패싯의 카운트를 계산합니다.

GoogleSQL

WITH search_results AS (
  SELECT AlbumId, Title, Genres, Rating, Likes
  FROM Albums
  WHERE SEARCH(Title_Tokens, "foo")
  ORDER BY Likes DESC, AlbumId
  LIMIT 10000
)

SELECT
-- Result set #1: First page of search results
ARRAY(
  SELECT AS STRUCT *
  FROM search_results
  ORDER BY Likes DESC, AlbumId
  LIMIT 50
) as result_page,
-- Result set #2: Number of results by rating
ARRAY(
  SELECT AS STRUCT Rating, COUNT(*) as result_count
  FROM search_results
  GROUP BY Rating
  ORDER BY result_count DESC, Rating DESC
) as rating_counts,
-- Result set #3: Number of results for top 5 genres
ARRAY(
  SELECT AS STRUCT genre, COUNT(*) as result_count
  FROM search_results
  JOIN UNNEST(Genres) genre
  GROUP BY genre
  ORDER BY result_count DESC, genre
  LIMIT 5
) as genres_counts

PostgreSQL

WITH search_results AS (
  SELECT albumid, title, genres, rating, likes
  FROM albums
  WHERE spanner.SEARCH(title_tokens, 'foo')
  ORDER BY likes DESC, albumid
  LIMIT 10000
)

-- The pattern ARRAY(SELECT TO_JSONB ...) enables returning multiple nested
-- result sets in the same query.
SELECT
  -- Result set #1: First page of search results
  ARRAY(
  SELECT JSONB_BUILD_OBJECT(
    'albumid', albumid,
    'title', title,
    'genres', genres,
    'rating', rating,
    'likes', likes
    )
  FROM search_results
  ORDER BY likes DESC, albumid
  LIMIT 50
) as result_page,
-- Result set #2: Number of results by rating
ARRAY(
  SELECT JSONB_BUILD_OBJECT(
    'rating', rating,
    'result_count', COUNT(*)
    )
  FROM search_results
  GROUP BY rating
  ORDER BY COUNT(*) DESC, rating DESC
) as rating_counts,
-- Result set #3: Number of results for top 5 genres
ARRAY(
  SELECT JSONB_BUILD_OBJECT(
    'genre', genre,
    'result_count', COUNT(*)
    )
  FROM
    search_results,
    UNNEST(genres) AS genre
  GROUP BY genre
  ORDER BY COUNT(*) DESC, genre
  LIMIT 5
) as genres_counts

구체적으로, 이 예시는 다음을 수행합니다.

  • WITH search_results AS (...) 구문은 첫 번째 결과 페이지와 패싯 계산에 사용할 대규모 초기 검색 결과 집합을 수집합니다.
  • SEARCH(Title_Tokens, "foo")는 기본 검색 쿼리입니다.
  • LIMIT 10000은 결과 집합을 10,000개로 제한하여 검색 비용을 줄입니다. 수백만 개의 결과를 반환할 수 있는 매우 광범위한 검색의 경우, 전체 데이터 세트에 대해 정확한 패싯 카운트를 계산하는 데 비용이 많이 들 수 있습니다. 검색 결과를 제한하면 쿼리가 근사치(하한선) 형태의 패싯 카운트를 빠르게 제공할 수 있습니다. 즉, 이 카운트는 최소한 그만큼의 결과를 반영하지만, 10,000개 제한을 초과하는 추가 일치 결과가 있을 수도 있습니다.
  • result_page 서브 쿼리는 사용자에게 표시되는 첫 번째 검색 결과 페이지를 생성합니다. 이 쿼리는 LikesAlbumId 기준으로 정렬된 search_results에서 상위 50개 레코드만 선택합니다. 이것이 사용자가 처음에 보게 되는 화면입니다.
  • rating_counts 서브 쿼리는 Rating에 대한 패싯 카운트를 계산합니다. 이 쿼리는 search_results의 모든 레코드를 Rating별로 그룹화하고, 각 등급 카테고리에 속하는 결과 수를 계산합니다.
  • genres_counts 서브 쿼리는 Genres에 대한 패싯 카운트를 계산합니다. 배열이므로, UNNEST(Genres)를 사용해 배열 내 각 장르를 개별 행으로 처리하여 카운트를 수행합니다.

후속 페이지 검색

초기 패싯 쿼리 이후 연속된 페이지를 쿼리할 때는 첫 번째 페이지에서 반환된 패싯 카운트를 재사용할 수 있습니다.

페이지로 나누는 방법에 대한 자세한 내용은 키 기반 페이지로 나누기 사용을 참조하세요.