본문 바로가기
개발/ELK

[Elasticsearch] 검색 쿼리

by parkkingcar 2023. 4. 7.

 

 

다음 예제들과 인덱스는 ELK stack이 구축되어 있다는 가정하에, 키바나에서 제공하는 샘플데이터를 이용합니다.

 

키바나 홈 -> Get started by adding integrations

 

 

 

 

 

 

엘라스틱서치에서 쿼리를 사용하는 방법은 쿼리 스트링쿼리 DSL 두 가지가 있습니다. 쿼리 스트링은 한 줄 정도의 간단한 쿼리에 사용하고 쿼리 DSL은 한 줄에 넣기 힘든 복잡한 쿼리에 사용합니다.

 

 

먼저 쿼리를 작성하기 위해 엘라스틱 서치의 문자열에 대해 알아야 합니다. 5.x 버전부터 문자열 타입이 텍스트와 키워드 라는 두가지 타입으로 분리되었습니다. 텍스트 타입은 일반적으로 분석기에 의해 토큰으로 분리되고 역인덱싱을 합니다. 키워드 타입의 경우 문자열 전체가 하나의 용어로 인덱싱 됩니다. 자세한 내용은 아래 글을 참고해주세요.

 

7.2.1 문자열 - text, keyword - Elastic 가이드북

"ignore_above" : <자연수> - 디폴트는 2,147,483,647 이며 다이나믹 매핑으로 생성되면 ignore_above: 256 로 설정이 됩니다. 설정된 길이 이상의 문자열은 색인을 하지 않아 검색이나 집계가 불가능합니다. _s

esbook.kimjmin.net

 

 

쿼리스트링

쿼리 스트링은 REST API의 URI주소에 쿼리문을 작성하는 방식으로 실행해볼 수 있습니다.

 


쿼리 스트링 예시

GET kibana_sample_data_ecommerce/_search?q=customer_full_name:Mary

 

kibana_sample_data_ecommerce 인덱스 중 customer_full_name 필드에 'Mary'라는 용어가 포함된 도큐먼트를 검색합니다.



 

 

쿼리 DSL

쿼리 DSL은 REST API 요청 본문 안에 json형태로 쿼리를 작성합니다.

쿼리 DSL 예시

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "customer_full_name": "Mary"
    }
  }
}

 





매치 쿼리

매치쿼리는 대표적인 전문쿼리로 가장 기본이 되는 쿼리입니다. 검색하고자 하는 필드명을 알아야 사용할 수 있습니다. 

모르는 경우 GET 인덱스명/_mapping으로 인덱스에 포함된 필드가 어떤것들이 있는지 알 수 있습니다.

 


용어를 검색하는 매치쿼리 예시

GET kibana_sample_data_ecommerce/_search
{
  "size": 100, 
  "_source": ["customer_full_name"],
  "query": {
    "match": {
      "customer_full_name": "mary bailey"
    }
  }
}


이때 customer_full_name 를 보면 "mary bailey"는 분석기에 의해 [marry, bailey]로 토큰화 되어 공백은 OR로 인식하여 검색하게 됩니다.


쿼리를 살펴보면 size는 검색하는 결과 값의 개수를 정하고, _source는 검색하는 결과 값의 표기할 필드명을 정합니다. 

설정하지 않는다면 모든 경우의 값이 나오게 됩니다.

 

만약 'mary'와 'bailey'가 모두 포함된 도큐먼트를 찾기 위해서는 다음과 같이 "operator": "and"를 사용합니다.

GET kibana_sample_data_ecommerce/_search
{
  "size": 100, 
  "_source": ["customer_full_name"],
  "query": {
    "match": {
      "customer_full_name": {
        "query": "mary bailey",
        "operator": "and"
      }
    }
  }
}

 

 

 

 

매치 프레이즈 쿼리

매치쿼리와 마찬가지로 전문 쿼리의 한종류인 매치 프레이즈 쿼리는 구를 검색할때 사용합니다.

2개 이상의 단어가 모여서 뜻을 만드는 구를 검색할때 예를 들어, 검색어인 'mary bailey'가 매치 쿼리와 동일하게 토큰화 되지만 검색에 사용된 용어들이 모두 포함되면서 순서도 같아야 합니다.

 

 

매치 프레이즈 쿼리 예시

GET kibana_sample_data_ecommerce/_search
{
  "_source": ["customer_full_name"],
  "query": {
    "match_phrase": {
      "customer_full_name": "mary bailey"
    }
  }
}

 

 


 

용어 쿼리

용어쿼리는 용어 수준 쿼리의 대표적인 쿼리로 사용방법은 매치 쿼리와 비슷합니다.

용어 쿼리의 경우 검색어를 토큰화하지 않고 'mary bailey'를 검색하면 정확히 일치하는 용어가 있는 경우에만 매칭합니다.

매치 쿼리와 다르게 대소문자가 소문자로 변경되어 토큰화되지 않아 아래와 같이 대문자를 섞어서 검색하면 아무 결과도 나오지 않습니다.

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

 

이때  "customer_full_name"의 매핑은 텍스트와 키워드타입을 갖는 멀티 필드로 지정되어 있어 키워드 타입으로 검색하면 우리가 원하는 결과를 얻을 수 있습니다.

키워드와 텍스트 타입을 다루는 매핑은 따로 선행학습이 필요합니다.

 

7.2 매핑 - Mappings - Elastic 가이드북

인덱스의 매핑에서 필드들은 mappings 아래 properties 항목의 아래에 지정됩니다. 위 예제에서 보면 데이터 형식에 맞게 title, author, category 필드들은 text와 keyword타입으로, pages 필드는 long 타입으로, p

esbook.kimjmin.net

 

다음 쿼리문을 통해 kibana_sample_data_ecommerce 인덱스의 매핑을 볼 수 있습니다.

GET kibana_sample_data_ecommerce/_mapping

 

 

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

 

 

용어들 쿼리

용어들 쿼리는 용어 수준 쿼리의 일종으로 여러 용어들을 검색해줍니다.

키워드 타입으로 매핑된 필드에서 사용해야 합니다. 또한 대소문자도 신경 써야 검색이 가능합니다.

 

GET kibana_sample_data_ecommerce/_search
{
  "_source": ["day_of_week"],
  "query": {
    "terms": {
      "day_of_week": ["Monday", "Sunday"]
    }
  }
}

결과로는 1193개의 도큐먼트를 찾을 수 있고 해당하는 필드의 "monday'혹은 'Sunday'인 도큐먼트를 얻을 수 있습니다.

 

 

 

멀티 매치 쿼리

지금까지는 쿼리를 이용해 검색할때 반드시 필드명을 적어야 했습니다. 하지만 검색하고자 하는 용어나 구절이 정확히 어떤 필드에 있는지 모르는 경우는 여러개의 필드에서 검색해야 합니다.멀티 매치 쿼리는 1개 이상의 필드에 쿼리를 요청 할 수 있습니다.

 

GET kibana_sample_data_ecommerce/_search
{
  "_source": ["day_of_week"],
  "query": {
    "multi_match": {
      "query": "mary",
      "fields": [
        "customer_full_name",
        "customer_first_name",
        "customer_last_name"
      ]
    }
  }
}

검색하려는 필드가 많은 경우 다음과 같이 필드명에 *같은 와일드카드를 사용해 복수의 필드를 선택 할 수 있습니다.

GET kibana_sample_data_ecommerce/_search
{
  "_source": ["day_of_week"],
  "query": {
    "multi_match": {
      "query": "mary",
      "fields": "customer_*_name"
    }
  }
}

 

여러개의 필드중에 특정 필드에 가중치를 두는 방법이 있습니다. 이를 부스팅 기법이라 하고 다음과 같이 사용합니다.

GET kibana_sample_data_ecommerce/_search
{
  "_source": ["day_of_week"],
  "query": {
    "multi_match": {
      "query": "mary",
      "fields": [
        "customer_full_name^2",
        "customer_first_name",
        "customer_last_name"
      ]
    }
  }
}

 


 

범위쿼리

범위쿼리는 특정 날짜나 숫자의 범위를 지정해 범위 안에 포함된 데이터를 검색할 때 사용합니다.

키워드 타입의 데이터에는 범위 쿼리를 사용할 수 없습니다. 아래 예시와 같이 범위 쿼리를 사용할 수 있습니다.

 

날짜/시간 범위쿼리 예시

GET kibana_sample_data_flights/_search
{
  "query": {
    "range": {
      "timestamp": {
        "gte": "2020/12/15",
        "lte": "2020/12/16"
      }
    }
  }
}

위 쿼리를 실행하면 에러가 발생합니다. 쿼리에서 사용한 날짜/시간 포맷과 도큐먼트에 저장된 날짜/시간 포맷이 다르기 때문에 에러가 발생했습니다.

 

위 예시 인덱스의 timestamp 필드는 yyyy-mm-dd 형식의 날짜/시간 포맷을 사용하는데 범위 쿼리에서 날짜/시간 포맷을 yyyy/mm/dd 같은 형식으로 작성하면 오류가 발생합니다.

 

 

검색 범위를 지정하는 파라미터

  • gte: 1  | 1과 같거나 1보다 큰 값
  • gt: 1 | 1보다 큰 값
  • lte: 1 | 1과 같거나 1보다 작은 값
  • lt: 1 | 1보다 작은 값
  • 날짜의 경우 동일하게 적용합니다. ex)
  • gt: 2021-01-21 | 2021년1월 21일 이후의 날짜

 

 

추가적으로 현재 시간을 기준으로 날짜/시간 검색을 하는 경우 아래와 같이 now를 사용합니다.

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

위 예시는 timestamp필드에서 현재 시각을 기준으로 한 달 전까지의 모든 데이터를 가져옵니다.

 

 

 

 

날짜/시간 관련 범위 표현식

  • now | 현재시각
  • now + 1d | 현재시각 + 1일
  • now + 1h + 30m + 10s | 현재시각 + 1시, 30분 ,10초
  • 2021-01-21 || + 1M   2021-01-21 + 1달

 

범위에 포함되는 정도를 정하고 싶을 때, relation이라는 파라미터를 사용하면 어떤 범위를 얼만큼 포함할지 결정 할 수 있습니다.

GET kibana_sample_data_flights/_search
{
  "query": {
    "range": {
      "test_date": {
        "gte": "2020/12/15",
        "lte": "2020/12/16",
        "relation": "within"
      }
    }
  }
}

 

 

relation에 들어갈 수 있는 값 들

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

 

ex)

문서 / 숫자범위

1 / 10 ~19

2 / 20 ~29

3 / 30 ~ 39

4 / 40 ~ 49

 

쿼리 범위 값 / relation / 찾은 문서

15 ~ 45 / intersects / 1, 2, 3, 4

15 ~45 / within / 2, 3

25 ~ 29 / contains / 2

25 ~ 35 / contains  / x

 

 


논리 쿼리

논리 쿼리는 복합 쿼리로 위에 나온 쿼리를 조합할 수 있습니다.

예를 들어 ~~기간에 생성된 로그 중에서 상태가 error인 것들을 검색하는 경우, 매치 쿼리나 용어 쿼리를 단독으로 사용해서는 검색 할 수 없습니다.

 

 

논리 쿼리는 쿼리를 조합할 수 있도록 다음 4개의 타입을 지원합니다.

  • must | 쿼리를 실행하여 참인 도큐먼트를 찻는다. ( AND연산 )
  • must_not | 쿼리를 실행하여 거짓인 도큐먼트를 찾는다. 다른 타입과 같이 사용할 경우 도큐먼트에서 제외한다.
  • should | 단독으로 사용 시 쿼리를 실행하여 참인 도큐먼트를 찾는다. ( OR연산 )
  • filter | 쿼리를 실행하여 '예/아니요' 형식의 필터 컨텍스트를 수행한다.

 

하나의 쿼리를 사용하는 must 타입

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {"customer_first_name": "mary"}
      }
    }
  }
}

 

복수 개의 쿼리를 사용하는 must 타입

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {"day_of_week": "Sunday"}},
        {"match": {"customer_first_name": "mary"}}
      ]
    }
  }
}

AND 조건에 의해 day_of_week가 'Sunday'이면서 customer_first_name에 'mary'가 들어간 도큐먼트들만 검색합니다.

 

 

 

하나의 쿼리를 사용하는 must_not 타입

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "bool": {
      "must_not": {
        "match": {"customer_first_name": "mary"}
      }
    }
  }
}

 

 

하나의 쿼리를 사용하는 should 타입

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "bool": {
      "should": {
        "match": {"customer_first_name": "mary"}
      }
    }
  }
}

 

복수 개의 쿼리를 사용하는 should 타입

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "bool": {
      "should": [
        {"term": {"day_of_week": "Sunday"}},
        {"match": {"customer_first_name": "mary"}}
      ]
    }
  }
}

 

하나의 쿼리를 사용하는 filter 타입

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "products.base_price": {
            "gte": 30,
            "lte": 60
          }
        }
      }
    }
  }
}

 


 

패턴 검색

검색하려는 검색어가 길거나 검색어를 정확히 알지 못하는 경우가 있습니다. 검색어의 대햑적인 키워드나 몇 개의 알파벳만 알고 있을 경우 패턴을 이용해 검색 할 수 있습니다. 패턴을 이용한 검색은 와일드카드를 사용하는 와일드카드 쿼리와 정규 표현식을 사용하는 정규식 쿼리 두 가지 방법이 존재합니다. 패턴 검색은 많은 리소스를 사용하기 때문에 권장하지는 않지만, 필요할 때 효율적으로 사용하면 도움을 받을 수 있습니다.

 

 

와일드카드 쿼리

와일드카드 쿼리는 용어를 검색할 때 *와 ?라는 두가지 기호를 사용할 수 있습니다. *는 공백까지 포함하여 글자 수에 상관없이 모든 문자를 매칭할 수 있고, ?는 한 문자만 매칭할 수 있습니다. 검색하려는 용어의 맨 앞에 *와 ?를 사용하면 속도가 매우 느려지기 때문에 검색어 앞에는 사용하지 않아야 합니다. 

아래글은 wildcard 쿼리에 대한 참고내용입니다.

 

ElasticSearch 에서 wildcard 쿼리에 대한 이해

Software Developer, I love code.

findstar.pe.kr

 

 

 

'aabbcd' 라는 용어가 있을때 아래 와일드카드 기호를 사용함에 따라 다음과 같이 매칭이 됩니다. 

 

 ' * '  매칭 성공과 실패 케이스

  •  *   :  매칭성공
  •  aabbbcd* : 매칭성공
  •  b* : 매칭실패
  •  a*e : 매칭실패

 ' ? '  매칭 성공과 실패 케이스

  •  a??bb???  :  매칭성공
  •  a????cd : 매칭성공
  •  a? : 매칭실패
  •  aabbbcd? : 매칭실패

 

와일드카드 패턴 검색

GET kibana_sample_data_ecommerce/_search
{
  "_source": "customer_full_name",
  "query": {
    "wildcard": {
      "customer_full_name.keyword": "M?r*"
    }
  }
}

 

정규식쿼리

정규식 쿼리는 특정한 패턴을 가진 문자열을 표현하기 위한 형식 언어 입니다. 주의해야할 점은 와일드카드에서 사용한 *, ?기호가 정규식에서는 쓰임이 다르다는 점 입니다.

 

 

'aabbcd' 라는 용어가 있을때 아래 정규식 기호를 사용함에 따라 다음과 같이 매칭이 됩니다. 

 

 ' . '  정규식 매칭 성공과 실패 케이스

점(.)은 하나의 문자를 의미하고 어떤 문자가 와도 상관없이 매칭되었다고 판단합니다.

  • a.b..c.   :  매칭성공
  • .......   :  매칭성공
  • aab.  : 매칭실패
  • a.bbcd  : 매칭실패

 

 ' + '  정규식 매칭 성공과 실패 케이스

+ 기호는 + 기호 앞 문자와 같은 문자가 한 번 이상 반복되면 매칭되었다고 판단합니다.

  • aab+cd   :  매칭성공
  • a+b+c+d   :  매칭성공
  • aabbbcd+   :  매칭성공
  • aabbbcde+ : 매칭실패

 

 ' * '  정규식 매칭 성공과 실패 케이스

* 기호 앞 문자와 같은 문자가 0번, 혹은 여러 번 반복되면 매칭되었다고 판단합니다.

  • aab*cd  :  매칭성공
  • a*b*c*d  :  매칭성공
  • aabbbcde*  :  매칭성공
  • *bbbcd : 매칭실패

 

 ' ? '  정규식 매칭 성공과 실패 케이스

? 기호 앞 문자와 같은 문자가 0번, 혹은 한 번 나타나면 매칭되었다고 판단합니다.

  • aa?b?bbcd  :  매칭성공
  • aabbbcde?  :  매칭성공
  • aab?bcd  :  매칭성공
  • a?b??cd  :  매칭성공

 

정규식을 이용한 패턴 검색

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "regexp": {
      "customer_first_name.keyword": "Mar."
    }
  }
}

 

댓글