
본 포스팅은 인프런 JSCODE 박재성님의 실전에서 바로 써먹는 Elasticsearch 입문 (검색 최적화편)
강의를 들은 후 해당 강의를 참고하여 작성되었습니다.
https://inf.run/767Nk
MySQL에 맥북 에어 13 M4 라고 저장하고 아래와 같이 검색하면 결과가 나오지 않음.
SELECT * FROM products WHERE name like "%맥북 13 에어%"
* 가장 기본적인 standard Tokenizer를 기준으로 정리하였음
필드 값을 단어를 단위로 분리하여 찾기 쉽게 정리해놓은 목록
ElasticSearch가 document를 저장하고 검색하는 방식은 아래와 같다.
POST /products/_create/1
{
"name" : "Apple 2025 맥북 에어 13 M4 10코어"
}
POST /products/_create/2
{
"name" : "Apple 2024 에어팟 4세대"
}
POST /products/_create/3
{
"name" : "Apple 2024 아이패드 mini A17 Pro"
}
"Apple 2025 맥북 에어 13 M4 10코어"
-> [apple, 2025, 맥북, 에어, 13, m4, 10코어]
"Apple 2024 에어팟 4세대"
-> [apple, 2024, 에어팟, 4세대]
"Apple 2024 아이패드 mini A17 Pro"
-> [apple, 2024, 아이패드, mini, a17, pro]
| 토큰(token) | 도큐먼트 id |
|---|---|
| apple | [1, 2, 3] |
| 2025 | [1] |
| 맥북 | [1] |
| 에어 | [1] |
| 13 | [1] |
| m4 | [1] |
| 10코어 | [1] |
| 2024 | [2, 3] |
| 에어팟 | [2] |
| 4세대 | [2] |
| 아이패드 | [3] |
| mini | [3] |
| a17 | [3] |
| pro | [3] |
GET /products/_search
{
"query" : {
"match": {
"name" : "Apple 2024 아이패드"
}
}
}
| 토큰(token) | 도큐먼트 id |
|---|---|
| Apple | [1, 2, 3] |
| 2025 | [1] |
| 맥북 | [1] |
| 에어 | [1] |
| 13 | [1] |
| M4 | [1] |
| 10코어 | [1] |
| 2024 | [2, 3] |
| 에어팟 | [2] |
| 4세대 | [2] |
| 아이패드 | [3] |
| mini | [3] |
| A17 | [3] |
| Pro | [3] |
BM25 알고리즘을 기반으로 scoring을 통해 score가 높은 순서대로 document를 응답한다.
"Apple 2024 아이패드" 로 검색시 모든 단어가 매칭되는 3번 document가 가장 높은 우선순위, Apple만 매치되는 1번 document가 가장 낮은 우선순위로 응답된다.
document의 필드값을 token으로 분리해 역인덱스로 저장하는 과정에서
문자열을 token으로 변환하는 장치를 Analyzer(아날라이저)라고 한다

아날라이저는 내부적으로 charcter filter, tokenizer, token filter 를 이용해 문자열을 토큰으로 변환한다.
또한 사용자가 해당 필드를 검색할때도 사용자가 입력한 text를 token으로 분리하여 매칭되는 도큐먼트를 찾는다.
각각의 기능은 다음과 같다.
주된 사용 용례는 아래와 같다.
- html_strip
검색시에는 불필요한 html 태그를 제거한다.
"<\h1>The Brwon Foxes jumped over the roof<\h1>"
-> "The Brwon Foxes jumped over the roof"
주된 사용 용례는 아래와 같다.
- standard (standard Analyzer에서 사용)
공백 혹은,,.,!,?,"등 문장 부호를 기준으로 자른다.
"The Brwon Foxes jumped over the roof"
-> [The, Brown, Foxes, jumped, over, the, roof]
주된 사용 용례는 아래와 같다.
- lowercase (standard Analyzer에서 사용)
문자를 소문자로 변환한다.
[The, Brown, Foxes, jumped, over, the, roof]
-> [the, brown, foxes, jumped, over, the, roof]- stemmer
단어의 형태를 기본형으로 변환한다.
[the, brown, foxes, jumped, over, the, roof]
-> [the, brown, fox, jump, over, the, roof]- stop
a, the, is 등 특별한 의미를 가지지 않는 단어를 제거한다.
[the, brown, fox, jump, over, the, roof]
-> [brown, fox, jump, roof]- mapping
검색시 동의어나 유의어 처리를 위해 특정 문자를 치환한다.
[삼성, 노트북]
-> [삼성, samsung, 노트북, notebook]
document에 Samsung Notebook으로 저장을 하더라도, 삼성 노트북 으로도 검색되기를 희망하는 경우
Custom Analyzer를 활용해 매핑을 정의할 수 있다.
내용은 예시 3번을 참조.
h1 태그 삭제 및 소문자로 변경
PUT /boards
{
"settings":{
"analysis": {
"analyzer": {
"boards_content_analyzer": {
"char_filter": ["html_strip"], # html_strip
"tokenizer": "standard",
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"content" :{
"type" : "text",
"analyzer": "boards_content_analyzer" # 정의한 analyzer를 입력
}
}
}
}
---
# /_analze 엔드포인트를 통해 토큰이 어떻게 생성되는지 확인
GET /boards/_analyze
{
"field": "content",
"text": "<h1>Running cats</h1>"
}
---
# 결과 - h1 태그가 삭제 및 소문자로 변경되어있음
{
"tokens": [
{
"token": "running",
"start_offset": 4,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "cats",
"start_offset": 12,
"end_offset": 16,
"type": "<ALPHANUM>",
"position": 1
}
]
}
단어를 기본형으로 변경 및 소문자로 변경
PUT /boards
{
"settings":{
"analysis": {
"analyzer": {
"boards_content_analyzer": {
"char_filter": [],
"tokenizer": "standard",
"filter": ["lowercase", "stemmer"] # stemmer 사용
}
}
}
},
"mappings": {
"properties": {
"content" :{
"type" : "text",
"analyzer": "boards_content_analyzer" # 정의한 analyzer를 입력
}
}
}
}
---
# /_analze 엔드포인트를 통해 토큰이 어떻게 생성되는지 확인
GET /boards/_analyze
{
"field": "content",
"text": "Running cats, jumpping!"
}
---
# 결과 - 기본형으로 변경 및 소문자로 변경되어있음
{
"tokens": [
{
"token": "run",
"start_offset": 0,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "cat",
"start_offset": 8,
"end_offset": 12,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "jump",
"start_offset": 14,
"end_offset": 22,
"type": "<ALPHANUM>",
"position": 2
}
]
}
사용자가 입력한 단어 대신 사전에 정의된 단어로 토큰 생성
PUT /products
{
"settings": {
"analysis": {
# 이곳의 필터에 적용 -- start
"filter": {
"products_synonym_filter": {
"type": "synonym",
"synonyms": [
"notebook, 노트북, 랩탑, 휴대용 컴퓨터, laptop",
"samsung, 삼성"
]
}
},
# 이곳의 필터에 적용 -- end
"analyzer": {
"products_name_analyzer": {
"char_filter": [],
"tokenizer": "standard",
"filter": [
"lowercase",
"products_synonym_filter" # 생성한 fitler 기입
]
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "products_name_analyzer" # 생성한 analyzer 입력
}
}
}
}
---
# /_analze 엔드포인트를 통해 토큰이 어떻게 생성되는지 확인
GET /products/_analyze
{
"field": "name",
"text": "Samsung Notebook"
}
---
# 결과 - samsung, notebook 대신 다양한 단어로 토큰 생성
{
"tokens": [
{
"token": "samsung",
"start_offset": 0,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "삼성",
"start_offset": 0,
"end_offset": 7,
"type": "SYNONYM",
"position": 0
},
{
"token": "notebook",
"start_offset": 8,
"end_offset": 16,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "노트북",
"start_offset": 8,
"end_offset": 16,
"type": "SYNONYM",
"position": 1
},
{
"token": "랩탑",
"start_offset": 8,
"end_offset": 16,
"type": "SYNONYM",
"position": 1
},
{
"token": "휴대용",
"start_offset": 8,
"end_offset": 16,
"type": "SYNONYM",
"position": 1
},
{
"token": "laptop",
"start_offset": 8,
"end_offset": 16,
"type": "SYNONYM",
"position": 1
},
{
"token": "컴퓨터",
"start_offset": 8,
"end_offset": 16,
"type": "SYNONYM",
"position": 2
}
]
}