학습일지/ElasticSearch

[ElasticSearch] ES 맛보기 - 역인덱스(Inverted Index)

Merge Log 2025. 9. 27. 16:20

우리가 쿠팡이나 쇼핑몰에서 검색을 통해 원하는 물건을 찾는다

이때 우리는 문자 순서에 상관없이 비슷한 물건을 조회할 수 있다

  • ex) "맥북 에어 13" / "맥북 13 에어"

어떻게 이 검색이 가능할까? ElasticSearch 에서는 역인덱스를 통해 관련된 것을 찾아서 조회하게 된다

참고로 이런 작동 구조는 데이터 타입이 text 타입에 한해서만 적용된다

역인덱스 (Inverted Index)

  • 필드 값을 단어마다 쪼개서 찾기 쉽게 정리해놓은 목록
  • 예시를 통해서 알아보자
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"
}

위와 같이 데이터를 저장한다고 하자


특정 기준을 통해 단어를 추출

"name": "Apple 2025 맥북 에어 13 M4 10코어"
[Apple, 2025, 맥북, 에어, 13, M4, 10코어]


"name": "Apple 2024 에어팟 4세대"
[Apple, 2024, 에어팟, 4세대]


"name": "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]
  • 필드값에서 추출되어 역인덱스에 저장된 단어를 보고 토큰(token) 이라고 부른다
  • 각 토큰에 대해 어디에 토큰이 포함되어 있는지를 도큐먼트 ID 로 저장
    • "Apple" 의 경우 1, 2, 3 도큐먼트 모두 등장
  • 생성된 역인덱스를 직접 눈으로 확인할 수는 없다, 시스템 내부적으로 생성될 뿐 이다

검색을 하면 역인덱스를 활용

만약 "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]
  • 각 토큰이 어디 도큐먼트와 많이 일치하는지 역인덱스를 통해서 찾는다
  • 종합적으로 ID 가 3인 도큐먼트가 가장 많이 일치하기 때문에 맨 처음으로 조회된다
    • ID 가 1인 도큐먼트는 단어 1개 일치 → "Apple"
    • ID 가 2인 도큐먼트는 단어 2개 일치 → "Apple", "2024"
    • ID 가 3인 도큐먼트는 단어 3개 일치 → "Apple", "2024", "아이패드"

이와 같은 ES 의 자체적인 로직으로 score(점수)를 매겨 score 가 높은 순으로 도큐먼트를 조회한다
일반적으로 검색어와 관련성이 높으면 높을수록 score가 높게 측정된다

대략적인 score 계산

  • Term Frequency (TF)
    • 문서 내에서 검색어가 얼마나 자주 등장하는가
    • 많이 등장할수록 점수가 높음
  • Inverse Document Frequency (IDF)
    • 검색어가 전체 문서 중 얼마나 희귀하냐
    • 희귀할수록 점수가 높음 / 흔한 단어일수록 점수가 낮음
  • Field Length Normalization
    • 문서(필드)가 짧을수록 점수가 높다
    • 검색어가 짧은 문서에서 등장하면 더 관련성이 높다고 판단

애널라이저(Analyzer)

  • 필드값을 저장할 때 토큰으로 분리후 역인덱스로 저장하는 것을 살펴보았다
  • 이 과정에서 문자열(text)을 토큰으로 변환시켜주는 장치를 보고 애널라이저라고 부른다

  • 문자열을 간단하게 단어 단위로만 자르는 게 아니라 "애널라이저"가 여러가지의 작업을 거쳐서 토큰을 만들어낸다
  • 내부적으로 "캐릭터 필터(character filter)", "토크나이저(tokenizer)", "토큰 필터(token filter)" 라는 걸 활용해 문자열을 토큰으로 변형시킨다

캐릭터 필터(character filter)

  • 문자열을 토큰으로 자르기 전에 문자열을 다듬는 역할을 한다
  • 다양한 종류의 필터가 존재, 여러 개의 필터를 적용시킬 수 있다
  • ex)
    • html_strip 필터 적용 (HTML 태그 제거) / "<h1>아이폰 15</h1>" → "아이폰 15"
    • 그외에도 mapping 필터, pattern replace 필터 등이 존재

토크나이저(tokenizer)

  • 문자열을 토큰으로 자르는 역할
  • ex)
    • standard 토크나이저 (공백 또는 ,/./!/? 와 같은 문장 부호를 기준으로 자름)
      • 그외에도 classic 토크나이저, keyword 토크나이저, pattern 토크나이저 등등

토큰 필터(token filter)

  • 잘린 토큰을 최종적으로 다듬는 역할
  • ex)
    • lowercase 필터 적용 (소문자로 변환)
    • stop 필터 적용 (a/the/is 와 같은 특별한 의미를 가지 않는 단어 제거)
    • stemmer 필터 적용 (단어의 원래 형태로 변환)
      • foxes → fox / jumped → jump
  • 사실 위의 역직렬화 예시 표는 잘못된 예시이다. 기본적인 애널라이저를 사용한다면 standard analyzer 를 사용하게 되며 `lowercase` 라는 character filter 가 있기 때문에 소문자로 변환해서 토큰을 저장한다
  • 아래는 실제 analyzer 분석

  • 하지만 "apple" 로 저장되어도 "Apple" 도 검색이 된다 이유가 뭘까?
  • 도큐먼트를 생성할 때 Analyzer 가 문자열을 토큰으로 분리해 역인덱스를 생성한다. 그런데 검색을 할 때도 Analyzer 가 검색어로 입력한 문자열을 토큰으로 분리해 검색한다

  • 위와 같이 도큐먼트 생성과 검색은 같은 애널라이저 과정을 거치게 된다

한글과 영어를 섞은 단어 Analyzer

  • 한글을 의미있게 토큰으로 만드는 Plugin 인 Nori Aanlyzer 를 붙인 후 테스트 해본 결과 한글에 알맞게 잘 적용된다는 것을 알게되었다
  • 그러나 한글과 영어를 섞은 단어를 알맞게 역인덱스 저장하는 방식은 어떻게 해야할까?
GET /_analyze
{
    "text": "오늘 영어 책에서 'It depends on the results.'이라는 문구를 봤다.",
    "char_filter": [],
    "tokenizer": "nori_tokenizer",
    "filter": ["nori_part_of_speech", "nori_readingform", "lowercase", "stop", "stemmer"]
}
  • nori_tokenizer 를 통해서 한글을 알맞게 토크나이저 처리했다
    • 기본은 standard
  • 이후 lowercase 로 소문자 처리
  • stop 을 통해 의미없는 영어 제거 (a, the 등)
  • stemmer 를 통해 depends 라는 영단어를 → depend 로 원본 영단어로 변경