IT/elasticsearch

Elasticsearch 여러 필드 검색 하기 COPY_TO사용법

할일없는라이프 2023. 8. 14. 23:57

Elasitcsearch는 inverted index를 이용해서 아주 빠르게 우리가 원하는 내용을 찾을 수 있습니다. 

오늘은 Elasitcsearch를 이용해서 여러 필드를 검색하는 방법에 대해 작성해 보겠습니다. 

위에 내용처럼 ES는 Inverted index를 사용한 검색을 하기 때문에 기본적으로 DSL을 사용합니다.  찾으려는 내용이 Text 필드 이거나 Keyword필드일 때 각각 match와 term을 사용해서 검색을 할 수 있습니다. 

그런데 간혹 2가지 이상의 필드를 검색할때 어떻게 검색해야 하는지 난감한 순간이 있는데, 그럴 때 제가 사용했던 방법에 대해 말씀드리겠습니다.

Text필드 검색

text 검색일때는 multi_match를 사용하면 됩니다. 
예를 들어서  아래와 같은 인덱스가 있다고 할 때 text와 text2에서 나이키를 가진 Doc를 찾고 싶을 때는 multi_match를 사용하면 됩니다.

POST test_match/_doc
{
  "text": "나이키 루나 신상품",
  "text2": "나이키"
}

#검색방법

GET test_match/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "나이키",
            "fields": ["text","text2"]
          }
        }
      ]
    }
  }
}

위와 같이 multi_match를 사용해서 fields에 작성을 해주면 2개 혹은 여러개의 필드에서 "나이키"라는 단어를 찾아낼 수 있습니다. 

Keyword 필드 검색

그런데 문제는 keyword일때 입니다.  보통 keyword 검색에는 term을 사용하는데 쿼리를 찾아봐도 multi_term은 찾을 수 없습니다. 
term과 terms 만이 있는데 term는 단일필드에 단일값, terms는 단일필드에 여러 값을 찾을때 사용합니다. 

그럴 때는 텀을 2번 사용해서 같은 내용을 검색하면 됩니다. 

GET test_match/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "text.keyword": {
              "value": "나이키"
            }
          }
        },
        {
          "term": {
            "text2.keyword": {
              "value": "나이키"
            }
          }
        }
      ]
    }
  }
}

보통은 이런 식으로 찾아야 되는 필드를 term쿼리로 쭉 작성 후에 value를 사용하면 됩니다. 

또한 해당 쿼리는 소프트웨어로 작성시에는 multi_match의 경우 fields 값에 배열을 넣고 , term의 경우 must 쿼리를 순환작성해서 
ES로 전달 하면 됩니다. 

그런데 여기서 한가지 귀차니즘이 발생합니다. text야 보통 형태소를 이용한 장문 text라 그럴 일이 없는데 keyword는 보통 meta 데이터 category, type값은 구분 값들이 들어가고, 인덱스 하나에 보통 그런 메타가 적을 때는 5개 많을 때는 20개 이상 가지고 있는데 그때마다 장문의 쿼리를 작성하는 게 귀찮다는 생각이 들었다.  

그리고 사람이 쿼리를 작성하지 않을경우 모든 필드에 대해서 동일하게 검색하는 방법이 없을까 하다가 Copy_to를 사용하게 되었습니다. 

COPY_TO사용방법

copy_to 는 간단하게 생각하면 필드 2개를 복사해서 한 개의 필드에 넣는 것이라고 생각하면 됩니다. 

아래 예제는 2개의 필드에 여러가지 값을 넣고 검색하는 쿼리입니다. 
copy_to를 사용하려면 mapping을 해줘야 하는데 아래와 같이 mapping을 하면 됩니다. 

PUT test_keyword  // 인덱스 메핑 
{
  "mappings": {
    "properties": {
      "keyword1" : {
        "type": "keyword",
        "copy_to": "search"  //copy_to작성후 검색할 1개의 필드로 이름 작성
      },
      "keyword2": {
        "type": "keyword",
        "copy_to": "search"
      },
      "search": {
        "type": "keyword"
      }
    }
  }
}


GET test_keyword/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "search": {  //한개의 필드에서 모자 검색
              "value": "모자"
            }
          }
        }
      ]
    }
  }
}

그리고 COPY_TO를 사용할때 wildcard검색도 할 수 있기 때문에 여러 메타필드를 한 번에 검색할 때 편하게 사용할 수 있다

GET test_keyword/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "wildcard": {
            "search": {
              "value": "나*"
            }
          }
        }
      ]
    }
  }
}