전문 검색(Full-Text Search) 색인 정의를 위한 스키마
전문 검색은 완전, 혹은 부분 일치만 허용하는 필드 검색과 달리 정의된 쿼리에 맞는 검색 후보를 점수화하여 최적의 결괏값을 제공합니다. 이를 이용하여 토큰의 유사도에 기반하여 검색하거나 벡터 검색을 통해 의미상 유사성에 기반하여 검색할 수 있습니다.
전문 검색 색인은 하나 이상 생성이 가능합니다. 정의된 필드와 옵션에 따라 색인 생성 비용과 저장 용량이 증가할 수 있습니다.
다음은 전문 검색 색인 생성을 위한 스키마의 예시입니다. 아래의 예시는 문서의 아이디를 의미하는 doc_id
와 문서의 본문인 content
, 그리고 본문의 임베딩 데이터인 embedding
필드로 구성되어 있습니다.
{
"name": "sk_fts",
"fields": [
"doc_id",
"content",
"embedding"
],
"unique": False,
"index_type": "kFullTextSearchIndex",
"options": {
"doc_id": {
"analyzer": {
"type": "KeywordAnalyzer"
},
"index_options": "doc_freqs"
},
"content": {
"analyzer": {
"type": "StandardAnalyzer"
},
"index_options": "offsets"
},
"embedding": {
"analyzer": {
"type": "DenseVectorAnalyzer",
"options": {
"index_type": "HNSW",
"dims": 768,
"m": 64,
"ef_construction": 200,
"ef_search": 32,
"metric": "inner_product",
"normalize": True,
"shards": 1
}
},
"index_options": "doc_freqs"
}
}
}
전문 검색은 다음과 같이 정의합니다.
name
: 색인명fields
: 전문 검색에 사용할 필드의 목록index_type
:kFullTextSearchIndex
를 입력options
: 전문 검색에 사용할analyzer
,tokenizer
등을 정의
options은 다음과 같이 구성됩니다.
fields
에서 명세한 필드명analyzer
: 명세한 필드를 분석할 analyzer를 선택index_options
: 색인 옵션
{
"doc_id": {
"index_options": "doc_freqs",
"analyzer": {
"type": "KeywordAnalyzer"
}
}
},
Nested Fields
만약 입력된 필드에 계층적인 데이터가 포함되어 있다면 이 데이터는 별도의 데이터베이스 정규화 과정을 거치지 않고 입력과 색인 생성이 가능합니다.
예를 들어 다음과 같이 attribute
필드에 author
와 price
로 구성된 계층 구조가 포함되어 있으며 별도의 처리 없이 입력이 가능합니다.
data = [
{"doc_id": "1", "title": "Mastering Machine Learning", "attribute": {"author": "finn", "price": 21000}},
{"doc_id": "2", "title": "Database System", "attribute": {"author": "jeapil", "price": 33000}},
]
doc_db.insert(collection_name, data)
)
이렇게 입력된 데이터는 다음과 같이 attribute.author
의 형식으로 계층 구조에 접근할 수 있고 색인을 지정하여 빠르게 검색할 수 있습니다.
{
"name": "sk_fts",
"fields": [
"doc_id",
"title",
"attribute.author"
"attribute.price"
],
"unique": False,
"index_type": "kFullTextSearchIndex",
"options": {
"doc_id": {
"analyzer": {
"type": "KeywordAnalyzer"
},
"index_options": "doc_freqs"
},
"title": {
"analyzer": {
"type": "StandardAnalyzer"
},
"index_options": "offsets"
},
"attribute.author": {
"analyzer": {
"type": "KeywordAnalyzer"
},
"index_options": "doc_freqs"
},
"attribute.author": {
"analyzer": {
"type": "Int64Analyzer"
},
"index_options": "doc_freqs"
}
}
}
Index Options
색인 옵션에 따라 각 필드에 대한 점수 계산 방식이 달라집니다. 색인 옵션에서 선택 가능한 항목은 다음과 같습니다.
doc_freqs
: 문서에서 출현한 빈도수만 사용, 점수 계산이 제외됨term_freqs
: tokenizer에 의해 분해된 토큰의 빈도수를 사용positions
: 토큰의 빈도수와 토큰의 위치를 함께 고려함. 근접 혹은 구문 검색에 활용offsets
: 문서 빈도, 토큰 빈도, 위치, 시작과 끝 문자들이 위치 값을 함께 저장, 하이라이트 기능의 속도 개선을 위해 사용
Analyzer
Analyzer는 입력된 텍스트를 분석하는 역할을 합니다. 일반적으로 문자를 정규화하고 색인을 위한 토큰화 그리고 불용어 제거를 위한 토큰 필터 등의 작업을 조합하여 수행합니다.
Analyzer는 다음 하위 모듈이 통합되어 동작합니다.
- Character Filter: 문자 변환, 정규화
- Tokenizer: 토큰 분리
- Token Filter: 토큰 변환, 정규화
각각의 analyzer는 다음과 같은 별명을 가지고 있습니다.
항목 | 별명 |
---|---|
CustomAnalyzer | custom |
KeywordAnalyzer | keyword |
StandardAnalyzer | standard |
StandardCJKAnalyzer | standard_cjk |
DenseVectorAnalyzer | dense_vector |
RegexAnalyzer | regex |
DateTimeAnalyzer | datetime |
BooleanAnalyzer | boolean |
Int64Analyzer | int64 |
Float64Analyzer | float64 |
따라서 다음 예시는
{
"type": "KeywordAnalyzer"
}
다음 코드와 동일한 의미를 가집니다.
{
"type": "keyword"
}
CustomAnalyzer
Tokenizer, Character Filter, Token Filter를 원하는 형식으로 구성할 수 있는 분석기 입니다.
options | 내용 | 타입 |
---|---|---|
char_filters | Character Filter | string | object[] |
tokenizer | Tokenizer | string | object[] |
token_filters | Token Filter | string | object[] |
다음은 옵션 정의 예시입니다.
{
"type": "CustomAnalyzer",
"options": {
"char_filters": [
"LowerCaseCharacterFilter"
],
"tokenizer": "ICUWordTokenizer",
"token_filters": [
"NormalizationTokenFilter",
{
"type": "SnowballTokenFilter",
"options": {
"language": "english"
}
}
]
}
}
KeywordAnalyzer
전달된 문자열을 하나의 토큰으로 다룹니다. 이 분석기는 분류코드, 카테고리 등을 텍스트를 알고 있고 전체 텍스트를 입력하여 검색하고자 할 때 적합합니다.
options | 내용 | 타입 |
---|---|---|
case_insensitive | 대소문자 구별하지 않음 | boolean |
{
"type": "KeywordAnalyzer"
"options": {
"case_insensitive": True
}
}
StandardAnalyzer
단어 단위로 검색할 때 적합한 분석기입니다. 대소문자 필터가 적용되어 대소문자 구별없이 검색이 가능합니다.
options | 내용 | 타입 |
---|---|---|
char_filters | Character Filter | string | object[] |
tokenizer | Tokenizer | string | object[] |
stopwords_filter | StopWordsTokenFilter | dict |
snowball_filter | SnowballTokenFilter | dict |
아래는 CustomAnalyzer에 대응하는 분석기 옵션의 기본값입니다.
{
"type": "CustomAnalyzer"
"options": {
"char_filters": [
"LowerCaseCharacterFilter", "NormalizationCharacterFilter"
],
"tokenizer": "ICUWordTokenizer",
"token_filters": [
{
"type": "StopWordsTokenFilter",
"options": {
"language": "english"
}
},
{
"type": "SnowballTokenFilter",
"options": {
"language": "english"
}
}
]
}
}
stopwords_filter
, snowball_filter
는 다음과 같은 형식으로 활성화 할 수 있습니다.
{
"options": {
"stopwords_filter": {
"enabled": True
},
"snowball_filter": {
"enabled": True
}
}
}
StandardCJKAnalyzer
형태소 혹은 n-gram 단위로 검색할 때 적합한 분석기입니다. 기본적으로 StandardAnalyzer와 분석기 옵션의 기본 동작이 동일합니다. 하지만 한국어와 같은 굴절어를 위해 NGramTokenFilter와 ByteLengthTokenFilter를 선택하여 구성할 수 있습니다.
options | 내용 | 타입 |
---|---|---|
char_filters | Character Filter | string | object[] |
tokenizer | Tokenizer | string | object[] |
ngram_filter | NGramTokenFilter | dict |
ngram_offsets | n-gram의 offset 사용 여부 | bool |
stopwords_filter | StopWordsTokenFilter | dict |
snowball_filter | SnowballTokenFilter | dict |
byte_length_filter | ByteLengthTokenFilter | dict |
다음은 ngram_filter
와 byte_length_filter
를 정의한 예시입니다.
{
"type": "StandardCJKAnalyzer"
"options": {
"ngram_filter": {
"min_size": 1,
"max_size": 6
},
"byte_length_filter": {
"min_length": 2,
"max_length": 0
}
}
}
DenseVectorAnalyzer
벡터검색을 위한 색인입니다. 여기서 필수값은 차원인 dims
이며 이는 임베딩 모델이 출력하는 차원과 일치해야 합니다. 여기에 m
, ef_construction
, ef_search
등을 조절하여 검색 시간과 성능을 조율할 수 있습니다.
다음은 옵션 정의 예시입니다.
{
"type": "DenseVectorAnalyzer",
"options": {
"index_type": "HNSW",
"dims": 768,
"m": 64,
"ef_construction": 200,
"ef_search": 32,
"metric": "inner_product",
"normalize": True,
"shards": 1
}
}
options
에서 선택할 수 있는 항목은 다음과 같습니다.
options | 내용 | 가능한 입력값 | 타입 | 기본값 |
---|---|---|---|---|
index_type | 색인 종류 | HNSW : 그래프 기반 색인IVF_HNSW : 역색인이 포함된 그래프 기반 색인 | str | HNSW |
dims | 차원수(필수 입력) | int | ||
m | 이웃 노드 수 | int | 32 | |
metric | 거리 함수 | inner_product : 내적L2 : 유클리드 | str | inner_product |
normalize | 정규화 여부 | bool | False | |
shards | 샤드 크기 | int | 1 | |
ef_construction | 색인에 사용되는 동적 큐의 크기 | int | 40 | |
ef_search | 검색에 사용되는 동적 큐의 크기 | int | 16 | |
num_inv_lists | 역색인의 수, IVF_HNSW의 옵션 | int | 65536 | |
num_bits | 해싱된 백터의 정밀도, IVF_HNSW의 옵션 | int | 8 | |
num_probe | 검색할 역색인의 수, IVF_HNSW의 옵션 | int | 10 | |
quantizer | 양자화 비트수 | flat , 4bit , 6bit , 8bit , 8bit_direct , 16bit | str | 8bit |
encoding | 인코딩 데이터 타입 | 0 : float161 : float32 | int | 1 |
training_set_size | 색인 클러스터 학습에 사용되는 샘플 수 | int | 10000 |
quantizer
가 활성화 되는 경우 m
은 dims % m == 0
조건을 만족해야 합니다.
RegexAnalyzer
기본적인 구성은 StandardAnalyzer와 같습니다. 이 분석기는 tokenizer가 RegexTokenizer로 선택되어 정규식으로 이용한 토큰 분리가 가능합니다.
options | 내용 | 타입 |
---|---|---|
patterns | 토큰 분리를 위한 정규식 | string |
trim | 분리된 토큰에서 제거할 문자 | string |
다음은 옵션 정의 예시입니다.
{
"type": "RegexAnalyzer",
"options": {
"tokenizer": {
"type": "RegexTokenizer",
"options": {
"patterns": [
"(\\b|\\$)\\d+(,\\d+)*[\\-\\w%]*",
"[a-zA-Zㄱ-ㅎ가-힣][&0-9a-zA-Zㄱ-ㅎ가-힣]*"
],
"trim": ",- "
}
}
}
}
DateTimeAnalyzer
ISO 8601 (opens in a new tab) 형식의 문자열을 UNIX time stamp 형식으로 변환합니다.
BooleanAnalyzer
문자열을 bool 타입으로 변환합니다.
Int64Analyzer
문자열을 int64 타입으로 변환합니다.
Float64Analyzer
문자열을 float64 타입으로 변환합니다.
Tokenizer
tokenizer는 입력된 문자열을 토큰으로 분리하여 반환하는 모듈입니다.
각각의 tokenizer는 다음과 같은 별명을 가지고 있습니다.
항목 | 별명 |
---|---|
KeywordTokenizer | keyword |
WhitespaceTokenizer | whitespace |
ICUWordTokenizer | icu |
DelimiterTokenizer | delimiter |
DateTimeTokenizer | datetime |
NGramTokenizer | ngram |
MeCabTokenizer | mecab |
RegexTokenizer | regex |
PathHierarchyTokenizer | path_hierarchy |
ReversePathHierarchyTokenizer | reverse_path_hierarchy |
KeywordTokenizer
입력된 문자열을 하나의 토큰으로 변환합니다.
WhitespaceTokenizer
입력된 문자열을 공백 단위로 분리된 토큰을 반환합니다.
ICUWordTokenizer
Unicode 문자가 고려된 단어 단위로 분리된 토큰을 반환합니다.
DelimiterTokenizer
delimiter
에 정의된 문자를 기준으로 토큰이 분할됩니다.
다음과 같은 Tokenizer 정의가 있다면
{
"type": "DelimiterTokenizer",
"options": {
"delimiter": ", "
}
}
아래와 같은 문자열은
Aeca API, Documents
다음과 같이 분해됩니다.
["Aeca", "API", "Documents"]
DateTimeTokenizer
ISO 8601 (opens in a new tab) 형식의 문자열을 UNIX time stamp 형식으로 변환된 토큰을 반환합니다.
NGramTokenizer
입력된 문자열을 n-gram (opens in a new tab)으로 분리합니다. 한국어와 같은 굴절어를 형태소 분석기를 사용하지 않고 검색할 때 유용합니다.
다음과 같은 tokenizer 정의가 있다면
{
"type": "NGramTokenizer",
"options": {
"min_size": 2,
"max_size": 4
}
}
아래와 같은 문자열은
코그니카
다음과 같이 분해됩니다.
["코그", "그니", "니카", "코그니", "그니카", "코그니카"]
옵션에 따라 토큰의 수가 크게 늘어날 수 있습니다.
MeCabTokenizer
MeCab 기반의 형태소 분석기를 사용하여 분리된 형태소를 토큰으로 반환합니다. 현재는 한국어 (opens in a new tab)만 지원하고 있습니다.
RegexTokenizer
정규식을 이용하여 토큰을 분리합니다.
다음은 옵션 정의 예시입니다.
{
"type": "RegexTokenizer",
"options": {
"patterns": [
"(\\b|\\$)\\d+(,\\d+)*[\\-\\w%]*",
"[a-zA-Zㄱ-ㅎ가-힣][&0-9a-zA-Zㄱ-ㅎ가-힣]*"
],
"trim": ",- "
}
}
사용 가능한 옵션은 다음과 같습니다.
patterns
: 분리할 토큰의 패턴trim
: 분리된 토큰에서 제거할 문자
PathHierarchyTokenizer
파일 경로와 같이 계층적인 구조를 가진 문자열을 delimiter
를 기준으로 분리된 토큰을 반환합니다.
{
"type": "PathHierarchyTokenizer",
"options": {
"delimiter": "/"
}
}
아래와 같은 문자열은
a/b/c
다음과 같이 분해됩니다.
["a", "a/b", "a/b/c"]
ReversePathHierarchyTokenizer
기본적인 동작은 PathHierarchyTokenizer와 유사하지만 반환하는 계층의 순서는 반대로 동작합니다.
{
"type": "ReversePathHierarchyTokenizer",
"options": {
"delimiter": "/"
}
}
아래와 같은 문자열은
a/b/c
다음과 같이 분해됩니다.
["a/b/c", "a/b", "a"]
Character Filter
일반적으로 Character filter는 입력된 문자열을 정규화 하기 위한 목적으로 사용합니다.
각각의 Character filter는 다음과 같은 별명을 가지고 있습니다.
항목 | 별명 |
---|---|
LowerCaseCharacterFilter | lower_case |
UpperCaseCharacterFilter | upper_case |
NormalizationCharacterFilter | normalization |
LowerCaseCharacterFilter
입력된 문자열을 소문자로 변환합니다.
UpperCaseCharacterFilter
입력된 문자열을 대문자로 변환합니다.
NormalizationCharacterFilter
입력된 문자열을 ICU transforms (opens in a new tab) 룰에 따라 정규화 합니다.
변환을 위한 기본룰은 다음과 같습니다.
NFD; [:Mn:] Remove; NFC
Token Filter
토큰 필터는 분리된 토큰을 변환하거나 제거하는 기능을 가지고 있습니다.
각각의 token filter는 다음과 같은 별명을 가지고 있습니다.
항목 | 별명 |
---|---|
LowerCaseTokenFilter | lower_case |
UpperCaseTokenFilter | upper_case |
NormalizationTokenFilter | normalization |
ASCIIFoldingTokenFilter | ascii_folding |
CharLengthTokenFilter | char_length |
ByteLengthTokenFilter | byte_length |
NGramTokenFilter | ngram |
EdgeNGramTokenFilter | edge_ngram |
StopWordsTokenFilter | stopwords |
DoubleMetaphoneTokenFilter | double_metaphone |
ShingleTokenFilter | shingle |
SnowballTokenFilter | snowball |
WordDelimiterTokenFilter | word_delimiter |
LowerCaseTokenFilter
입력된 토큰을 소문자로 변환합니다.
UpperCaseTokenFilter
입력된 토큰을 대문자로 변환합니다.
NormalizationTokenFilter
입력된 토큰을 ICU transforms (opens in a new tab) 룰에 따라 정규화 합니다.
변환을 위한 기본룰은 다음과 같습니다.
NFD; [:Mn:] Remove; NFC
ASCIIFoldingTokenFilter
라틴 계열의 문자를 ASCII에서 표현할 수 있는 문자로 변환합니다. 예를 들어 Café
를 Cafe
로 변홥합니다.
CharLengthTokenFilter
토큰의 문자 길이를 기준으로 토큰을 제외합니다.
다음과 같이 정의 했을 때 토큰은 최소 2자 이상 최대 4자까지 허용됩니다.
{
"type": "CharLengthTokenFilter",
"options": {
"min_length": 2,
"max_length": 4,
}
}
아래와 같은 토큰이 존재할 때
["다람쥐", "헌", "쳇바퀴에", "타고파"]
다음과 같이 출력 됩니다.
["다람쥐", "쳇바퀴에", "타고파"]
ByteLengthTokenFilter
동작은 CharLengthTokenFilter와 동일 하지만 토큰의 문자열 길이가 아닌 바이트 단위로 계산합니다.
NGramTokenFilter
입력된 토큰을 n-gram (opens in a new tab)으로 분리합니다.
다음과 같은 정의가 있다면
{
"type": "NGramTokenFilter",
"options": {
"min_size": 2,
"max_size": 4,
}
}
아래와 같은 토큰은
["코그니카"]
다음과 같이 분해됩니다.
["코그", "그니", "니카", "코그니", "그니카", "코그니카"]
옵션에 따라 토큰의 수가 크게 늘어날 수 있습니다.
EdgeNGramTokenFilter
NGramTokenFilter와 유사하게 입력된 토큰을 n-gram (opens in a new tab)으로 분리합니다. 하지만 토큰 시작 위치부터 지정된 길이까지의 n-gram만 생성합니다.
다음과 같은 정의가 있다면
{
"type": "EdgeNGramTokenFilter",
"options": {
"min_size": "2",
"max_size": "3",
}
}
아래와 같은 토큰은
["코그니카", "데이터베이스는"]
다음과 같이 분해됩니다.
["코그", "코그니", "데이", "데이터"]
StopWordsTokenFilter
불용어에 해당되는 토큰을 제거합니다.
다음은 옵션 정의 예시입니다.
{
"type": "StopWordsTokenFilter",
"options": {
"language": "english"
}
}
사용 가능한 옵션은 다음과 같습니다.
language
: 언어english
: 영어
DoubleMetaphoneTokenFilter
Double Metaphone (opens in a new tab) 방법에 따라 입력된 토큰을 음소 유사성 가진 토큰으로 정규화합니다.
ShingleTokenFilter
n-gram (opens in a new tab) 형식으로 인접한 토큰을 병합하여 새로운 토큰을 추가합니다.
예를 들어 다음과 같이 옵션을 정의하고
{
"type": "ShingleTokenFilter",
"options": {
"min_size": 2,
"max_size": 3,
"output_unigrams": True,
"seperator": " ",
}
}
다음과 같이 입력된 토큰은
["애플", "컴퓨터", "신규", "제품"]
다음과 같이 변환 됩니다.
[
"애플", "애플 컴퓨터", "애플 컴퓨터 신규",
"컴퓨터", "컴퓨터 신규", "컴퓨터 신규 제품",
"신규", "신규 제품",
"제품"
]
사용 가능한 옵션은 다음과 같습니다.
min_size
: n-gram 최소 크기max_size
: n-gram 최대 크기output_unigrams
: unigram 출력 여부seperator
: 두 토큰을 병합할 때 입력할 구분자
SnowballTokenFilter
Snowball stemmer (opens in a new tab)를 사용하여 어간으로 변환합니다.
다음은 옵션 정의 예시입니다.
{
"type": "SnowballTokenFilter",
"options": {
"language": "english"
}
}
사용 가능한 옵션은 다음과 같습니다.
language
: 언어danish
dutch
english
finnish
french
german
hungarian
italian
norwegian
portuguese
romanian
russian
spanish
swedish