본문 바로가기

Elasticsearch

[Elasticsearch] nori, ngram tokenizer, shingle filter 사용하기 실습

반응형

 

Elasticsearch  

NGram, Shingle, Nori Tokenizer (decompound_mode: discard & mixed )

차이 알아보기 실습 

 

 

 

1. test_search라는 인덱스 settings - ngram, shingle, nori 사용하고 싶은대로 설정하기 

PUT test_search
{
    "settings": {
      "index": {
      "analysis": {
        "filter": {
          "shingle_max_3": {
            "type": "shingle",
            "max_shingle_size": 3,
            "token_seperator": ""
          }
        },
        "analyzer": {
          "nori_analyzer_discard": {
            "tokenizer": "nori_t_discard"
          },
          "nori_analyzer_mixed": {
            "tokenizer": "nori_t_mixed"
          },
          "nori_analyzer_discard_shingle_max_3": {
            "filter": "shingle_max_3",
            "tokenizer": "nori_t_discard"
          },
          "nori_analyzer_mixed_shingle_max_3": {
            "filter": "shingle_max_3",
            "tokenizer": "nori_t_mixed"
          },
          "ngram_analyzer_min_1_max_2": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer_min_1_max_2"
          },
          "ngram_analyzer_min_2_max_3": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer_min_2_max_3"
          },
          "ngram_analyzer_shingle_min_1_max_2": {
            "type": "custom",
            "filter": "shingle_max_3",
            "tokenizer": "ngram_tokenizer_min_1_max_2"
          },
          "ngram_analyzer_shingle_min_2_max_3": {
            "type": "custom",
            "filter": "shingle_max_3",
            "tokenizer": "ngram_tokenizer_min_2_max_3"
          }
        },
        "tokenizer": {
          "nori_t_discard": {
            "type": "nori_tokenizer",
            "decompound_mode": "discard"
          },
          "nori_t_mixed": {
            "type": "nori_tokenizer",
            "decompound_mode": "mixed"
          },
          "ngram_tokenizer_min_1_max_2": {
            "type": "ngram",
            "min_gram": 1,
            "max_gram": 2,
            "token_chars": [
              "letter",
              "digit"
              ]
          },
        "ngram_tokenizer_min_2_max_3": {
            "type": "ngram",
            "min_gram": 2,
            "max_gram": 3,
            "token_chars": [
              "letter",
              "digit"
              ]
          }
        }
      }
      }
    },
    "mappings": {
      "properties": {
        "field1": {
          "type": "text"
        },
        "field2": {
          "type":"text",
          "analyzer": "nori_analyzer_discard"
        },
        "field3": {
          "type":"text",
          "analyzer": "nori_analyzer_mixed"
        },
        "field4": {
          "type": "text",
          "analyzer": "nori_analyzer_discard_shingle_max_3"
        },
        "field5": {
          "type": "text",
          "analyzer": "nori_analyzer_mixed_shingle_max_3"
        },
        "field6": {
          "type": "text",
          "analyzer": "ngram_analyzer_min_1_max_2"
        },
        "field7": {
          "type": "text",
          "analyzer": "ngram_analyzer_min_2_max_3"
        },
        "field8": {
          "type": "text",
          "analyzer": "ngram_analyzer_shingle_min_2_max_3"
        },
        "field9": {
          "type": "text",
          "analyzer": "ngram_analyzer_shingle_min_2_max_3"
        }
      }
    }
}

 

 

 

 

2. _analyze 로 각각의 analyzer 결과 확인해보기 

 

1. 아무 analyzer 적용되지 않은 경우 

GET test_search/_analyze
{
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산
위에
저 
소나무
철갑을
두른

바람
서리
불변함은
우리
기상일세

: 띄어쓰기만으로 분리되었다.

 

get으로 검색해보기 

먼저 field1 (아무것도 적용되지 않은) 에 해당 내용을 insert 

POST test_search/_doc
{
  "field1": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}

GET으로 검색

GET test_search/_search 
{
  "query": {
    "term": {
      "field1": "기상일세"
    }
  }
}

위에 나온 모든 검색어로 가능하다. 그런데 남산 하고 " "띄어쓰기를 붙이면 안나온다. 또는 "남산 위에" 처럼 붙여 써도 안나온다. 기상일세 는 나오지만 (위에 있음) "기상" 하나만으로는 안나온다. 그런데 위에서 분석된 "저", "듯" 으로 검색 가능하다. 

 

결과

{
  "took" : 11,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "test_search",
        "_type" : "_doc",
        "_id" : "Zo_eBIEBC4xAaHMXhvzC",
        "_score" : 0.2876821,
        "_source" : {
          "field1" : "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
        }
      }
    ]
  }
}

 

 

 

 

2. nori - decompound : discard 적용

GET test_search/_analyze
{
  "analyzer": "nori_analyzer_discard", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산




나무
철갑

두르


바람
서리
불변



우리
기상

ㄹ세

: 저 자음들은 오타가 아니다 

띄어쓰기 뿐만 아니라 더욱 세분화되어 분리된 느낌이다. 어미(?) 도 분리되는 것 같다. 

 

get으로 검색해보기 (field2에 POST / GET 코드는 위와 같음)

 

검색 안된 경우 

기상일세, 남산 위에, ㄴ, ㅁ, ㄹ세 

 위에 분석되어진 걸로 검색이 되지만 자음 자체를 포함하고 있는걸로는 검색이 되지 않았다. 

그리고 "기상일세", "남산 위에" 처럼 붙여진 것이 안나온다. 이래서 nori와 shingle을 함께 쓰는 건가 보다! 

 

 

 

 

3. nori - decompound : mixed 적용

GET test_search/_analyze
{
  "analyzer": "nori_analyzer_mixed", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산



소나무

나무
철갑

두른
두르


바람
서리
불변




우리
기상
일세

ㄹ세

: decompound - mixed 로 했더니 discard되기 전과 후 모두 저장되었다. 

 

GET 검색해보기 

위의 결과들과 같다. 위에 분석된 단어로 검색하면 나오고, 위에 없는 단어로 검색하면 안나온다. 

또 자음 또는 자음이 포함된 단어로 검색이 안된다. ex "ㄴ", "ㅁ", "ㄹ세"

"" 빈 문자열로도 검색이 안된다. 

 

 

 

4. nori - decompound : discard + filter : shingle (max 3) 적용

GET test_search/_analyze
{
  "analyzer": "nori_analyzer_discard_shingle_max_3", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산
남산 위
남산 위 에

위 에
위 에 저 

에 저
에 저 소
저 
저  소
저 소 나무

소 나무
소 나무 철갑
나무
나무 철갑
나무 철갑 을
철갑
철갑 을
철갑 을 두르 

을 두르
을 두르 ㄴ
두르
두르  ㄴ
두르  ㄴ 듯

ㄴ 듯
ㄴ 듯 바람

듯 바람
듯 바람 서리
바람
바람 서리
바람 서리 불변
서리 
서리 불변
서리 불변 하
불변
불변 하
불변 하 ㅁ

하 ㅁ
하 ㅁ 은

ㅁ 은
ㅁ 은 우리

은 우리
은 우리 기상
우리 
우리 기상
우리 기상 이
기상
기상 이
기상 이 ㄹ세

이 ㄹ세
ㄹ세

: nori decompound : discard 한 결과에 첫 자리부터 1,2,3글자 (shingle - max : 3) 세트로 나왔다. 

 

GET 검색해보기

위의 결과들과 같다. 위에 분석된 단어로 검색하면 나오고, 위에 없는 단어로 검색하면 안나온다. (위의 단어라도 띄어쓰기가 완전이 일치해야 한다.)

자음 또는 자음이 포함된 단어로 검색이 안된다. ex "하 ㅁ 은", "ㅁ 은", "ㄹ세"

 

 

 

5. nori - decompound : mixed + filter : shingle (max 3) 적용

GET test_search/_analyze
{
  "analyzer": "nori_analyzer_mixed_shingle_max_3", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산
남산 위
남산 위 에

위 에
위 에 저

에 저 
에 저 소나무

저 소나무
저 소나무 소
소나무
소나무 소
소나무 소 나무

소 나무
소 나무 철갑
나무
나무 철갑
나무 철갑 을
철갑
철갑 을
철갑 을 두른

을 두른
을 두른 두르
두른
두른 두르
두른 두르 ㄴ
두르
두르 ㄴ
두르 ㄴ 듯

ㄴ 듯
ㄴ 듯 바람
듯 
.... 이하 생략 

: 이것도 nori decompound - mixed 의 결과에 1,2,3 shingle 세트로 나왔다. 

mixed는 중복인데 또 중복이 겹쳐서 이건 당장 사용할 일은 없어 보임

 

GET으로 검색해보기

위와 같은 결과이다. 

 

 

 

6. ngram ( min:1, max:2 ) 적용 

GET test_search/_analyze
{
  "analyzer": "ngram_analyzer_min_1_max_2", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}

남산


위에



소나

나무


철갑

갑을


두른



바람


서리


불변

변함

함은


우리


기상

상일

일세

: ngram (min: 1, max: 2) 은 띄어쓰기를 기준으로 나눈 "단어"에 1,2글자씩 나누었다. 띄어쓰기 중심으로 양쪽 단어 (2글자) 는 없다. 무조건 띄어쓰기가 기준이군!!

 

GET 검색해보기

위에 분석된 단어로만 검색 결과가 나온다. 

 

 

 

7. ngram ( mix: 2, max: 3 ) 적용

GET test_search/_analyze
{
  "analyzer": "ngram_analyzer_min_2_max_3", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산
위에
소나
소나무
나무
철갑
철갑을
갑을
두른
바람
서리
불변
불변함
변함
변함은
함은
우리
기상
기상일
상일
상일세
일세

: ngram (mix:2, max:3) 도 역시 띄어쓰기를 기준으로 나뉜 "단어" 에 2문자, 3문자로 분석했다. 

 

GET 검색해보기

위에 분석된 단어로만 검색 결과가 나온다. 

 

 

 

8. ngram (mix:1, max: 2) + shingle (max : 3) 

GET test_search/_analyze
{
  "analyzer": "ngram_analyzer_shingle_min_1_max_2", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}

남 남산
남 남산 산
남산
남산 산
남산 산 위

산 위
산 위 위에

위 위에 에
위에
위에 에
위에 에 저

에 저
에 저 소
저 
저 소
저 소 소나

소 소나
소 소나 나
소나 
소나 나
소나 나 나무
나 
나 나무
나 나무 무
나무
나무 무 
나무 무 철
무 
무 철
무 철 철갑

철 철갑
철 철갑 갑
... 이하 생략 

ngram (mix: 1, max: 2) 로 나온 결과에 shingle (max: 3) 이 더해져서 이렇게 엄청 많은 결과가 나왔다. 

ngram (mix: 1) 은 한 글자 한 글자씩 분석하겠다는 건데 거기에 Shingle max 3까지 .... 이 방법도 쓸 일은 없지 않을까,,,

 

GET 검색해보기

위에 분석된 단어로만 검색 결과가 나온다. 

 

 

 

9. ngram (mix:2, max:3) + Shingle (max: 3)

GET test_search/_analyze
{
  "analyzer": "ngram_analyzer_shingle_min_2_max_3", 
  "text": "남산 위에 저 소나무 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세"
}
남산
남산 위에
남산 위에 소나
위에 
위에 소나
위에 소나 소나무
소나 
소나 소나무 나무
소나무
소나무 나무 
소나무 나무 철갑
나무 
나무 철갑
나무 철갑 철갑을
철갑
철갑 철갑을
철갑 철갑을 갑을
철갑을 
철갑을 갑을 
철갑을 갑을 두른
갑을
갑을 두른
갑을 두른 바람
두른
두른 바람
두른 바람 서리
바람
바람 서리
바람 서리 불변
서리
서리 불변
서리 불변 불변함
불변 
불변 불변함
불변 불변함 변함
불변함
불변함 변함
불변함 변함 변함은
변함
변함 변함은
변함 변함은 함은
변함은
변함은 함은
변함은 함은 우리
... 이하 생략 

: 요것도 ngram (min: 2, max: 3) 의 결과에 shingle( max: 3) 이 더해져 위와 같이 나왔다. Min: 2여서 그런지 한 글자인 "저", "듯" 문자가 모두 생략되었다. 

 

GET 검색해보기

위에 분석된 단어로만 검색 결과가 나온다. 

 

 

 

이제 분석해본 결과들로 업무에 적용시켜보기 ! !

 

 

 

 

 

 

 

 

references : https://javas.tistory.com/16 

반응형