검색/ElasticSearch

동의어 등록 테스트 ( nori / Elasticsearch v6.5.4)

듐듐다다 2021. 8. 26. 14:22
반응형

동의어 사전이 적용되는 시점

Tokenizer를 통해 형태소 분석이 끝난 후에 Token Filters에 정의한 순서 대로 사전이 적용된다.

동의어 등록

ELS_HOME/config/synonyms.txt
동의어는 하나의 규칙당 한 줄식 입력해야 하며 파일은 UTF8로 인코딩 되어야한다.

에어프라이어,에어플라이어,애어프라이어,애어플라이어
에이아이,인공지능
대한민국,우리나라,한국
한국,코리아,korea
아름다움,멋=>뿜뿜

동의어 등록 후에는 _setting 정보를 update하기 위해 indices를 닫았다가 열어야한다.

POST synonyms_dic_test/_close
POST synonyms_dic_test/_open

이미 색인된 데이터에 사전을 반영하기 위해서는 재색인을 해야한다

동의어 적용 테스트

기존 인덱스 삭제

DELETE synonyms_dic_test

인덱스 생성

PUT synonyms_dic_test/
{
  "mappings": {
    "product": {
      "properties": {
        
        "product_sub_title": {
          "type": "text",
          "analyzer": "korean"
        },
        "product_title": {
          "type": "text",
          "analyzer": "korean"
        }
      }
    }
  },
  "settings": {
    "index": {
      "number_of_shards": "3",
      "refresh_interval" : "-1",
      "analysis": {
        "filter": {
          "stop_filter": {
            "type": "stop",
            "stopwords_path": "stopwords.txt"
          },
          "synonym": {
            "type": "synonym",
            "synonyms_path": "dic/synonyms.txt",
            "updateable": "true"
          }
        },
        "char_filter": {
          "decimal_mark_filter": {
            "pattern": "(\\d+),(?=\\d)",
            "type": "pattern_replace",
            "replacement": "$1 "
          }
        },
        "analyzer": {
          "korean": {
            "filter": [
              "lowercase",
              "synonym",
              "stop_filter"
            ],
            "type": "custom",
            "tokenizer": "korean_default_tokenizer"
          },
          "category_seq_analyzer": {
            "char_filter": [
              "decimal_mark_filter"
            ],
            "tokenizer": "standard"
          }
        },
        "tokenizer": {
          "korean_default_tokenizer": {
            "type": "nori_tokenizer",
            "user_dictionary": "userdict_ko.txt",
            "decompound_mode": "discard"
          }
        }
      },
      "number_of_replicas": "1"
    }
  }
}

테스트 데이터 삽입

PUT synonyms_dic_test/product/1
{
  "product_title":"대한민국에 오신것을 환영합니다."
}
PUT synonyms_dic_test/product/2
{
  "product_title":"우리나라는 강산이 어우러져 자연의 아름다움을 느낄 수 있습니다."
}
PUT synonyms_dic_test/product/3
{
  "product_title":"한국의 멋"
}
PUT synonyms_dic_test/product/4
{
  "product_title":"korea는 내나라"
}
PUT synonyms_dic_test/product/5
{
  "product_title":"우리나라의 자랑"
}
PUT synonyms_dic_test/product/6
{
  "product_title":"먼지가 뿜뿜"
}

검색 테스트 1

사전 등록

대한민국,우리나라,한국
한국,코리아,korea

검색 : '우리나라'
결과 : 사전에 등록한 키워드(대한민국,우리나라,한국,코리아,korea)를 포함한 문서가 검색 된다.

//검색
GET synonyms_dic_test/product/_search
{
  "query": {
    "match": {
      "product_title":{
        "query":"우리나라",
      "operator": "and"
      }
    }
  }
}
//결과
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 5,
    "max_score" : 6.9298778,
    "hits" : [
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "5",
        "_score" : 6.9298778,
        "_source" : {
          "product_title" : "우리나라의 자랑"
        }
      },
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "2",
        "_score" : 4.0648756,
        "_source" : {
          "product_title" : "우리나라는 강산이 어우러져 자연의 아름다움을 느낄 수 있습니다."
        }
      },
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "3",
        "_score" : 2.2562294,
        "_source" : {
          "product_title" : "한국의 멋"
        }
      },
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "1",
        "_score" : 1.6988081,
        "_source" : {
          "product_title" : "대한민국에 오신것을 환영합니다."
        }
      },
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "4",
        "_score" : 0.44839138,
        "_source" : {
          "product_title" : "korea는 내나라"
        }
      }
    ]
  }
}

검색 테스트 2

사전 등록

아름다움,멋=>뿜뿜

검색 : ‘아름다움’
결과 : 사전에 등록한 키워드(아름다움,멋=>뿜뿜) 대로 ‘아름다움’ or ‘멋' 으로 검색시에 ‘아름다움, 멋, 뿜뿜’을 포함한 문서가 검색 된다.

//검색
GET synonyms_dic_test/product/_search
{
  "query": {
    "match": {
      "product_title":{
        "query":"아름다움",
      "operator": "and"
      }
    }
  }
}
//결과
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 2.5682926,
    "hits" : [
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "6",
        "_score" : 2.5682926,
        "_source" : {
          "product_title" : "먼지가 뿜뿜"
        }
      },
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "3",
        "_score" : 2.3460367,
        "_source" : {
          "product_title" : "한국의 멋"
        }
      },
      {
        "_index" : "synonyms_dic_test",
        "_type" : "product",
        "_id" : "2",
        "_score" : 1.5926098,
        "_source" : {
          "product_title" : "우리나라는 강산이 어우러져 자연의 아름다움을 느낄 수 있습니다."
        }
      }
    ]
  }
}

검색 테스트 3
'B,C,D =>A' 등록 방식을 ‘A=>B,C,D’로 하면 어떤 결과가 나오는지 확인한다.
(A를 검색 할때 B,C,D문서가 모두 나올까?)

사전 검색어 결과
뿜뿜=>아름다움,멋,자랑 뿜뿜 _id:6 (먼지가 뿜뿜)
_id:2(우리나라는 강산이 어우러져 자연의 아름다움을 느낄 수 있습니다.)
_id:6, _id:3 (한국의 멋)
아름다움 _id:6, _id:2
자랑 _id:6, _id:5(우리나라의 자랑)
뿜뿜=>멋,자랑,아름다움 뿜뿜 _id:6, _id:2
뿜뿜=>자랑,아름다움,멋 뿜뿜 _id:6, _id:2
자랑=>뿜뿜,아름다움,멋 자랑 _id:6, _id:2, _id:5
자랑=>아름다움,멋,뿜뿜 자랑 _id:6, _id:2, _id:5
자랑=>멋,아름다움,뿜뿜 자랑 _id:6, _id:2, _id:5
아름다움=>멋,자랑,뿜뿜 아름다움 _id:6, _id:2
아름다움=>자랑,뿜뿜,멋 아름다움 _id:6, _id:2

결론 : 사전을 등록할 때 A=>B,C,D 로 등록을 하면 A로 검색 할 경우 B,C,D중 하나 혹은 두 개가 임으로 매칭되는 것 같지만 규칙은 모르겠다.
실제로 A 위치에 해당하는 키워드를 _termvectors로 확인해 보면 B,C,D 키워드를 모두 가지고 있다.

/*
사전에 '자랑=>멋,아름다움,뿜뿜' 등록 후
'우리나라의 자랑'이라는 문구가 들어간 _id:5을 termvectors 로 확인 해 보면
'자랑'이 '뿜','아름답','멋'으로 변환 되어 들어가 있다.
그리고 '뿜뿜','아름다움','멋'으로 검색 할 경우 '자랑'이 들어간 문서가 함께 결과로 나오지만,
'자랑'이라는 검색 결과로는 B,C,D 위치 중 어떤 키워드가 들어간 문장이 나올지 예측 할 수없다.
*/
GET synonyms_dic_test/product/5/_termvectors?
{
  "fields" : ["product_title"],
  "offsets" : true,
  "payloads" : true,
  "positions" : true,
  "term_statistics" : true,
  "field_statistics" : true
}

===
{
  "_index" : "synonyms_dic_test",
  "_type" : "product",
  "_id" : "5",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "product_title" : {
      "field_statistics" : {
        "sum_doc_freq" : 40,
        "doc_count" : 4,
        "sum_ttf" : 42
      },
      "terms" : {
        "ᄆ" : {
          "doc_freq" : 2,
          "ttf" : 2,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 4,
              "start_offset" : 6,
              "end_offset" : 8
            }
          ]
        },
        "나라" : {
          "doc_freq" : 3,
          "ttf" : 3,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 2,
              "end_offset" : 4
            }
          ]
        },
        "대한" : {
          "doc_freq" : 2,
          "ttf" : 2,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 2
            }
          ]
        },
        "멋" : {
          "doc_freq" : 1,
          "ttf" : 1,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 3,
              "start_offset" : 6,
              "end_offset" : 8
            }
          ]
        },
        "민국" : {
          "doc_freq" : 2,
          "ttf" : 2,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 2,
              "end_offset" : 4
            }
          ]
        },
        "뿜" : {
          "doc_freq" : 2,
          "ttf" : 4,
          "term_freq" : 2,
          "tokens" : [
            {
              "position" : 3,
              "start_offset" : 6,
              "end_offset" : 8
            },
            {
              "position" : 4,
              "start_offset" : 6,
              "end_offset" : 8
            }
          ]
        },
        "아름답" : {
          "doc_freq" : 2,
          "ttf" : 2,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 3,
              "start_offset" : 6,
              "end_offset" : 8
            }
          ]
        },
        "우리" : {
          "doc_freq" : 2,
          "ttf" : 2,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 2
            }
          ]
        },
        "의" : {
          "doc_freq" : 3,
          "ttf" : 3,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 2,
              "start_offset" : 4,
              "end_offset" : 5
            }
          ]
        },
        "한국" : {
          "doc_freq" : 3,
          "ttf" : 3,
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 4
            }
          ]
        }
      }
    }
  }
}

참고 링크

*같지만 다른 단어: 동의어로 Elasticsearch의 성능 강화 :
https://www.elastic.co/kr/blog/boosting-the-power-of-elasticsearch-with-synonyms

* 동의어 :
https://esbook.kimjmin.net/06-text-analysis/6.6-token-filter/6.6.3-synonym

 

반응형