ETC/Elasticsearch

[elasticsearch] 색인 -2

seokhyun2 2025. 2. 2. 22:51

0. 개요

오늘은 색인 중에서도 텍스트에 대해서 좀 더 자세하게 알아보도록 하겠습니다.

보통 DB를 사용하면 Like 검색을 주로 하는데요. Like 검색은 테이블의 전체 row를 탐색하기 때문에 데이터가 많아지면 많아질수록 기하급수적으로 늘어나는 것을 겪어보셨을거에요.

물론 mysql에서도 fulltext 검색을 위한 index가 존재합니다.
바로 ngram parser인데요. ngram parser는 https://seokhyun2.tistory.com/93 글에서 좀 더 자세하게 확인해보실 수 있습니다.

ngram parser를 사용하더라도, 텍스트 검색에 한계를 느끼게 되면 검색엔진을 도입하게 되는 경우가 많습니다.

그래서 오늘은 텍스트의 색인에 대해 좀 더 자세하게 알아보겠습니다.

먼저 텍스트 검색에 대한 역색인 구조는 https://seokhyun2.tistory.com/110 글에서 설명했었습니다.

역색인 구조에 대해서는 위 글을 참조하시기 바랍니다.

1. keyword vs text

elasticsearch에서는 텍스트를 색인할 수 있는 타입으로 keyword와 text로 2가지가 있습니다.

이 두가지 타입에 대한 차이는 매우 중요하므로 꼭 확인하고 가셔야 합니다.

먼저 keyword 타입은 말 그대로 키워드로 자르지 않고 통째로 저장됩니다.

예를 들면, keyword 타입으로 'elasticsearch study'라고 작성해서 색인을 하게 되면, 'elasticsearch study'가 하나의 키워드로 동작하고 인식이 되어 필터처럼 정확하게 일치하는 지는 검색할 수 있지만 'elasticsearch'만 검색하거나 'study'만 검색해서는 조회할 수 없는 형태가 됩니다.

반면에 text 타입은 분석기를 적용할 수 있고 분석기에 따라서 나뉘어져 색인이 됩니다.

default로는 elasticsearch에서 제공하는 standard analyzer가 있는데, 각 언어의 문법 기준으로 잘라주는 알고리즘을 채택하고 있다고 합니다. 기본적으로는 띄어쓰기 기준으로 대강 자른다고 생각하셔도 되는데 해당 알고리즘에 대해서 더 자세하게 확인하시려면 아래의 링크에서 확인해보시기 바랍니다.

https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html

그래서 text타입으로 'elasticsearch study'를 색인하게 되면 자동으로 'elasticsearch', 'study'가 잘라져서 색인이 되어 한 단어만 입력해도 검색을 할 수 있는 구성이 됩니다.

한번 실제로 확인을 해보도록 하겠습니다.

먼저 아래의 명령어로 first_name은 keyword 타입으로 full_name은 text 타입으로 지정하여 user라는 인덱스를 생성해보겠습니다.

full_name에 text 타입으로 지정하면서 analyzer는 따로 지정하지 않았기에 default analyzer가 적용됩니다.

curl --location --request PUT 'http://localhost:9200/user' \
--header 'Content-Type: application/json' \
--data '{
    "settings": {
        "refresh_interval": "1s"
    },
    "mappings": {
        "properties": {
            "first_name": {
                "type": "keyword"
            },
            "full_name": {
                "type": "text"
            }
        }
    }
}'

그 다음에는 아래의 명령어로 데이터를 색인해보겠습니다.

curl --location --request PUT 'http://localhost:9200/user/_doc/1' \
--header 'Content-Type: application/json' \
--data '{
    "first_name": "john",
    "full_name": "john legend"
}'
curl --location --request PUT 'http://localhost:9200/user/_doc/2' \
--header 'Content-Type: application/json' \
--data '{
    "first_name": "john",
    "full_name": "john wick"
}'

 

여기서 검색을 하려면 keyword 타입으로 생성한 first_name은 완전히 일치하게 검색해야 된다고 했는데, 그런 경우에는 아래와 같이 bool 쿼리를 활용하면 검색이 됩니다. 아래의 쿼리로 검색을 하게되면 2개의 데이터 모두 first_name이 john이라서 2개의 데이터가 검색이 됩니다.

curl --location 'http://localhost:9200/user/_search' \
--header 'Content-Type: application/json' \
--data '{
    "query": {
        "bool": {
            "filter": {
                "term" : { "first_name" : "john" }
            }
        }
    }
}'

text 타입으로 색인된 full_name에는 match 쿼리를 사용할 수 있는데요. match 쿼리는 검색하려는 텍스트에 대해서도 analyzer가 적용이 된다는 점을 알아두셔야 합니다. match 쿼리는 아래와 같이 사용할 수 있습니다.

curl --location 'http://localhost:9200/user/_search' \
--header 'Content-Type: application/json' \
--data '{
  "query": {
    "match": {
      "full_name": {
        "query": "john legend"
      }
    }
  }
}'

이렇게 쿼리를 날리면 'john legend'라고 검색을 했는데도 2개가 다 반환이 되는 걸 확인하실 수 있으실텐데요.

match 쿼리를 사용하면 'john'과 'legend'로 나뉘어져 검색이 되어 'john'이 있는지 'legend'가 있는지 확인한 후에 하나라도 존재하면 결과를 반환하기 때문입니다. 이 때는 반환되는 결과에 각각 score를 확인해보시면 john legend가 john wick보다 score가 더 높은 것을 확인하실 수 있는데요. 하나만 있어도 검색이 되지만 어쨌든 score를 통해 어떤게 더 정확한 지 알려준다고 생각하시면 됩니다.

2. Nori analyzer 

한국어를 사용하게 되면 default analyzer를 사용하게 되면 띄어쓰기 기준으로만 나뉘어지게 됩니다.

'오늘은 일요일입니다'라고 작성하면 '오늘은', '일요일입니다' 라고만 나뉘어지는 것인데요.

'오늘' 이라는 키워드만 사용하기 위해서는 형태소 분석이 가능해야 합니다.

elasticsearch에서는 plugin을 제공하고 있는데 그 중에 한국어 형태소 분석기 plugin도 제공을 하고 있습니다.

해당 plugin이 nori analyzer라고 하는 것인데요.

https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-nori.html

 

Korean (nori) analysis plugin | Elasticsearch Plugins and Integrations [8.17] | Elastic

Korean (nori) analysis plugin edit The Korean (nori) Analysis plugin integrates Lucene nori analysis module into elasticsearch. It uses the mecab-ko-dic dictionary to perform morphological analysis of Korean texts. This plugin can be installed using the pl

www.elastic.co

nori analyzer를 사용하게 되면, 자동으로 형태소 분석을 해주기 때문에 한국어 검색을 쉽게 적용해보실 수 있습니다.

plugin은 먼저 설치를 해주어야 합니다. elasticsearch 내부에서 아래의 명령어로 간편하게 설치할 수 있습니다.

참고로 동작 중인 elasticsearch에 설치를 했다면 재시작을 해주어야 사용할 수 있다는 것을 꼭 확인하셔야 합니다.

bin/elasticsearch-plugin install analysis-nori

이렇게 설치를 하면, 인덱스를 생성할 때 아래와 같이 parameter를 입력하면 nori analyzer가 적용됩니다.

curl --location --request PUT 'http://localhost:9200/document' \
--header 'Content-Type: application/json' \
--data '{
    "settings": {
        "refresh_interval": "1s",
        "analysis": {
            "anlayzer": {
                "nori": {
                    "tokenizer": "nori_tokenizer"
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "content": {
                "type": "text",
                "analyzer": "nori"
            }
        }
    }
}
'

그러면 아래와 같이 문서를 하나 색인해주고 아래의 match 쿼리를 사용해보면 '오늘'이라고만 검색해도 검색이 잘 되는 것을 확인해보실 수 있습니다.

curl --location --request PUT 'http://localhost:9200/document/_doc/1' \
--header 'Content-Type: application/json' \
--data '{
    "content": "오늘은 일요일입니다."
}'
curl --location --request GET 'http://localhost:9200/document/_search' \
--header 'Content-Type: application/json' \
--data '{
    "query": {
        "match": {
            "content": {
                "query": "오늘"
            }
        }
    }
}'

3. 마무리

오늘은 텍스트를 어떻게 색인하고 검색하는지 알아보았습니다.

텍스트 검색은 검색의 꽃이라고도 할 수 있는데요.

mysql 같은 RDB로는 텍스트 검색이 매우 어렵지만 elasticsearch를 활용하면 쉽게 형태소 분석까지 적용된 검색을 빠르게 제공할 수 있기 때문에 한번 공부해보시면 좋을 것 같습니다.

'ETC > Elasticsearch' 카테고리의 다른 글

[elasticsearch] 텍스트 검색  (0) 2025.03.16
[elasticsearch] 노드 관리 (Node Lifecycle)  (0) 2025.03.02
[elasticsearch] 색인 -1  (0) 2025.01.19
[elasticsearch] 클러스터  (0) 2024.11.10
[elasticsearch] 데이터 저장  (1) 2024.10.27