Post

엘라스틱서치 검색과 쿼리 활용

컨텍스트 유형, 쿼리 종류와 문법, 스코어링과 부스팅 전략

엘라스틱서치 검색과 쿼리 활용

1. 쿼리 컨텍스트와 필터 컨텍스트

  • 쿼리 컨텍스트
    • 질의에 대한 유사도를 계산해 이를 기준으로 더 정확한 결과를 먼저 보여준다
    • 연관성에 따른 스코어 결과를 제공
  • 필터 컨텍스트
    • 유사도를 계산하지 않고 일치 여부에 따른 결과만을 반환
    • “예/아니요”의 결과를 제공
    • 스코어 계산을 하지 않아 결과에 대한 업데이트를 수행할 필요가 없기 때문에 캐시를 이용할 수 있다
    • 엘라스틱서치는 기본적으로 힙 메모리의 10%를 캐시에 이용하고 있는데, 캐시를 이용한 빠른 검색을 하려면 필터 컨텍스트를 이용해야 한다



2. 쿼리 스트링과 쿼리 DSL

  • 퀴리 스트링
    • REST API의 URI 주소에 쿼리문을 작성하는 방식으로 실행 가능
    1
    
    curl -H "Content-Type: application/x-ndjson" -XGET localhost:9200/kibana_sample_data_ecommerce/_search?q=customer_fullname:Mary
    
  • 쿼리 DSL
    • REST API의 요청 본문 안에 JSON 형태로 쿼리를 작성
    1
    2
    3
    4
    5
    6
    7
    8
    
    curl -H "Content-Type: application/x-ndjson" -XGET localhost:9200/kibana_sample_data_ecommerce/_search
    {
      "query" : {
        "match" : {
          "customer_full_name" : "Mary"
        }
      }
    }
    



3. 유사도 스코어

  • 쿼리 컨텍스트는 기본적으로 BM25 알고리즘을 이용해 유사도 스코어를 계산
  • 유사도 스코어는 질의문과 도큐먼트의 유사도를 표현하는 값으로, 스코어가 높을수록 찾고자 하는 도큐먼트에 가깝다는 사실을 의미
  • 검색 시 explain: true를 추가하면 쿼리 내부적인 최적화 방법과 어떤 경로를 통해 검색되었으며 어떤 기준으로 스코어가 계산되었는지 알 수 있다


3-1. 스코어 알고리즘(BM25)

  • BM25 알고리즘은 검색, 추천에 많이 사용되는 알고리즘으로 TF(Term Frequency), IDF(Inverse Document Frequency) 개념에 문서 길이를 고려한 알고리즘
  • 검색어가 문서에서 얼마나 자주 나타나는지, 검색어가 문서 내에서 중요한 용어인지 등을 판단하는 근거를 제공


3-2. IDF 계산

  • 문서 빈도(Document Frequency)는 특정 용어가 ‘전체 도큐먼트’에서 얼마나 자주 등장했는지를 의미하는 지표
  • IDF(Inverse Document Frequency)는 문서 빈도의 역수로 전체 문서에서 자주 발생하는 단어일수록 중요하지 않은 단어로 인식하고 가중치를 낮추는 것


3-3. TF 계산

  • 용어 빈도(Term Frequency)는 특정 용어가 ‘하나의 도큐먼트’에서 얼마나 많이 등장했는지를 의미하는 지표
  • 일반적으로 특정 용어가 도큐먼트에서 많이 반복되었다면 그 용어는 도큐먼트의 주제와 연관되어 있을 확률이 높다
  • 최종 스코어는 IDF와 TF 그리고 boost 변수(2.2)를 곱하면 된다



4. 쿼리

4-1. 전문 쿼리와 용어 수준 쿼리

  • 전문 쿼리는 전문 검색을 하기 위해 사용되며, 전문 검색을 할 필드는 인덱스 매핑 시 텍스트 타입으로 매핑해야 한다
    • match 쿼리, match phrase 쿼리, multi match 쿼리, query string 쿼리
  • 용어 수준 쿼리는 정확히 일치하는 용어를 찾기 위해 사용되며, 인덱스 매핑 시 필드를 키워드 타입으로 매핑해야 한다
    • 일반적으로 숫자, 날짜, 범주형 데이터를 정확하게 검색할 때 사용
    • term 쿼리, terms 쿼리, fuzzy 쿼리


4-2. match 쿼리

  • 전문 쿼리의 가장 기본이 되는 쿼리
  • 전체 텍스트 중에서 특정 용어나 용어들을 검색할 때 사용
  • 전문 쿼리의 경우 검색어도 토큰화된다
  • match 쿼리에서 용어들 간의 공백은 OR로 인식한다


4-3. match phrase 쿼리

  • match phrase 쿼리는 구(phrase)를 검색할 때 사용
  • 검색어에 사용된 용어들이 모두 포함되면서 용어의 순서까지 맞아야 한다
  • 검색 시 많은 리소스를 요구하기 때문에 자주 사용하는 것은 좋지 않다


4-4. term 쿼리

  • term 쿼리는 용어 수준 쿼리의 대표적 쿼리로 정확한 용어가 있는 경우만 매칭이 된다
    • 예를 들어 “Mary Bailey”를 검색하면 토큰화되지 않으며 분석기를 거치지 않았기 때문에 대소문자도 정확히 맞아야 한다
  • term 쿼리를 포함한 용어 수준 쿼리는 키워드 타입으로 매핑된 필드를 대상으로 주로 키워드 검색이나 범주형 데이터를 검색하는 용도로 사용
  • match 쿼리를 포함한 전문 쿼리는 텍스트 타입으로 매핑된 필드를 대상으로 전문 검색에 사용해야 한다


4-5. terms 쿼리

  • 용어 수준 쿼리의 일종이며 여러 용어들을 검색
  • 키워드 타입으로 매핑된 필드에서 사용해야 하며, 분석기를 거치지 않았기 때문에 대소문자도 신경써야 한다


4-6. multi match 쿼리

  • 검색하고자 하는 용어나 구절이 정확히 어떤 필드에 있는지 모르는 경우 사용
  • 여러 개의 필드에서 검색하기 위한 multi match 쿼리는 전문 검색 쿼리의 일종으로, 텍스트 타입으로 매핑된 필드에서 사용
  • 1개 이상의 필드에 쿼리를 요청하면 각 필드에서 개별 스코어를 구한 다음 그중 가장 큰 값을 대표 스코어로 구한다
    • 대표 스코어 선택 방식은 사용자가 결정할 수 있지만, 특별한 설정을 하지 않으면 기본으로 가장 큰 스코어를 대표 스코어로 사용
    • explain 파라미터를 true로 설정하면 개별 필드의 스코어가 어떻게 계산되었는지 알 수 있고 대표 스코어가 어떻게 선정되는지도 알 수 있다
  • 검색하려는 필드가 많은 경우에는 필드명에 와일드카드를 사용해 이름이 유사한 복수의 필드를 선택할 수도 있음
  • boosting 기법
    • 여러 개의 필드 중 특정 필드에 가중치를 두는 방법
    • multi match 쿼리에서 자주 사용된다
    • 가중치를 부여하고자 하는 특정 필드에 ^ 기호와 숫자를 적어주면 된다
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    {
      "query" : {
        "multi_match" : {
          "query" : "mary",
          "fields" : [
            "customer_full_name^2",
            "customer_first_name",
            "customer_last_name"
          ]
        }
      }
    }
    


4-7. range 쿼리

  • 특정 날짜나 숫자의 범위를 지정해 범위 안에 포함된 데이터들을 검색할 때 사용
  • 날짜/숫자/IP 타입의 데이터는 범위 쿼리가 가능하지만 문자형, 키워드 타입의 데이터에는 범위 쿼리를 사용할 수 없다
  • 검색 범위를 지정하는 파라미터
    • gte, gt, lte, lt
  • 날짜/시간 관련 범위 표현식

    expressiondescription
    now현재 시각(2024-02-29T15:20:33)
    now+1d현재 시각 + 1일(2024-02-30T15:20:33)
    now+1h+30m+10s현재시각 + 1시간 30분 10초(2024-02-30T16:50:43)
    2024-02-29+1M2024-02-29 + 1달(2024-03-29T00:00:00)
  • 범위 데이터 타입
    • integer_range, float_range, long_range, date_range, ip_range
    • 범위 타입은 gte, lt 같은 파라미터를 이용해 범위를 지정해서 인덱싱해야 한다
      1
      2
      3
      4
      5
      6
      
      {
      "test_date" : {
        "gte" : "2024-01-01",
        "lt" : "2024-02-29"
      }
      }
      
  • range 쿼리는 relation이라는 파라미터를 이용해 어떤 범위를 포함할지 결정할 수 있다
  • relation에 들어갈 수 있는 값들

    valuedescription
    intersects(기본값)쿼리 범위 값이 도큐먼트의 범위 데이터를 일부라도 포함하기만 하면 된다
    contains도큐먼트의 범위 데이터가 쿼리 범위 값을 모두 포함해야 한다
    within도큐먼트의 범위 데이터가 쿼리 범위 값 내에 전부 속해야 한다


4-8. 논리 쿼리

  • 논리 쿼리는 복합 쿼리로 위 쿼리들을 조합할 수 있다
    • ‘2024년 2월 29일’에 생성된 로그 중에서 ‘상태가 불량’인 것들을 검색하거나 ‘서울’에서 발생한 데이터이면서 ‘제주도’에서 발생하지 않은 데이터를 검색해야 하는 경우 쿼리 조합이 필요
  • 논리 쿼리 타입
    • must, must_not, should, filter
  • must 타입
    • 쿼리를 실행하여 참인 도큐먼트를 찾는다
    • must 타입에 복수 개의 쿼리를 실행하면 AND 효과를 얻을 수 있다
      1
      2
      3
      4
      5
      6
      7
      8
      
      "query" : {
        "bool" : {
          "must" : [
            { "term" : { "day_of_week" : "Sunday" } },
            { "match" : { "customer_full_name" : "mary" } }
          ]
        }
      }
      
  • must_not 타입
    • 쿼리를 실행하여 거짓인 도큐먼트를 찾는다.
    • 다른 타입과 같이 사용할 경우 도큐먼트에서 제외한다
      1
      2
      3
      4
      5
      6
      7
      8
      
      "query" {
        "bool" : {
          "must" : {
            "match" : { "customer_first_name" : "mary" }
          },
          "must_not" : { "customer_last_name" : "bailey" }
        }
      }
      
  • should 타입
    • 단독으로 사용 시 쿼리를 실행하여 참인 도큐먼트를 찾는다 (must타입과 같은 결과)
    • 복수 개의 쿼리를 실행하면 OR 연산을 한다
    • 다른 타입과 같이 사용할 경우 검색 결과에 영향을 주지 않고 스코어에만 활용된다
  • filter 타입
    • must와 같은 동작을 하지만 필터 컨텍스트로 동작하기 때문에 유사도 스코어에 영향을 미치지 않는다
    • 예/아니요 두 가지 결과만 제공할 뿐 유사도를 고려하지 않는다


4-9. 패턴 검색

  • 검색하려는 검색어가 길거나 검색어를 정확히 알지 못하는 경우 대략적인 키워드나 몇 개의 알파벳만 가지고 패턴을 이용해 검색
  • 용어 수준 쿼리에 해당하므로 SQL의 LIKE 검색처럼 원문에서 특정 문자열을 검색하는 용도로는 적합하지 않다
  • 패턴 검색은 많은 리소스를 사용하기 때문에 권장하지 않지만, 필요할 때 효율적으로 사용
  • 와일드카드 쿼리
    • * : 공백까지 포함하여 글자 수에 상관없이 모든 문자를 매칭할 수 있다
    • ? : 한 문자만 매칭할 수 있다
    • 검색하려는 용어의 맨 앞에 *와 ?를 사용하면 속도가 매우 느려지기 때문에 검색어 앞에는 사용하지 않아야 한다
  • 정규식 쿼리

    1
    2
    3
    4
    5
    6
    7
    
    {
      "query" : {
        "regexp" : {
          "customer_first_name.keyword" : "Mar."
        }
      }
    }
    
This post is licensed under CC BY 4.0 by the author.