ELK공부 - 엘라스틱 스택 개발부터 운영까지 (3)

HJ·2024년 3월 26일
0

시작

이번에는 본격적으로 elasticsearch에 데이터를 인출하는 쿼리에 대해 공부했다.
기본적으로 쿼리를 사용하는 방법은 쿼리 스트링 , 쿼리 DSL로 나뉘는데 한줄에 넣기 복잡한 쿼리에서는 DSL을 사용한다. 따라서 이번 포스팅에서는 다양한 DSL방법들에 대해 다뤄보겠다.

검색엔진

엘라스틱서치 검색엔진은 쿼리 컨텍스트 , 필터 컨텍스트크게 두가지로 나뉜다. 간단하게 차이는 쿼리 컨텍스트는 질의에 대한 유사도를 계산해 이를 기준으로 더 정확한 결과를 먼저 보여준다.예를들면 '엘라스틱'이 포함되어 있는지를 찾을 떄 연관성을 계산해 최대한 비슷한 도큐먼트를 찾아준다.

반면에 필터 컨텍스트는 예/아니오 결과 제공한다. 예를들면 제목이 '엘라스틱'이 맞는지 아닌지에 대한 결과를 얻을때 유용하다. 따라서 전체적인쿼리 속도 올릴수있다. 보통 단독으로 사용보다는 쿼리/필터를 조합해 사용 많이한다.

쿼리 DSL

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "customer_full_name": "Mary"
    }
  }
// 여기에 "explain": true 를 추가하면 good
}

가장 일반적인 DSL로 위와같이 실행하면 customer_full_name에 'Mary'가 포함된 모든 결과를 반환한다.

스코어 알고리즘(BM25) 계산 방식

일반적으로 쿼리 컨텍스트 방식일때 스코어가 계산이 되는데 이 스코어를 계산하기 위해 사용되는 알고리즘이BM25알고리즘이다. BM25알고리즘TF-IDF개념에 문서 길이를 고려한 알고리즘이다. 검색어가 문서내 얼마나 자주 나타나는지 , 얼마나 중요한 용어인지등을 판단하는 근거를 제공해준다. BM25알고리즘 계산방식은 다음과같다.

<도큐먼트와 쿼리간의 연관성 수치를 계산>
1. IDF계산 : 전체 문서에서 자주 발생하는 단어일수록 중요하지 않은 단어로 인식하고 가중치를 낮춤 계산식 : log(1 + (N-n+0.5) / (n+0.5))
(n: 검색했던 용어가 몇개의 도큐먼트에 있는지 , N:인덱스의 전체 도큐먼트 수)
2. TF계산 : 특정 용어가 도큐먼트에서 많이 반복되었는지
계산식 : freq / (freq + k1 * (1-b+b*dl / avgdl))
(freq:도큐먼트내에서 용어가 나온 횟수 , k1,b:가중치 , dl:필드길이)
3. 최종 계산 : TF x IDF x boost

쿼리 종류

  • 리프쿼리 : 특정 필드에서 용어를 찾는 쿼리 (매치,용어,범위쿼리)
  • 복합쿼리 : 쿼리를 조합해 사용되는 쿼리 (논리 쿼리)

리프퀴리 - 전문쿼리 , 용어 수준 쿼리

전문쿼리

  • 텍스트가 많은 필드에서 특정 용어 검색할떄사용 (인덱스 매핑시 텍스트 타입으로 매핑해야함)
  • 분석기에 의해 토큰화가 되고 매칭 (매치 쿼리는 전문 쿼리의 가장 대표적인 쿼리)
GET kibana_sample_data_ecommerce/_search
{
  "_source" : ["customer_full_name"],
  "query": {
    "match": {
      "customer_full_name": "marry bailey"
    }
  }
}

해석 : customer_full_name에서 "marry"bailey가 하나라도 포함된 매칭 soure파라미터는 customer_full_name필드만 보여달라는뜻

# 매치 프레이즈 쿼리(구 검색)
GET kibana_sample_data_ecommerce/_search
{
  "_source" : ["customer_full_name"],
  "query": {
    "match_phrase" : {
      "customer_full_name": "marry bailey"
    }
  }
}

해석 : "marry""bailey"을 모두 포함하면서 순서도 맞아야함

용어 수준 쿼리

  • 정확한 용어를 검색할때 사용 (키워드 타입으로 매핑해야함)
  • 분석기를 거치지 않으므로 대소문자도 맞아야함
GET kibana_sample_data_ecommerce/_search
{
  "_source" : ["customer_full_name"],
  "query": {
    "term": {
      "customer_full_name": "Mary"
    }
  }
}

해석 : customer_full_name은 분석기에 의해 대문자가 소문자로 변경되어 [marry,bailey]등으로 매핑되어 있는데 "Marry"를 찾으므로 매핑 x

GET kibana_sample_data_ecommerce/_search
{
  "_source" : ["customer_full_name"],
  "query": {
    "term": {
      "customer_full_name.keyword": "Mary Bailey"
    }
  }
}

해석 : 따라서 위와같이 매핑값을 보고 멀티인덱스를 활용해 매핑 시켜야함
(매핑을 보면 customer_full_namedms 은 텍스트 타입 , customer_full_name.keyword는 키워드 타입인데 customer_full_name.keyword를 보고 매핑을 시켜야함)

리프퀴리 - 매치 쿼리

  • 필드명을 모를때 여러개의 필드에서 검색하기 위한 쿼리(여러개에서 검색가능)
GET kibana_sample_data_ecommerce/_search
{
  "_source" : ["customer_full_name","customer_last_name","customer_first_name"],
  "query": {
    "multi_match": {
      "query" : "mary",
       "fields": "customer_*_name"  
    }
  }
}

해석 : customer_로 시작하고 _name으로 끝나는 모든 필드에서 'mary'라는 용어로 매치 쿼리 진행

# 멀티 매치 쿼리 필드에 가중치 두기
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "multi_match": {
      "query" : "mary",
       "fields": "customer_full_name^2",
	     "customer_last_name",
	     "customer_first_name"
     ]
    }
  }
}

해석 : customer_full_name에서 얻은 스코어가 2배 더 높게 책정된다

범위 쿼리

  • 특정 날짜나 숫자의 범위를 지정해 범위 안에 포함된 데이터를 검색할때 사용. 문자형,키워드 타입은 범위 쿼리를 사용x
GET kibana_sample_data_flights/_search
{
  "query": {
    "range": {
      "timestamp": {
        "gte": "2020-12-15",
        "lt": "2020-12-16"
      }
    }
  }
}

해석 : timestamp필드에서 2020-12-15~ 2020-12-16 데이터 찾음
get : 같거나 큰값 / ge : 큰값 / lte : 같거나 작은값 / lt : 작은값

GET kibana_sample_data_flights/_search
{
  "query": {
    "range": {
      "timestamp": {
        "gte": "now-1M"
      }
    }
  }
}

해석 : 현재 시각 기준으로 한달전까지 모든 데이터 가져옴

now+1d : 현재시간 +1일
mow+1h+30m+10s : 현재시간 +1 1시30분10초
2021-01-21+1M : 2021-01-21 + 1달

논리 쿼리

  • 쿼리를 조합할 수 있도록 4개의 타입을 지원
  • 예를들면 '2021년 1월'에 생성된 로그중에서 '상태가불량'인 것들을 검색하는등 할떄
논리 쿼리 포맷
GET <index>/_search
{
  "query": {
    "bool": {
      "must": [
        {쿼리문},
      ],
      "must_not": [
        {쿼리문},
      ],
      "should": [
        {쿼리문},
      ],
      "filter": [
        {쿼리문},
      ]
    }
  }
}

-> must: 쿼리를 실행하여 참인 도큐먼트를 찾음 , 복수의 쿼리를 실행하면 and 연산
-> must_not : 쿼리를 실행하여 거짓인 도큐먼트 찾음 , 다른 타입과 같이 사용할 경우 도큐먼트에서 제외
-> should : 쿼리를 실행하여 참인 도큐먼트를 찾음 , 복수의 쿼리를 실행하면 or 연산, 다른타입과 같이 사용할경우 스코어에만 활용
-> filter : 쿼리를 실행하여 '예/아니요' 형식의 필터 컨텍스트를 수행

# must타입 예시 # 
GET kibana_sample_data_ecommerce/_search
{
  "_source" :["day_of_week","customer_full_name"],
  "query":{
    "bool":{
      "must": [
        {"term": {"day_of_week":"Sunday"}},
        {"match":{"customer_full_name":"mary"}}
      ]
    }
  }
}

해석 : 용어쿼리+매치쿼리 형태인데 day_of_week"Sunday"면서 customer_full_name"mary"가 들어간 도큐먼트만 검색

# must not 예시 # 
GET kibana_sample_data_ecommerce/_search
{
  "_source" :["customer_full_name"],
  "query":{
    "bool":{
      "must": {
        "match":{"customer_first_name":"mary"}
      },
      "must_not": {
        "term":{"customer_last_name":"baily"}
      }
    }
  }
}

해석 : customer_full_name을 검색하는데 customer_first_name"marry"가 들어가고
customer_last_name"bailey"가 들어가지 않은 도큐먼트

# should타입 예시 #
GET kibana_sample_data_ecommerce/_search
{
  "_source" :["day_of_week","customer_full_name"],
  "query":{
    "bool":{
      "should": [
        {"term": {"day_of_week":"Sunday"}},
        {"match":{"customer_full_name":"mary"}}
      ]
    }
  }
}

해석 : must와 똑같은데 복수로 사용할시 or 효과

# must , should같이 사용 # 
GET kibana_sample_data_ecommerce/_search
{
  "_source" :["customer_full_name","day_of_week"],
  "query":{
    "bool":{
      "must": {
        "match":{"customer_first_name":"mary"}
      },
      "should": {
        "term":{"day_of_week":"Monday"}
      }
    }
  }
}

해석 : customer_first_name'marry'가 들어간 모든 도큐먼트를 검색하는데
day_of_week"monday"인 도큐먼트들의 우선순위 높임. (should를 사용해 검색순위 최적화 가능)

filter 타입

  • must와 같은 동작을 하지만 필터 컨텍스트로 동작하기 때문에 유사도 스코어에 영향을 미치지 않음
  • must와 같이 사용시 불필요한것을 필터링 하고(filter) 특정 조건인것을 검색할수있다(must)
GET kibana_sample_data_ecommerce/_search
{
  "_source": ["day_of_week","customer_full_name"],
  "query": {
    "bool":{
      "filter":{
        "term":{"day_of_week":"Sunday"}
      },
      "must":{
        "match":{"customer_full_name":"mary"}
      }
    }
  }
}

해석 : day_of_week"Sunday"인것을 먼저 필터링하고 customer_full_name"mary"인것을 검색 , 불필요한 스코어 계산을 줄일 수 있음

마무리

이번 포스팅에서는 엘라스틱서치에서 사용하는 DSL에 대해 다뤄보았다. 이제 다음 포스팅에서는 엘라스틱서치에 주요 핵심 기능인 집계에 대해 다뤄볼 예정이다. 많이 부족하지만 하나씩 배워가자 !

profile
데이터 공부 기록

0개의 댓글

관련 채용 정보