텍스트 검색 지원
텍스트 검색 지원 (Text Search Support)
Pinot의 텍스트 검색 지원에 대해 설명해요.
출처: 문서
본문
이 텍스트 인덱스 방식은 실험적인 native text index보다 권장돼요.
배경 정보를 건너뛰고 절차로 바로 가려면 per-column text index 활성화를 클릭하세요.
왜 텍스트 검색이 필요할까
Pinot는 비-BLOB 계열 컬럼의 인덱스를 통해 초고속 쿼리 처리를 지원해요. 정확 일치 필터 쿼리는 dictionary 인코딩, inverted index, sorted index의 조합으로 효율적으로 실행돼요.
이것은 각각 STRING과 INT 유형의 두 컬럼에 정확 일치를 찾는 다음 쿼리에 유용해요.
SELECT COUNT(*)
FROM Foo
WHERE STRING_COL = 'ABCDCD'
AND INT_COL > 2000
BLOB/CLOB 영역에 속하는 임의의 텍스트 데이터에 대해서는 정확 일치 이상이 필요해요. 여기에는 종종 BLOB 같은 데이터에 대한 regex, phrase, fuzzy 쿼리 사용이 포함돼요. 텍스트 인덱스는 각 컬럼 값이 큰 텍스트 BLOB인 STRING 컬럼에서 TEXT_MATCH 함수로 임의 검색을 효율적으로 수행할 수 있어요.
SELECT COUNT(*)
FROM Foo
WHERE TEXT_MATCH (<column_name>, '<search_expression>')
여기서 <column_name>은 텍스트 인덱스가 생성된 컬럼이고 <search_expression>은 다음 중 하나를 따르는 표현식이에요.
| Search Expression Type | Example |
|---|---|
| Phrase query | TEXT_MATCH (<column_name>, '"distributed system"') |
| Term Query | TEXT_MATCH (<column_name>, 'Java') |
| Boolean Query | TEXT_MATCH (<column_name>, 'Java AND c++') |
| Prefix Query | TEXT_MATCH (<column_name>, 'stream*') |
| Regex Query | TEXT_MATCH (<column_name>, '/Exception.*/') |
| Not Query | TEXT_MATCH (<column_name>, ': NOT c%') NOT TEXT_MATCH (<column_name>, 'c%') |
현재 제한 사항
Pinot는 다음 요구 사항으로 텍스트 검색을 지원해요.
- 컬럼 유형은 STRING이거나 STRING으로 저장되어야 함(예: JSON).
샘플 데이터셋
텍스트 검색은 각 컬럼 값이 상당히 큰 텍스트 blob이라 표준 필터 연산(EQUALITY, RANGE, BETWEEN)이 맞지 않는 STRING 컬럼에 이상적으로 사용돼요.
Apache Access Log
Apache access log의 다음 스니펫을 고려해 보세요. 로그의 각 줄은 임의 데이터(IP 주소, URL, 타임스탬프, 기호 등)로 구성되며 하나의 컬럼 값을 나타내요. 이런 데이터는 텍스트 검색을 하기 좋은 후보예요.
다음 데이터 스니펫이 Pinot 테이블의 ACCESS_LOG_COL 컬럼에 저장되어 있다고 가정해요.
109.169.248.247 - - [12/Dec/2015:18:25:11 +0100] "GET /administrator/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-
...
이 데이터에 대한 검색 쿼리 예시가 몇 가지 있어요.
GET 요청 수 세기.
SELECT COUNT(*)
FROM MyTable
WHERE TEXT_MATCH(ACCESS_LOG_COL, 'GET')
URL에 administrator가 있는 POST 요청 수 세기 (administrator/index)
SELECT COUNT(*)
FROM MyTable
WHERE TEXT_MATCH(ACCESS_LOG_COL, 'post AND administrator AND index')
특정 URL을 가지고 Firefox 브라우저로 처리된 POST 요청 수 세기
SELECT COUNT(*)
FROM MyTable
WHERE TEXT_MATCH(ACCESS_LOG_COL, 'post AND administrator AND index AND firefox')
이력서 텍스트 (Resume text)
이력서 텍스트를 사용한 또 다른 예시를 고려해 보세요. 이 파일의 각 줄은 서로 다른 후보자 이력서의 스킬 데이터를 나타내요.
이 데이터는 Pinot 테이블의 SKILLS_COL 컬럼에 저장돼요. 입력 텍스트의 각 줄은 하나의 컬럼 값을 나타내요.
Distributed systems, Java, C++, Go, distributed query engines for analytics and data warehouses, Machine learning, spark, Kubernetes, transaction processing
...
이 데이터에 대한 검색 쿼리 예시가 몇 가지 있어요.
"machine learning"과 "gpu processing"을 모두 가진 후보자 수 세기: 이것은 문구 검색(phrase search)이며 "machine learning"과 "gpu processing" 문구의 정확 일치를 찾는 것으로, 원본 데이터에서 반드시 같은 순서일 필요는 없어요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"Machine learning" AND "gpu processing"')
"distributed systems"와 'Java' 또는 'C++' 중 하나를 가진 후보자 수 세기: 이것은 정확 문구 "distributed systems"를 다른 용어와 함께 검색하는 조합이에요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"distributed systems" AND (Java C++)')
쿼리 로그
다음으로 데이터베이스가 처리한 SQL 쿼리를 포함하는 로그 파일의 스니펫을 고려해 보세요. 파일의 각 줄(쿼리)은 Pinot 테이블의 QUERY_LOG_COL 컬럼 값 하나를 나타내요.
SELECT count(dimensionCol2) FROM FOO WHERE dimensionCol1 = 18616904 AND timestamp BETWEEN 1560988800000 AND 1568764800000 GROUP BY dimensionCol3 TOP 2500
...
이 데이터에 대한 검색 쿼리 예시가 몇 가지 있어요.
GROUP BY가 있는 쿼리 수 세기
SELECT COUNT(*)
FROM MyTable
WHERE TEXT_MATCH(QUERY_LOG_COL, '"group by"')
SELECT count... 패턴이 있는 쿼리 수 세기
SELECT COUNT(*)
FROM MyTable
WHERE TEXT_MATCH(QUERY_LOG_COL, '"select count"')
timestamp 컬럼에 BETWEEN 필터와 함께 GROUP BY를 사용하는 쿼리 수 세기
SELECT COUNT(*)
FROM MyTable
WHERE TEXT_MATCH(QUERY_LOG_COL, '"timestamp between" AND "group by"')
각 쿼리 유형에 대한 구체적인 예시와 Pinot에서 텍스트 검색 쿼리를 작성하는 방법에 대한 단계별 가이드는 계속 읽어보세요.
Pinot의 컬럼은 dictionary-encoded 또는 RAW로 저장될 수 있어요. 또한 dictionary-encoded 컬럼에 inverted index 및/또는 sorted index를 만들 수 있어요.
텍스트 인덱스는 사용자가 Pinot에서 만들 수 있는 per-column 인덱스 유형에 추가되는 것이에요. 하지만 RAW 컬럼에서만 텍스트 인덱스를 지원하고 dictionary-encoded 컬럼에서는 지원하지 않아요.
다중 컬럼 텍스트 인덱스
다중 컬럼 텍스트 인덱스는 Pinot 1.4.0에서 도입됐어요.
1.4.0부터 Pinot는 두 가지 유형의 텍스트 인덱스를 제공해요.
- Per-column (single-column) text index — 각 인덱스 컬럼에 대해 별도의 Lucene 인덱스와 문서 ID 매핑을 저장. 1.4.0 이전에 사용 가능했던 원래 텍스트 인덱스 유형.
- Per-segment (multi-column) text index — 모든 인덱스 컬럼을 단일 문서 ID 매핑으로 단일 Lucene 인덱스에 함께 저장. 이것은 많은 작은 파일을 하나의 인덱스 디렉토리로 통합해 디스크 공간, 메모리(파일 핸들 포함), 인덱스 생성 시간을 줄여줌. 텍스트 인덱스 컬럼 수가 많을 때(수십~수백) 특히 유익함.
쿼리 시점에 다중 컬럼 텍스트 인덱스는 per-column 인덱스와 동일하게 동작해요. 동일한 TEXT_MATCH 함수와 동일한 검색 표현식 문법으로 쿼리해요. 유일한 차이는 인덱스가 테이블 구성에서 구성되는 방식이에요.
다중 컬럼 텍스트 인덱스를 언제 사용할까
다중 컬럼 텍스트 인덱스는 다음 경우에 적합해요.
- 텍스트 인덱싱이 필요한 컬럼이 많을 때(수십 또는 수백). 통합된 Lucene 디렉토리는 세그먼트당 열린 파일 수를 크게 줄여줌.
- 같은 쿼리에서 여러 텍스트 인덱스 컬럼을 자주 쿼리할 때. 다중 컬럼 인덱스는 단일 문서 ID 매핑을 공유하므로 컬럼 간 문서 ID 해석이 더 빠름.
- 텍스트 인덱스의 전반적 디스크 및 메모리 사용량을 줄이고자 할 때.
per-column과 multi-column 사이 선택
| Property \ Type | Per-Column | Per-segment (multi-column) |
|---|---|---|
| Querying speed | 더 느림 - 특히 여러 컬럼을 쿼리할 때 | 더 빠름 |
| Disk and memory usage | 더 높음 - 각 컬럼이 별도 Lucene 파일 세트와 문서 id 매핑 사용 | 더 낮음 - Lucene 파일 크기가 더 작음; 모든 컬럼에 문서 id 매핑 하나만 사용 |
| Initial build time | 더 높음 - 각 컬럼이 별도 Lucene 파일을 사용하기 때문 | 더 낮음 - 하나의 Lucene 파일 세트와 문서 id 매핑 하나가 생성됨 |
| Rebuild time | 더 낮음 - 영향을 받는 컬럼만 재구축하고 다른 인덱스는 복사됨 | 더 높음 - 모든 파일을 제거하고 처음부터 재구축 |
PR #16103의 벤치마크는 인덱스된 STRING 컬럼 50개에서 다중 컬럼 텍스트 인덱스가 동등한 per-column 인덱스보다 약 50% 적은 디스크 공간을 사용하고 약 30% 더 빠르게 구축되는 것을 보여줘요.
per-column 텍스트 인덱스 활성화
테이블 구성의 "fieldConfigList"라는 새 섹션을 추가해 컬럼에 텍스트 인덱스를 활성화하세요.
"fieldConfigList":[
{
"name":"text_col_1",
"encodingType":"RAW",
"indexTypes":["TEXT"]
},
{
"name":"text_col_2",
"encodingType":"RAW",
"indexTypes":["TEXT"]
}
]
텍스트 인덱스가 있는 각 컬럼은 tableIndexConfig에서 noDictionaryColumns로도 지정되어야 해요.
"tableIndexConfig": {
"noDictionaryColumns": [
"text_col_1",
"text_col_2"
]}
다음 시나리오에서 텍스트 인덱스를 구성할 수 있어요.
- 하나 이상의 컬럼에서 텍스트 인덱스가 활성화된 새 테이블 추가.
- 기존 테이블에 텍스트 인덱스가 활성화된 새 컬럼 추가.
- 기존 컬럼에 텍스트 인덱스 활성화.
텍스트 인덱스를 사용할 때 불필요한 저장 오버헤드를 줄이려면 인덱스된 컬럼을
noDictionaryColumns목록에 추가하세요.해당 구성 속성에 대한 지침은 Raw value forward index 문서를 참조하세요.
per-segment 텍스트 인덱스 활성화
per-column 텍스트 인덱스(즉 fieldConfigList에서 컬럼별로 구성됨)와 달리 per-segment 다중 컬럼 텍스트 인덱스는 tableIndexConfig에 multiColumnTextIndexConfig 요소를 추가해 한 번 구성돼요.
전체 구성 예시
{
"tableName": "myTable_OFFLINE",
"tableType": "OFFLINE",
"tableIndexConfig": {
"noDictionaryColumns": ["hobbies", "skills", "titles"],
"multiColumnTextIndexConfig": {
"columns": ["hobbies", "skills", "titles"],
"properties": {
"caseSensitive": "false",
"luceneUseCompoundFile": "false",
"luceneMaxBufferSizeMB": "500"
},
"perColumnProperties": {
"titles": {
"caseSensitive": "true"
},
"skills": {
"stopWordExclude": "it, those",
"enablePrefixSuffixMatchingInPhraseQueries": "true"
}
}
}
}
}
구성은 세 부분으로 나뉘어요.
| Field | Required | Description |
|---|---|---|
columns |
Yes | 다중 컬럼 텍스트 인덱스에 포함할 컬럼 이름 목록. |
properties |
No | 모든 컬럼에 적용되는 공유 속성. |
perColumnProperties |
No | 컬럼별 속성 재정의. 키는 컬럼 이름이고 값은 속성 맵. 특정 컬럼에 대해 properties의 일치 키를 재정의함. |
per-column 텍스트 인덱스와 마찬가지로 다중 컬럼 텍스트 인덱스의 컬럼도 불필요한 저장 오버헤드를 줄이기 위해
noDictionaryColumns에 나열해야 해요.
공유 속성 (properties)
공유 속성은 인덱스의 모든 컬럼에 적용돼요. 다음 키가 허용돼요.
| Key | Description |
|---|---|
enableQueryCacheForTextIndex |
Lucene 쿼리 결과 캐싱 활성화. |
luceneUseCompoundFile |
Lucene compound file 형식을 사용해 열린 파일 핸들 줄임. |
luceneMaxBufferSizeMB |
Lucene 인덱스 작성기의 최대 RAM 버퍼 크기(MB). |
reuseMutableIndex |
실시간 세그먼트 전체에서 변경 가능한 인덱스 재사용. |
또한 아래 per-column 속성에 나열된 모든 키도 공유 속성으로 유효해요(모든 컬럼의 기본값을 설정함).
Per-column 속성 (perColumnProperties)
Per-column 속성은 특정 컬럼에 대해 공유 속성을 재정의해요. 다음 키가 허용돼요.
| Key | Description |
|---|---|
useANDForMultiTermTextIndexQueries |
다중 용어 쿼리의 기본 연산자로 AND 사용(기본값은 OR). |
enablePrefixSuffixMatchingInPhraseQueries |
문구 쿼리 내 와일드카드 접두사/접미사 일치 허용. |
stopWordInclude |
인덱싱 및 검색 중 제외할 추가 중지 단어의 쉼표 구분 목록. |
stopWordExclude |
유지할 기본 중지 단어의 쉼표 구분 목록(제외 해제). |
caseSensitive |
텍스트 일치가 대소문자 구분인지 여부("true" 또는 "false"). |
luceneAnalyzerClass |
사용자 지정 Lucene 분석기의 정규화된 클래스 이름. |
luceneAnalyzerClassArgs |
사용자 지정 분석기 생성자의 인수. |
luceneAnalyzerClassArgTypes |
사용자 지정 분석기 생성자의 인수 유형. |
luceneQueryParserClass |
사용자 지정 Lucene 쿼리 파서의 정규화된 클래스 이름. |
luceneMaxBufferSizeMB처럼 공유 속성으로만 유효한 키는perColumnProperties에 넣으면 테이블 구성 검증 시점에 거부돼요.
검증 규칙
Pinot는 테이블 구성 생성 또는 업데이트 시 multiColumnTextIndexConfig를 검증해요.
columns는 최소 하나의 컬럼을 포함해야 함.columns는 중복을 포함할 수 없음.properties아래 키는 유효한 공유 속성 키여야 함.perColumnProperties아래 각 키는columns에 나열된 컬럼과 일치해야 함.perColumnProperties아래 각 속성은 위에 나열된 per-column 키 중 하나여야 함.
이러한 검사 중 하나라도 실패하면 Pinot는 잘못된 설정을 조용히 무시하는 대신 테이블 구성 변경을 거부해요.
쿼리 문법
per-column 텍스트 인덱스와 같은 TEXT_MATCH 함수로 다중 컬럼 텍스트 인덱스 컬럼을 쿼리해요. 각 TEXT_MATCH 호출은 단일 컬럼을 대상으로 해요.
SELECT COUNT(*)
FROM myTable
WHERE TEXT_MATCH(hobbies, 'painting')
AND TEXT_MATCH(skills, '"machine learning"')
모든 검색 표현식 유형(phrase, term, boolean, prefix, regex)은 per-column 텍스트 인덱스와 동일하게 동작해요.
제한 사항
- 다중 컬럼 텍스트 인덱스는
noRawDataForTextIndex와rawValueForTextIndex속성을 지원하지 않아요. 이 속성들은 raw forward index 데이터가 텍스트 인덱스와 함께 저장되는지 여부를 제어하며 per-column 텍스트 인덱스에만 적용돼요. - 다중 컬럼 텍스트 인덱스를 재구축해야 할 때(예: 인덱스에서 컬럼 추가/제거 후) 모든 컬럼이 처음부터 재구축돼요. Per-column 인덱스는 개별 컬럼을 독립적으로 재구축할 수 있어요.
- 테이블당
multiColumnTextIndexConfig는 하나만 존재할 수 있어요. 다중 컬럼 텍스트 인덱스를 공유해야 하는 모든 컬럼은 함께 나열되어야 해요. - 컬럼은 per-column 텍스트 인덱스 구성(
indexTypes: ["TEXT"]의fieldConfigList) 또는multiColumnTextIndexConfig에 하나만 나타날 수 있으며 둘 다는 안 돼요.
텍스트 인덱스 생성
테이블 구성을 통해 하나 이상의 컬럼에서 텍스트 인덱스가 활성화되면 세그먼트 생성 코드가 (컬럼별로) 텍스트 인덱스를 자동으로 생성해요.
텍스트 인덱스는 offline 및 real-time 세그먼트 모두 지원돼요.
텍스트 파싱 및 토큰화
원본 텍스트 문서(텍스트 인덱스가 활성화된 컬럼의 값)는 파싱되고 토큰화되며 개별 "인덱싱 가능한" 용어가 추출돼요. 이 용어들이 인덱스에 삽입돼요.
Pinot의 텍스트 인덱스는 Lucene 위에 구축돼요. Lucene의 표준 영어 텍스트 토크나이저는 대부분의 텍스트 클래스에서 일반적으로 잘 동작해요. 특정 사용자 요구 사항에 맞는 사용자 지정 텍스트 파서와 토크나이저를 구축하려면, 이를 컬럼별 텍스트 인덱스 기준으로 사용자가 지정할 수 있게 구성 가능하게 할 수 있어요.
Pinot 텍스트 인덱스에는 기본 "stop words" 세트가 내장돼 있어요. 이것은 검색 효율성과 인덱스 크기를 위해 제외되는 영어 고빈도 단어 세트로, 다음을 포함해요.
"a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it",
"no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "than", "there", "these",
"they", "this", "to", "was", "will", "with", "those"
이 단어들의 발생은 인덱스 생성 및 검색 중 토크나이저에 의해 무시돼요.
경우에 따라 사용자가 세트를 사용자 지정하고 싶을 수 있어요. 좋은 예는 텍스트의 IT(Information Technology)가 "it"와 충돌하거나 검색에서 정보가 없는 일부 문맥 특정 단어가 있을 때예요. 이를 위해 fieldConfig에서 기본 stop word에 포함/제외할 단어를 구성할 수 있어요.
"fieldConfigList":[
{
"name":"text_col_1",
"encodingType":"RAW",
"indexType":"TEXT",
"properties": {
"stopWordInclude": "incl1, incl2, incl3",
"stopWordExclude": "it"
}
}
]
단어는 쉼표로 구분되고 소문자여야 해요. 두 목록 모두에 나타나는 단어는 예상대로 제외돼요.
텍스트 검색 쿼리 작성
TEXT_MATCH 함수는 SQL에서 텍스트 검색을 사용할 수 있게 해요.
TEXT_MATCH(text_column_name, search_expression [, options])
text_column_name은 텍스트 인덱스가 있는 단일 값 컬럼이어야 함.search_expression은 Lucene 쿼리를 포함하는 단일 값 문자열 리터럴이어야 함.options는 TEXT_MATCH Query Options에 설명된 선택적 파서 옵션 문자열.
TEXT_MATCH는 BOOLEAN 값을 반환해요. WHERE에서 필터 조건자로 사용할 수 있고 다른 불리언 표현식처럼 프로젝션이나 정렬에도 사용할 수 있어요.
SELECT COUNT(*) FROM Foo WHERE TEXT_MATCH(text_col, 'pinot')
SELECT id, TEXT_MATCH(text_col, 'pinot') AS matched
FROM Foo
ORDER BY TEXT_MATCH(text_col, 'pinot') DESC, id
SELECT TEXT_MATCH(text_col, 'pinot') AS matched, COUNT(*)
FROM Foo
GROUP BY 1
일반적인 배치:
WHERESELECTCASE WHENORDER BYGROUP BY
WHERE에서 TEXT_MATCH를 사용할 때 다른 필터 연산자와 결합할 수 있어요. 예:
SELECT COUNT(*) FROM Foo WHERE TEXT_MATCH(...) AND some_other_column_1 > 20000
SELECT COUNT(*) FROM Foo WHERE TEXT_MATCH(...) AND some_other_column_1 > 20000 AND some_other_column_2 < 100000
여러 TEXT_MATCH 필터 절을 결합할 수 있어요.
SELECT COUNT(*) FROM Foo WHERE TEXT_MATCH(text_col_1, ....) AND TEXT_MATCH(text_col_2, ...)
실제로 Pinot는 selection, aggregation, aggregation-with-group-by 쿼리 전체에서 TEXT_MATCH를 지원해요. WHERE 밖에 나타날 때 Pinot는 각 행에 대해 표현식을 평가하고 불리언 결과를 반환해요.
검색 표현식(TEXT_MATCH 함수의 두 번째 인수)은 Pinot가 컬럼의 텍스트 인덱스에서 텍스트 검색을 수행하는 데 사용할 쿼리 문자열이에요.
TEXT_MATCH 쿼리 옵션
TEXT_MATCH 함수는 쿼리 시점에 Lucene 쿼리 파서 옵션을 지정하기 위해 선택적 세 번째 파라미터를 지원해요. 이는 테이블 구성을 변경하지 않고 유연하고 고급 텍스트 검색을 가능하게 해요.
함수 시그니처:
TEXT_MATCH(text_column_name, search_expression [, options])
text_column_name: 텍스트 검색을 수행할 컬럼 이름.search_expression: 텍스트 검색용 쿼리 문자열.options(선택): 쿼리 파싱 및 검색 동작을 제어하는 키-값 쌍의 쉼표 구분 문자열.
사용 가능한 옵션:
| Option | Values | Description |
|---|---|---|
parser |
CLASSIC, STANDARD, COMPLEX, MATCHPHRASE |
사용할 Lucene 쿼리 파서를 선택. 기본값은 CLASSIC. MATCHPHRASE는 마지막 용어의 접두사 일치도 지원하는 문구 지향 파서를 사용. |
allowLeadingWildcard |
true, false |
쿼리가 와일드카드(예: *term)로 시작하도록 허용. 기본값은 false. |
defaultOperator |
AND, OR |
다중 용어 쿼리의 기본 불리언 연산자 설정. 기본값은 OR. |
enablePrefixMatch |
true, false |
parser=MATCHPHRASE에서만 적용. true일 때 Pinot는 마지막 용어를 정확 일치 대신 접두사 일치로 처리. 기본값은 false. |
slop |
Non-negative integer | parser=MATCHPHRASE에서만 적용. 문구 용어가 몇 위치까지 떨어져 나타날 수 있는지 제어. 기본값은 0. |
inOrder |
true, false |
parser=MATCHPHRASE에서만 적용. 문구 용어가 쿼리 순서로 나타나야 하는지 제어. 기본값은 true. |
예시:
-- CLASSIC 파서와 선행 와일드카드 지원 사용
SELECT * FROM myTable WHERE TEXT_MATCH(myCol, '*search*', 'parser=CLASSIC, allowLeadingWildcard=true')
-- STANDARD 파서와 AND 연산자 사용
SELECT * FROM myTable WHERE TEXT_MATCH(myCol, 'term1 term2', 'parser=STANDARD, defaultOperator=AND')
-- 고급 쿼리용 COMPLEX 파서 사용
SELECT * FROM myTable WHERE TEXT_MATCH(myCol, 'complex query', 'parser=COMPLEX')
-- 정확 문구 일치용 MATCHPHRASE 사용
SELECT * FROM myTable WHERE TEXT_MATCH(myCol, 'realtime streaming system', 'parser=MATCHPHRASE')
-- 마지막 용어를 접두사로 동작하게 하고 한 위치 간격 허용
SELECT * FROM myTable
WHERE TEXT_MATCH(myCol, 'Tensor database', 'parser=MATCHPHRASE,enablePrefixMatch=true,slop=1')
문구 쿼리 (Phrase query)
이 쿼리는 주어진 문구의 정확 일치를 찾는 데 사용되며, 사용자가 지정한 문구의 용어가 원본 텍스트 문서에서 같은 순서로 나타나요.
다음 예시는 14개 문서를 포함한 이력서 텍스트 데이터의 이전 예시를 재사용해 쿼리를 살펴봐요. 이 문장에서 "document"는 컬럼 값을 의미해요. 데이터는 SKILLS_COL 컬럼에 저장되고 이 컬럼에 텍스트 인덱스를 만들었어요.
Java, C++, worked on open source projects, coursera machine learning
...
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 일치 문서가 문구 "Distributed systems"를 반드시 포함하는 문서를 찾아요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"Distributed systems"')
검색 표현식은 '"Distributed systems"'
- 검색 표현식은 항상 단일 따옴표 안에 지정됨 '
' - 문구 검색을 하므로 문구는 단일 따옴표 안의 이중 따옴표로 지정되고 이중 따옴표는 이스케이프되어야 함
- '"
"'
- '"
위 쿼리는 다음 문서와 일치해요.
Distributed systems, Java, C++, Go, distributed query engines for analytics and data warehouses, Machine learning, spark, Kubernetes, transaction processing
Distributed systems, database development, columnar query engine, database kernel, storage, indexing and transaction processing, building large scale systems
Distributed systems, Java, realtime streaming systems, Machine learning, spark, Kubernetes, distributed storage, concurrency, multi-threading
Distributed systems, Java, database engine, cluster management, docker image building and distribution
Distributed systems, Apache Kafka, publish-subscribe, building and deploying large scale production systems, concurrency, multi-threading, C++, CPU processing, Java
Databases, columnar query processing, Apache Arrow, distributed systems, Machine learning, cluster management, docker image building and distribution
하지만 다음 문서와는 일치하지 않아요.
Distributed data processing, systems design experience
문구 쿼리가 원본 문서에서 "그대로" 발생하는 문구를 찾기 때문이에요. 사용자가 문구로 지정한 용어는 문서가 일치로 간주되려면 원본 문서에서 정확히 같은 순서로 있어야 해요.
참고: 일치는 항상 대소문자를 구분하지 않는 방식으로 이루어져요.
다음 예시는 SKILLS_COL 컬럼을 쿼리해 각 일치 문서가 문구 "query processing"을 반드시 포함하는 문서를 찾아요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"query processing"')
위 쿼리는 다음 문서와 일치해요.
Apache spark, Java, C++, query processing, transaction processing, distributed storage, concurrency, multi-threading, apache airflow
Databases, columnar query processing, Apache Arrow, distributed systems, Machine learning, cluster management, docker image building and distribution"
용어 쿼리 (Term query)
용어 쿼리는 개별 용어를 검색하는 데 사용돼요.
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 일치 문서가 용어 'Java'를 반드시 포함하는 문서를 찾아요.
앞서 언급했듯이 검색 표현식은 항상 단일 따옴표 안에 있어요. 그러나 용어 쿼리이므로 단일 따옴표 안에 이중 따옴표를 사용할 필요 없어요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, 'Java')
불리언 연산자를 사용한 복합 쿼리
AND와 OR 불리언 연산자가 지원되며 복합 쿼리를 만드는 데 사용할 수 있어요. 불리언 연산자는 문구와 용어 쿼리를 임의 방식으로 결합하는 데 사용될 수 있어요.
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 일치 문서가 문구 "machine learning"과 "tensor flow"를 반드시 포함하는 문서를 찾아요. 이는 AND 불리언 연산자로 두 문구를 결합한 것이에요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"Machine learning" AND "Tensor Flow"')
위 쿼리는 다음 문서와 일치해요.
Machine learning, Tensor flow, Java, Stanford university,
C++, Python, Tensor flow, database kernel, storage, indexing and transaction processing, building large scale systems, Machine learning
CUDA, GPU processing, Tensor flow, Pandas, Python, Jupyter notebook, spark, Machine learning, building high performance scalable systems
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 문서가 문구 "machine learning"과 용어 'gpu', 'python'을 반드시 포함하는 문서를 찾아요. 이는 불리언 연산자로 문구와 두 용어를 결합한 것이에요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"Machine learning" AND gpu AND python')
위 쿼리는 다음 문서와 일치해요.
CUDA, GPU, Python, Machine learning, database kernel, storage, indexing and transaction processing, building large scale systems
CUDA, GPU processing, Tensor flow, Pandas, Python, Jupyter notebook, spark, Machine learning, building high performance scalable systems
불리언 연산자로 용어(들)와 문구(들) 또는 둘 다를 결합할 때 다음을 유의하세요.
- 일치 문서는 용어와 문구를 어떤 순서로든 포함할 수 있음.
- 일치 문서는 용어가 서로 인접하지 않을 수 있음(필요하면 적절한 문구 쿼리 사용).
OR 연산자 사용은 암시적이에요. 즉 검색 표현식에서 문구(들)와 용어(들)가 AND 연산자로 결합되지 않으면 기본적으로 OR 연산자가 사용돼요.
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 문서가 다음 중 하나라도 반드시 포함하는 문서를 찾아요.
- 문구 "distributed systems" OR
- 용어 'java' OR
- 용어 'C++'.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"distributed systems" Java C++')
괄호를 사용한 그룹화가 지원돼요.
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 문서가 반드시 포함하는 문서를 찾아요.
- 문구 "distributed systems" AND
- 용어 Java 또는 C++ 중 하나 이상
여기서 용어 Java와 C++는 연산자 없이 그룹화되어 OR 사용을 의미해요. 루트 연산자 AND는 문구 "distributed systems"와 결합하는 데 사용돼요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, '"distributed systems" AND (Java C++)')
접두사 쿼리 (Prefix query)
접두사 쿼리는 단일 용어의 맥락에서 수행할 수 있어요. 문구에는 접두사 일치를 사용할 수 없어요.
이 예시는 SKILLS_COL 컬럼을 쿼리해 각 문서가 stream, streaming, streams 같은 텍스트를 반드시 포함하는 문서를 찾아요.
SELECT SKILLS_COL
FROM MyTable
WHERE TEXT_MATCH(SKILLS_COL, 'stream*')
위 쿼리는 다음 문서와 일치해요.
Distributed systems, Java, realtime streaming systems, Machine learning, spark, Kubernetes, distributed storage, concurrency, multi-threading
Big data stream processing, Apache Flink, Apache Beam, database kernel, distributed query engines for analytics and data warehouses
Realtime stream processing, publish subscribe, columnar processing for data warehouses, concurrency, Java, multi-threading, C++,
C++, Java, Python, realtime streaming systems, Machine learning, spark, Kubernetes, transaction processing, distributed storage, concurrency, multi-threading, apache airflow
정규식 쿼리 (Regular Expression Query)
문구 및 용어 쿼리는 텍스트 인덱스에서 용어를 조회하는 기본 논리로 동작해요. 원본 텍스트 문서(텍스트 인덱스가 활성화된 컬럼의 값)는 파싱되고 토큰화되며 개별 "인덱싱 가능한" 용어가 추출돼요. 이 용어들이 인덱스에 삽입돼요.
원본 텍스트의 특성과 텍스트가 토큰으로 분할되는 방식에 따라 일부 용어가 개별적으로 인덱싱되지 않을 수 있어요. 그런 경우 텍스트 인덱스에 정규식 쿼리를 사용하는 것이 더 좋아요.
예외를 찾고자 하는 서버 로그를 예로 들어 보세요. 'exception'이 개별 인덱스 토큰으로 존재할 가능성이 낮으므로 regex 쿼리가 적합해요.
regex 쿼리의 문법은 앞서 언급한 쿼리와 약간 달라요. 정규식은 한 쌍의 슬래시(/) 사이에 작성돼요.
SELECT SKILLS_COL
FROM MyTable
WHERE text_match(SKILLS_COL, '/.*Exception/')
위 쿼리는 "exception"을 포함하는 모든 텍스트 문서와 일치해요.
와일드카드 용어 일치가 있는 문구 검색
와일드카드 및 접두사 용어 일치가 있는 문구 검색은 "pache pino"와 같은 패턴을 텍스트 "Apache Pinot"에 직접 일치시킬 수 있어요. 이 쿼리 유형은 사용자가 긴 텍스트에서 용어 경계를 가로질러 하위 문자열을 검색해야 하는 로그 검색 같은 사용 사례에서 매우 흔해요. (Lucene이 기본적으로 비용이 많이 드는 용어 일치를 피하기 위해 *로 시작하는 패턴을 허용하지 않으므로 비용이 더 들 수 있는) 이러한 검색을 활성화하려면 컬럼 텍스트 인덱스 구성에 새 구성 키를 추가할 수 있어요.
"fieldConfigList":[
{
"name":"text_col_1",
"encodingType":"RAW",
"indexType":"TEXT",
"properties": {
"enablePrefixSuffixMatchingInPhraseQueries": "true"
}
}
]
이 구성이 활성화되면 다음 문법처럼 phrase wildcard 검색을 수행할 수 있어요.
SELECT SKILLS_COL
FROM MyTable
WHERE text_match(SKILLS_COL, '*pache pino*')
SKILLS_COL에서 문자열 "Apache pinot"과 일치시켜요. 'pache pino AND apche luce' 같은 불리언 표현식도 지원돼요.
쿼리 유형 결정
불리언 연산자와 그룹화로 문구와 용어 쿼리를 결합하면 복잡한 텍스트 검색 쿼리 표현식을 만들 수 있어요.
기억할 핵심 사항은 문서에서 용어의 순서가 중요하고 최종 사용자 관점에서 문구를 개별 용어로 분리하는 것이 말이 안 될 때 문구를 사용해야 한다는 것이에요.
예는 문구 "machine learning"이에요.
TEXT_MATCH(column, '"machine learning"')
그러나 Java와 C++ 용어와 일치하는 문서를 검색한다면 문구 쿼리 "Java C++"를 사용하면 이제 사용자가 이력서 텍스트에서 이러한 스킬을 정확히 같은 순서(서로 인접하게)로 지정한다는 것에 의존하므로 부분 결과(비어 있을 수도 있음)가 됩니다.
TEXT_MATCH(column, '"Java C++"')
불리언 AND 연산자를 사용한 용어 쿼리가 그러한 경우에 더 적합해요.
TEXT_MATCH(column, 'Java AND C++')
텍스트 인덱스 튜닝
Lucene 인덱스 생성 시간을 개선하기 위해 일부 구성이 제공됐어요. Field Config 속성 luceneUseCompoundFile과 luceneMaxBufferSizeMB는 더 빠른 인덱스 쓰기를 제공하지만 파일 디스크립터 및/또는 메모리 압력을 증가시킬 수 있어요.
텍스트 검색용 클러스터 구성
텍스트 검색 쿼리에 너무 많은 용어나 절이 포함되면 Lucene이 TooManyClauses 예외를 던져 쿼리 실패를 일으킬 수 있어요. 이는 일반적으로 다음에서 발생해요.
- OR 조건이 많은 복잡한 불리언 쿼리
- 많은 용어로 확장되는 와일드카드 쿼리
- 많은 수의 검색 용어를 가진 쿼리
이런 경우를 처리하려면 클러스터 수준에서 최대 절 수를 늘릴 수 있어요. pinot.lucene.max.clause.count 설정에 대한 cluster configuration reference를 참조하세요.
구성 파라미터
텍스트 인덱스는 fieldConfigList의 indexes.text 객체에 다음 구성 파라미터를 지원해요.
| Parameter | Default | Description |
|---|---|---|
fst |
- | 사용할 FST 유형: LUCENE 또는 NATIVE |
rawValue |
- | 원시 텍스트 값을 저장할지 여부 |
queryCache |
false | Lucene 쿼리 결과 캐시 활성화 |
useANDForMultiTermQueries |
false | 다중 용어 쿼리에 AND 사용 (OR 대신) |
stopWordsInclude |
[] | 포함할 추가 중지 단어 |
stopWordsExclude |
[] | 제외할 기본 중지 단어 |
luceneUseCompoundFile |
true | Lucene compound file 형식 사용 |
luceneMaxBufferSizeMB |
500 | Lucene 인덱스 작성기의 최대 RAM 버퍼 크기 |
luceneAnalyzerClass |
StandardAnalyzer | 사용자 지정 Lucene 분석기 클래스 이름 |
luceneAnalyzerClassArgs |
- | 사용자 지정 분석기 생성자의 인수 |
luceneAnalyzerClassArgTypes |
- | 사용자 지정 분석기 생성자의 인수 유형 |
luceneQueryParserClass |
QueryParser | 사용자 지정 Lucene 쿼리 파서 클래스 이름 |
enablePrefixSuffixMatchingInPhraseQueries |
false | 문구 쿼리에서 접두사/접미사 일치 활성화 |
reuseMutableIndex |
false | 실시간 세그먼트 전체에서 변경 가능한 인덱스 재사용 |
luceneNRTCachingDirectoryMaxBufferSizeMB |
0 | NRT 캐싱 디렉토리의 최대 버퍼 크기 (0 = 비활성) |
useLogByteSizeMergePolicy |
false | Lucene 세그먼트에 log-byte-size 병합 정책 사용 |
docIdTranslatorMode |
Default | Doc ID 변환기 모드: Default, TryOptimize, 또는 Skip |
caseSensitive |
false | 텍스트 인덱스가 대소문자 구분인지 여부 |
storeInSegmentFile |
false | 텍스트 인덱스를 세그먼트 파일 안에 저장 |
사용자 지정 파라미터를 사용한 예시
{
"fieldConfigList": [
{
"name": "logLine",
"encodingType": "RAW",
"indexes": {
"text": {
"luceneAnalyzerClass": "org.apache.lucene.analysis.standard.StandardAnalyzer",
"luceneMaxBufferSizeMB": 200,
"useANDForMultiTermQueries": true,
"stopWordsInclude": ["the", "a", "an"],
"caseSensitive": false,
"queryCache": true
}
}
}
]
}