본문 바로가기

프로젝트/여행지 오픈 API

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

목차

     

    프로젝트 진행 중 가장 큰 고민은, 단순 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;"
              }
            }
          }
        }
      }
    }

     

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