프로젝트/여행지 오픈 API

[Elasticsearch] 여행지 정보 키워드 추출, 집계

블랑v 2023. 11. 7. 17:47

 

프로젝트 진행 중 가장 큰 고민은, 단순 match가 아닌, 어떻게 하면 정보를 정확하고 유사하게 뿌려줄까? 라는 고민이었다.

 

  • attraction_name : 가장 높은 가산치를 줘야 할 것 같다.
  • wiki_title match 점수
  • 공공데이터의 description : 그 다음 높은 가산치
  • wiki의 description : 낮은 가산치

 

검색어가 있을 것이다. 단일 검색어든 뭐든..

일단 content_id로 한번에 묶으려고 한다.

검색어가 '서울 남산공원' 이라면 우리의 데이터는 '서울' 과 '남산공원' 이 같은 contend_id로 묶여있기 때문.

이 content_id는 기본 베이스인 공공데이터의 id 번호이다

 

script를 써야 할 것 같은데, 먼저 content_id로 묶을 것이다.

여러 Document들 중 같은 content_id로 묶은 다음, 각 도큐먼트마다 필드값을 합치고, 이 안에서 용어를 검색할 경우 정확도가 높을 것이다.

 

 

집계 로직

#먼저 content_id로 집계하자.

GET scrap_wiki_1107/_search
{
  "size": 0,
  "track_total_hits": true,
  "aggs": {
    "by_content_id": {
      "terms": {
        "field": "content_id", 
        "size": 10 
      }
    }
  }
}

 

# 모든 필드 합산
GET scrap_wiki_1107/_search
{
  "size": 0,
  "track_total_hits": true,
  "aggs": {
    "by_content_id": {
      "terms": {
        "field": "content_id",
        "size": 100000 #충분히 큰 크기를 사용
      },
      "aggs": {
        "combined_fields": {
          "scripted_metric": {
            "init_script": "state.messages = []",
            "map_script": "state.messages.add(doc['attraction_name'].value + ' ' + doc['wiki_title'].value + ' ' + doc['wiki_content'].value + ' ' + doc['overview'].value)",
            "combine_script": "return state.messages",
            "reduce_script": "return states.flatten()"
          }
        }
      }
    }
  }
}

 

하지만 중간에 value가 null인 문서가 존재했다. 이를 위해 value를 확인하는 로직을 추가해야 한다.

 

# map script를 다음과 같이 수정
"map_script": 
"String attraction = doc['attraction_name'].size() != 0 ? 
doc['attraction_name'].value : ''; 
String wikiTitle = doc['wiki_title'].size() != 0 ? 
doc['wiki_title'].value : ''; 
String wikiContent = doc['wiki_content'].size() != 0 ? 
doc['wiki_content'].value : ''; 
String overview = doc['overview'].size() != 0 ? 
doc['overview'].value : ''; 
state.messages.add(attraction + ' ' + wikiTitle + ' ' + wikiContent + ' ' + overview)"


# 최종 수정 코드
GET scrap_wiki_1107/_search
{
  "size": 0,
  "track_total_hits": true,
  "aggs": {
    "by_content_id": {
      "terms": {
        "field": "content_id",
        "size": 100000 
      },
      "aggs": {
        "combined_fields": {
          "scripted_metric": {
            "init_script": "state.messages = []",
            "map_script": 
            """String attraction = doc['attraction_name'].size() != 0 ? 
            doc['attraction_name'].value : ''; 
            String wikiTitle = doc['wiki_title'].size() != 0 ? 
            doc['wiki_title'].value : ''; 
            String wikiContent = doc['wiki_content'].size() != 0 ? 
            doc['wiki_content'].value : ''; 
            String overview = doc['overview'].size() != 0 ? 
            doc['overview'].value : ''; 
            state.messages.add(attraction + ' ' + wikiTitle + ' ' + wikiContent + ' ' + overview)""",
            "combine_script": "return state.messages",
            "reduce_script": "List combined = new ArrayList(); for (List state : states) { combined.addAll(state); } return combined;"
          }
        }
      }
    }
  }
}

 

최종 결과. 나름 유의미한 토큰 같기도..?