ES 검색엔진 만들기 (6) Update by query 로 user dict 변경사항 doc에 반영하기
https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-update-by-query.html#docs-update-by-query
Elasticsearch 에서 Dictionary 변경시 anlyzer와 인덱싱된 Document 갱신 하기
why userdict
GET _analyze
{
"tokenizer": "nori_tokenizer",
"text" : [
"아일리스프런티어"
]
}
를 검색했을때, 결과는 아래와 같이 나온다.
{
"tokens" : [
{
"token" : "아",
"start_offset" : 0,
"end_offset" : 1,
"type" : "word",
"position" : 0
},
{
"token" : "일리스",
"start_offset" : 1,
"end_offset" : 4,
"type" : "word",
"position" : 1
},
{
"token" : "프런티어",
"start_offset" : 4,
"end_offset" : 8,
"type" : "word",
"position" : 2
}
]
}
이러면 나중에 검색을 할수 없다. 이러한 문제로 인해 우리는 USER DICT를 사용하는것을 모두 알고 있을것이다. 이제 USER DICT을 추가하고 반영하는 방법을 차근차근 알아보도록 하자.
userdict.txt
아일리스프런티어
만 딱 등록해준다.
PUT nori_sample
PUT nori_sample
{
"settings": {
"index": {
"analysis": {
"tokenizer": {
"nori_user_dict": {
"type": "nori_tokenizer",
"decompound_mode": "mixed",
"user_dictionary": "dictionary/userdict.txt"
}
},
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "nori_user_dict"
}
}
}
}
},
"mappings": {
"properties": {
"message": {
"type": "text",
"analyzer": "my_analyzer"
}
}
}
}
GET nori_sample/_analyze
{
"analyzer": "my_analyzer",
"text" : [
"아일리스프런티어"
]
}
{
"tokens" : [
{
"token" : "아일리스프런티어",
"start_offset" : 0,
"end_offset" : 8,
"type" : "word",
"position" : 0
}
]
}
이건 userdict을 만든상태에서 index를 생성한 케이스고 이미 존재하는 index의 document를 변경 반영하는것이 이번 학습의 목표이다. 일단 테스트를 위해 doc하나만 생성하도록 하자.
POST nori_sample/_doc/1
{
"message": "아일리스프런티어 소속 한승우입니다."
}
그런데 이렇게 하면 이제 아일리스프런티어로 검색하여 찾는것이 가능해진다. 그런데 만약 유저 사전에서 아일리스프런티어를 삭제하면 어떻게 될까???
일단 아일리스프런티어를 삭제해보자 그후
POST /nori_sample/_close
POST /nori_sample/_open
후에
GET nori_sample/_analyze
{
"analyzer": "my_analyzer",
"text" : [
"아일리스프런티어"
]
}
를 다시 검색해보도록 한다.
{
"tokens" : [
{
"token" : "아",
"start_offset" : 0,
"end_offset" : 1,
"type" : "word",
"position" : 0
},
{
"token" : "일리스",
"start_offset" : 1,
"end_offset" : 4,
"type" : "word",
"position" : 1
},
{
"token" : "프런티어",
"start_offset" : 4,
"end_offset" : 8,
"type" : "word",
"position" : 2
}
]
}
성공???
GET nori_sample/_search
{
"query": {
"match": {
"message": "프런티어"
}
}
}
를 검색했을때 이제 검색이 되어야 한다.
안된다…. 뭐지??? 이제 왜 안되었는지 아래서부터 차근차근 알아보도록 한다.
Userdict, SynonymDicy 반영
Elasticsearch 에서 Dictionary를 사용하여 analyzer를 만들고 이것을 사용하여 index에 Document를 인덱싱할 수 있다. 그런데 Dictionary가 변경되면 analyzer를 변경하고 indexing된 document를 갱신하려면 어떻게 해야 하는지 알아보자.
사전 지식
analyzer는 character filter, tokenizer, token filter 순으로 진행된다. 기본적으로 analyzer는 indexing time 과 search time에 적용이 된다. index time 분석 대상은 source datat이고, search time 분석은 query string 이다. 그러므로 사전을 변경하는 것은 indexing, searching 두곳 모두에 영향을 준다.
사전 업데이트
Update by query 과정
- update by query 요청은 cordinating node에서 수신한다. 요청을 받자마자 인덱스의 snap shot 이 생성된다.
- snapshot이 생성되면서 쿼리 조건에 맞는 모든 documnet를 찾기 위해서 각각의 인덱스들에 search query가 전송 된다.
- 쿼리 조건에 맞는 doc을 찾으면 doc을 업데이트 하기 위해 bulk request를 전송한다,
- bulk reuqest 결과 내 배치 필드를 통해 doc를 회수하기 위해 사용된 배치 수를 파악할 수 있으며, 반환된 doc의 개수가 만개를 넘어것을 대피 하여 search query는 scroll 을 사용한다.
- search query 와 bulk request는 짝을 이루며 순차적으로 인덱스에 전송된다.
- 요청들을 동시에 전송하지 않는 잉는 업데이트 오류 처리 과정과 밀접한 영향을 갖는다,
- es는 업데이트 오류 발생시 디폴트로 10번 재시도 하며, 10번 재시도 이후 실패시 query전체가 취소된다. (취소의 개념이 RDB transactional 처럼 업데이트 건도 rollback 하는것은 아니라 기존 업데이트 건을 그대로 두고 실패한 doc이후의 doc에 대해서는 업데이트 요청을 하지 않는것이다.)
- 이 방식은 delete_by_query또한 동일