접두사·접미사·엔그램 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)
시뮬레이션된 엔그램 접근 방식
이 패치는 엔그램을 컬럼에 저장하는 시뮬레이션된 엔그램 방식을 만듭니다:
- 먼저 UDF를 사용해 데이터 수집 중 엔그램 컬럼 생성
- 쿼리를 사전 필터로 변환해 엔그램 컬럼으로 빠른 필터링 사용
- 트레이드오프: 디스크 크기는 커지지만 쿼리 성능이 크게 향상
구현 단계 (Implementation Steps)
- 접두사, 접미사, 엔그램 값으로 파생 컬럼 생성
- 파생 컬럼에 역인덱스 생성
- 엔그램 인덱스로 사전 필터링을 사용해 후보 행 감소
- 오탐을 피하기 위해 원본 텍스트 매치 검증 적용
자세한 내용은 GitHub PR #12392를 참고하세요.