접두사·접미사·엔그램 UDF

접두사·접미사·엔그램 UDF (Add Prefix, Suffix & Ngram UDFs)

접두사(prefix), 접미사(postfix/suffix), 엔그램(ngram) UDF에 대한 레퍼런스 문서예요. 파생 컬럼과 역인덱스(inverted index)를 통해 효율적인 텍스트 검색을 가능하게 해 쿼리 처리량을 개선하는 것이 목적입니다.

출처: 문서

본문

배경 (Context)

매칭 사용 사례의 쿼리 처리량을 높이는 것이 목표였어요. 초기 테스트에서 REGEXP_LIKE와 TEXT_MATCH 쿼리를 사용하는 현재 접근 방식이 성능 한계에 도달했고, 이러한 방법으로는 QPS 개선이 더 이상 불가능하다는 것을 확인했습니다. 평가 중인 대표 쿼리는 다음과 같아요.

SELECT col1, col2 FROM table WHERE REGEXP_LIKE(col3, '^data*')
SELECT col1, col2 FROM table WHERE REGEXP_LIKE(col3, 'data$')
SELECT col1, col2 FROM table WHERE REGEXP_LIKE(col3, '*data*')
SELECT col1, col2 FROM table WHERE TEXT_MATCH(col3, '/data*/')

계획은 접두사, 접미사, 엔그램을 영속화하는 파생 컬럼을 생성해 역인덱스로 결과를 빠르게 필터링하고, 필터링 후 텍스트 매치 인덱스를 사용해 검증해 오탐(false positive)을 피하는 것입니다.

엔그램이란 (What is N-gram)

엔그램은 텍스트를 겹치는 문자 시퀀스로 분해해요. 예를 들어:

"Apache pinot" 는 다음을 생성합니다:

  • ap, pa, ac, ch, he, e, p, ...

ngram(col, "ap")로 일치시키면 생성된 엔그램 안에서 부분 문자열 "ap"를 검색해요.

ngram(col, 'apache')로 일치시키면 "apache"에서 엔그램(ap, pa, ac 등)을 생성해 컬럼의 엔그램과 대조합니다.

엔그램 사용 시점 (When to Use N-gram)

엔그램은 다음의 성능 문제를 해결해요:

  • 텍스트 매치(Text match) - 전체 스캔 회피
  • REGEXP_LIKE - 현재 전체 스캔 필요
  • 접두사 일치(Prefix matching) - O(N * 문자열 길이) 복잡도 감소
  • 와일드카드 일치(Wildcard matching) - O(N * 길이) 복잡도 감소

와일드카드를 빠르게 만드는 방법

모든 행의 전체 스캔을 피하려면 사전 필터링이 필요합니다. 사전 필터링 방법 → 엔그램을 사용하세요!

함수 파라미터

함수 파라미터 설명
prefixes String input, int maxlength maxlength보다 짧은 접두사 문자열 배열 생성
prefixesWithPrefix String input, int maxlength, @Nullable String prefix 앞에 접두사가 붙은 접두사 매처 배열 생성(예: 정규식의 '^')
suffixes String input, int maxlength maxlength보다 짧은 접미사 문자열 배열 생성
suffixesWithSuffix String input, int maxlength, @Nullable String suffix 뒤에 접미사가 붙은 접미사 매처 배열 생성(예: 정규식의 '$')
uniqueNgrams String input, int length 정확히 지정한 길이의 고유 엔그램 배열 생성
uniqueNgrams String input, int minGram, int maxGram 길이 범위 [minGram, maxGram] 내의 고유 엔그램 배열 생성
uniqueNgramsMV / generateUniqueNgramsMV String[] inputs, int length 다중 값 컬럼의 모든 입력 문자열에 걸쳐 정확히 지정한 길이의 고유 엔그램 생성

예시 쿼리

SELECT organizationUUID, confirmedEmployeeCount, name
FROM rta.rta.u4b_organizations
WHERE deletedAt IS NULL
  AND (IN_SERIALIZED_LIST(entityTypes, 'org:u4b:organization'))
  AND (TEXT_MATCH(name, '/.*pacific.*/ AND /.*equity.*/')
       OR TEXT_MATCH(profileName, '/.*pacific.*/ AND /.*equity.*/'))
ORDER BY confirmedEmployeeCount DESC
OFFSET 0 LIMIT 10

접두사 연산 (Prefix Operations)

SELECT prefixes('data', 3) AS result
FROM myTable
result
["d", "da", "dat"]
SELECT prefixesWithPrefix('data', 3, '^') AS result
FROM myTable
result
["^d", "^da", "^dat"]

접미사 연산 (Suffix Operations)

SELECT suffixes('data', 3) AS result
FROM myTable
result
["a", "ta", "ata"]
SELECT suffixesWithSuffix('data', 3, '$') AS result
FROM myTable
result
["a$", "ta$", "ata$"]

엔그램 생성 (N-gram Generation)

변환이 있는 테이블 설정 (Table Config with Transformation)

SQL UDF 대신 transformationConfig를 사용해 수집 중 엔그램 생성을 설정할 수 있어요.

{
  "tableName": "myTable",
  "tableType": "OFFLINE",
  "ingestionConfig": {
    "transformationConfigs": [
      {
        "columnName": "content_bigrams",
        "transformFunction": "uniqueNgrams(content, 2)"
      },
      {
        "columnName": "content_ngrams",
        "transformFunction": "uniqueNgrams(content, 2, 4)"
      }
    ]
  }
}

쿼리 예시

SELECT uniqueNgrams('Apache pinot', 2) AS bigrams
FROM myTable
bigrams
["Ap", "pa", "ac", "ch", "he", "e ", " p", "pi", "in", "no", "ot"]
SELECT uniqueNgrams('data', 2, 4) AS ngrams
FROM myTable
ngrams
["da", "at", "ta", "dat", "ata", "data"]

다중 값 컬럼 예시

SELECT uniqueNgramsMV(ARRAY['ab', 'bc'], 2) AS ngrams
FROM myTable
ngrams
["ab", "bc"]
SELECT uniqueNgramsMV(ARRAY['abcd'], 1, 2) AS ngrams
FROM myTable
ngrams
["a", "b", "c", "d", "ab", "bc", "cd"]

입력 배열이 null이거나 비어 있으면 Pinot은 빈 배열을 반환해요. null 요소와 요청한 엔그램 길이보다 짧은 값은 건너뜁니다.

생성된 엔그램 사용하기

-- 기존 방식 (느림)
SELECT * FROM T WHERE REGEXP_LIKE(field, '*pino.*')

-- 엔그램 사전 필터링을 사용한 최적화 방식
SELECT * FROM T
WHERE ngram = 'pin' AND ngram = 'ino' AND ngram = 'not'
  AND REGEXP_LIKE(field, '*pino.*')
-- 짧은 문자열의 기존 방식
SELECT * FROM T WHERE REGEXP_LIKE(field, '*pi.*')

-- 최적화 방식 (검색 문자열 ≤ 엔그램 길이이면 검증 불필요)
SELECT * FROM T WHERE ngram = 'pi'

엔그램 사용 방법 (How to Use N-gram)

시뮬레이션된 엔그램 접근 방식

이 패치는 엔그램을 컬럼에 저장하는 시뮬레이션된 엔그램 방식을 만듭니다:

  1. 먼저 UDF를 사용해 데이터 수집 중 엔그램 컬럼 생성
  2. 쿼리를 사전 필터로 변환해 엔그램 컬럼으로 빠른 필터링 사용
  3. 트레이드오프: 디스크 크기는 커지지만 쿼리 성능이 크게 향상

구현 단계 (Implementation Steps)

  1. 접두사, 접미사, 엔그램 값으로 파생 컬럼 생성
  2. 파생 컬럼에 역인덱스 생성
  3. 엔그램 인덱스로 사전 필터링을 사용해 후보 행 감소
  4. 오탐을 피하기 위해 원본 텍스트 매치 검증 적용

자세한 내용은 GitHub PR #12392를 참고하세요.

더 알아보기 (Learn more)