ElasticSearch - 기본 검색 기능 및 작동 원리에 대한 이해
- 역인덱스는 필드 값을 단어마다 쪼개서 찾기 쉽게 정리해놓은 목록이다.
POST /products/_doc
{
"name" : "Apple 2025 맥북 에어 13 M4 10코어"
}
- 이 문서가 색인되면
name 필드는 다음과 같은 형태로 분리되어 저장된다.
| 토큰 |
문서 ID |
| apple |
1 |
| 2025 |
1 |
| 맥북 |
1 |
| 에어 |
1 |
| 13 |
1 |
| m4 |
1 |
| 10코어 |
1 |
- 이렇게 토큰 → 문서 ID 방향으로 뒤집어 정리한 목록을 역인덱스라고 한다.
- RDB의
LIKE '%맥북%'이 모든 행을 훑는 것과 달리, 역인덱스는 맥북 항목만 찾으면 해당 문서를 바로 얻는다.
-
products 인덱스의 name 필드값을 토큰으로 분리해 역인덱스로 저장하는 과정을 거친다.
- 이 때, 문자열을 토큰으로 변환시켜주는 장치를 애널라이저(Analyzer)라고 한다.
POST /products/_doc
{
"name" : "Apple 2025 맥북 에어 13 M4 10코어"
}
-
캐릭터 필터 : 문자열을 토큰으로 자르기 전에 문자열을 다듬는 역할을 수행한다.(0개 이상)
-
토크나이저 : 문자열을 토큰으로 자르는 역할을 수행한다.(정확히 1개, 필수)
-
토큰 필터 : 잘린 토큰을 최종적으로 다듬는 역할을 수행한다.(0개 이상)
- 순서는 항상
캐릭터 필터 → 토크나이저 → 토큰 필터이며, 각 단계는 앞 단계의 출력을 입력으로 받는다.
- 애널라이저 설정은 인덱스를 만들지 않고도
_analyze API로 바로 테스트할 수 있다.
- 인덱스를 만들어놓고 결과를 확인하는 것보다 훨씬 빠르므로, 설정을 확정하기 전에 이 API로 검증하는 습관을 들이는 것이 좋다.
POST /_analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Apple 2025 맥북 에어"
}
ElasticSearch에 기본값으로 설정되어 있는 애널라이저(Analyzer)
- 별도 설정을 하지 않으면
text 타입 필드에는 Standard Analyzer가 적용된다.
- 구성은
캐릭터 필터 없음 + standard 토크나이저 + lowercase 토큰 필터이다.
- 아래 케이스에서
Apple이 apple로 바뀐 것은 lowercase 토큰 필터가 적용되었기 때문이다.
- 토큰 타입(
<ALPHANUM>, <NUM>, <HANGUL>)은 토크나이저가 문자 종류를 구분해 붙여준 값이다.
$ GET /_analyze
{
"text" : "Apple 2025 맥북 에어",
"analyzer" : "standard"
}
// Result
{
"tokens": [
{
"token": "apple",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "2025",
"start_offset": 6,
"end_offset": 10,
"type": "<NUM>",
"position": 1
},
{
"token": "맥북",
"start_offset": 11,
"end_offset": 13,
"type": "<HANGUL>",
"position": 2
},
{
"token": "에어",
"start_offset": 14,
"end_offset": 16,
"type": "<HANGUL>",
"position": 3
}
]
}
- 기본 애널라이저로 부족할 때 캐릭터 필터, 토크나이저, 토큰 필터를 조합해 직접 정의할 수 있다.
PUT /products_custom
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"html_strip"
],
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
}
}
-
주의 :
analysis 설정은 인덱스 생성 시점에만 지정할 수 있다. 이미 존재하는 인덱스에 PUT을 다시 보내면 resource_already_exists_exception이 발생한다.
- 기존 인덱스의 애널라이저를 바꾸려면 인덱스를 닫고(
_close) 설정을 변경하거나, 새 인덱스를 만들어 _reindex 해야 한다.
- 정의한 애널라이저는 다음과 같이 확인한다.
POST /products_custom/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "<p>Apple 맥북</p>"
}
HTML strip character filter
- HTML 태그를 제거한 뒤 토크나이저로 넘긴다.
-
<p>Apple <b>맥북</b></p> → Apple 맥북
- 토크나이저가
keyword이므로 문자열 전체가 토큰 하나로 남는다는 점에 유의한다.
PUT /html_strip_test
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "keyword",
"char_filter": [
"html_strip"
]
}
}
}
}
}
- 검색에 의미가 없는 단어를 제거한다.
-
필터를 정의하는 것만으로는 아무 일도 일어나지 않는다. 반드시 애널라이저의
filter 배열에 등록해야 적용된다.
PUT /stop_test
{
"settings": {
"analysis": {
"filter": {
"my_stop_filter": {
"type": "stop",
"stopwords": [
"in",
"the",
"days"
]
}
},
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"my_stop_filter"
]
}
}
}
}
}
Stemmer(어간 추출) Token Filter
- 시제나 활용형이 달라도 같은 어간으로 정규화해 검색되도록 한다.
-
running runs → run, run
-
한글에는 동작하지 않는다. 기본
stemmer는 영어(english)를 대상으로 하므로 달리기, 달렸다는 그대로 남는다.
- 한글 활용형 처리는 아래 형태소 분석기 항목을 참고한다.
PUT /stemmer_test
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "whitespace",
"filter": [ "stemmer" ]
}
}
}
}
}
Synonym(동의어) Token Filter
- 서로 다른 단어를 같은 의미로 묶어 검색되도록 한다.
-
synonym은 내장 필터 이름이 아니다. filter 배열에 "synonym"이라고만 적으면 다음 오류가 발생한다.
- 반드시
filter 블록에서 type: synonym으로 정의하고 동의어 목록을 지정한 뒤, 그 이름을 애널라이저에 등록해야 한다.
-
맥북 노트북 → 맥북, macbook, 노트북, 랩탑
- 동의어가 많아지면
synonyms 대신 synonyms_path로 파일을 참조한다.
PUT /synonym_test
{
"settings": {
"analysis": {
"filter": {
"my_synonym_filter": {
"type": "synonym",
"synonyms": [
"맥북, macbook",
"노트북, 랩탑"
]
}
},
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"my_synonym_filter"
]
}
}
}
}
}
- 애널라이저는 색인할 때와 검색할 때 각각 적용되며, 기본적으로 같은 것이 쓰인다.
- 둘을 다르게 지정하려면 매핑에서
analyzer와 search_analyzer를 나눠 설정한다.
- 동의어 필터는 보통 검색 시점에만 적용한다. 색인 시점에 적용하면 동의어 목록이 바뀔 때마다 전체 문서를 다시 색인해야 하기 때문이다.
PUT /products_search
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "standard",
"search_analyzer": "my_analyzer"
}
}
}
}
- Standard Analyzer는 공백 기준으로만 한글을 나누므로, 붙여 쓴 단어를 쪼개지 못한다.
- 결과는
맥북에어 토큰 하나뿐이다. 따라서 맥북으로 검색해도 이 문서는 검색되지 않는다.
- 한글을 제대로 다루려면 형태소 분석기인 nori 플러그인이 필요하다.
POST /_analyze
{
"analyzer": "standard",
"text": "맥북에어"
}