결국 무엇이든 해내는 사람

ElasticSearch - Synonym 토큰필터, 동의어 사전 파일 [ 예제, 설명 ] 본문

두서없는 공부 노트/ElasticSearch

ElasticSearch - Synonym 토큰필터, 동의어 사전 파일 [ 예제, 설명 ]

kkm8257 2021. 12. 14. 17:38
반응형
-- 검색 서비스에 따라서 동의어 검색이 필요할 수 있다
-- Synonym 토큰 필터를 사용하면 텀의 동의어 저장이 가능
-- 불용어 사전과 마찬가지로 직접 목록을 입력하거나, 파일로 관리가 가능

-- "A, B => C" : 왼쪽의 A, B 대신 오른쪽의 C 텀을 저장. A, B 로는 C의 검색이 가능하지만 C로는 A, B가 검색되지 않는다.
-- "A, B" : A,B 각각의 텀이 A와 B 두 개의 텀을 모두 저장한다. A와 B 모두 서로의 검색어로 검색이 된다.


-- "amazon" => "aws"로 동의어를 지정하는 예제
-- amazon 을 검색할 경우 aws의 검색결과도 같이 보여준다.

PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms": [
            "amazon => aws"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}


-- test 데이터 두개 저장
PUT my_synonym/_doc/1
{ "message" : "Amazon Web Service" }
PUT my_synonym/_doc/2
{ "message" : "AWS" }






-- 동의어 사전에 의해 , lowercase 필터 다음에 syn_aws 적용될 때 amazon 대신 aws로 바뀐 후 저장된다.
-- termvector 검색
GET my_synonym/_termvectors/1?fields=message


-- 결과
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 4,
        "doc_count" : 2,
        "sum_ttf" : 4
      },
      "terms" : {
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 6
            }
          ]
        },
        "service" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 2,
              "start_offset" : 11,
              "end_offset" : 18
            }
          ]
        },
        "web" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 7,
              "end_offset" : 10
            }
          ]
        }
      }
    }
  }
}


-- termvector 검색
GET my_synonym/_termvectors/2?fields=message

-- aws 는 그대로 저장이 된것을 확인
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 4,
        "doc_count" : 2,
        "sum_ttf" : 4
      },
      "terms" : {
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 3
            }
          ]
        }
      }
    }
  }
}




-- term 쿼리를 날렸을때, "aws"를 검색하면 "aws"와 "Amazon Web Service" 결과를 둘다 보여준다
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "aws"
    }
  }
}

-- 결과
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.22920428,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.22920428,
        "_source" : {
          "message" : "AWS"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.15136129,
        "_source" : {
          "message" : "Amazon Web Service"
        }
      }
    ]
  }
}


-- 반대로 "amazon"으로 term 검색할 경우
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "amazon"
    }
  }
}


-- 아무것도 나타나지 않음
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}



-- match 쿼리를 사용 할경우
GET GET my_synonym/_search
{
  "query": {
    "match": {
      "message": "amazon"
    }
  }
}


-- term 쿼리로 "aws" 를 검색했을 때와 동일한 결과
-- why?? match 쿼리는 검색어에도 my_syn 애널라이저가 적용되어 "aws"로 변환하여 검색을 하기 때문에 aws로 검색한 것과 같은 결과가 나타난다.
-- term 쿼리는 검색어에 애널라이저를 적용하지 않고 그대로 검색하기 때문에 term 쿼리로 aws를 검색하면 두 개의 도큐먼트가 검색된다.

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.22920428,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.22920428,
        "_source" : {
          "message" : "AWS"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.15136129,
        "_source" : {
          "message" : "Amazon Web Service"
        }
      }
    ]
  }
}



-- [ 정리 ]
-- my_syn 필터를 이용해서 색인을 하면
-- "Amazon Web Service"이 원본 데이터 --- > aws, web, service 텀으로 쪼개짐
-- "AWS"가 원본 데이터 --- > aws 텀으로 쪼개짐
-- 검색시에 term 쿼리로 검색을 할 때 , "aws"를 검색하게 되면  원본 데이터 "Amazon Web Service" , "AWS" 가 출력 되는 것이다
-- 검색시에 term 쿼리로 검색을 할 때 , "amazon"을 검색하게 되면 해당하는 term이 없으니까 출력할 데이터가 없음
-- 검색시에 term 쿼리가 아니라 match 쿼리로 검색할 경우 , "amazon"을 검색하면 match 쿼리는 애널라이저가 적용되므로
-- 검색어 "amazon"이 "aws"로 변환되어 검색이 되고, 검색어 "aws"에 해당하는 원본데이터 "Amazon Web Service" , "AWS" 가 출력된다






-- amazon과 aws를 동의어로 지정하기, 서로 검색이 되게끔
-- 인덱스 삭제 후 재생성
PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms": [
            "amazon, aws"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}


-- test 데이터 삽입
PUT my_synonym/_doc/1
{ "message" : "Amazon Web Service" }
PUT my_synonym/_doc/2
{ "message" : "AWS" }

-- termvector 출력
GET my_synonym/_termvectors/1?fields=message


-- "aws"와 "amazon"은 동의어이므로 positon 값이 같으며,   id 1번의 데이터는 "Amazon Web Service"였다
-- Amazon 대신 amazon으로 바뀌었고 ( lowercase ), Amazon은 동의어 사전에 의해 aws와 같은 동의어가 되었으며
-- 이는 색인시 amazon과 aws 둘다 저장되는 결과를 가져옴
-- 이렇게 되면 "amazon"으로 term 검색을 할 경우 "AWS" 와 "Amazon Web Service" 두 개다 검색결과로 보여준다

-- termvector 결과
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 6,
        "doc_count" : 2,
        "sum_ttf" : 6
      },
      "terms" : {
        "amazon" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 6
            }
          ]
        },
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 6
            }
          ]
        },
        "service" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 2,
              "start_offset" : 11,
              "end_offset" : 18
            }
          ]
        },
        "web" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 7,
              "end_offset" : 10
            }
          ]
        }
      }
    }
  }
}


-- "amazon"이라고 검색
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "amazon"
    }
  }
}


-- "=>" 로 설정했을 떄와는 다른 결과
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.25069216,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.25069216,
        "_source" : {
          "message" : "AWS"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.18232156,
        "_source" : {
          "message" : "Amazon Web Service"
        }
      }
    ]
  }
}



-- 불용어 사전과 마찬가지로 파일로 관리가능, config 폴더 밑에 디렉토리를 새로 만들어서 참조하는 것은 동일하다

PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms_path": "analysis/my_synonym_dic.txt"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}


-- test 데이터 삽입
PUT my_synonym/_doc/1
{ "message": "Quick brown fox jump" }
PUT my_synonym/_doc/2
{ "message": "hop rabbit is fast" }




-- quick으로 검색시 fast를 포함하는 도큐먼트도 같이 출력된다. fast로 검색해도 마찬가지

GET my_synonym/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "message": "quick"
          }
        }
      ]
    }
  }
}


-- 결과
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.21110919,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.21110919,
        "_source" : {
          "message" : "Quick brown fox jump"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.21110919,
        "_source" : {
          "message" : "hop rabbit is fast"
        }
      }
    ]
  }
}
반응형
Comments