이번에는 본격적으로 elasticsearch에 데이터를 인출하는 쿼리에 대해 공부했다.
기본적으로 쿼리를 사용하는 방법은 쿼리 스트링
, 쿼리 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알고리즘
은 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배 더 높게 책정된다
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달
논리 쿼리 포맷
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를 사용해 검색순위 최적화 가능)
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
에 대해 다뤄보았다. 이제 다음 포스팅에서는 엘라스틱서치에 주요 핵심 기능인 집계에 대해 다뤄볼 예정이다. 많이 부족하지만 하나씩 배워가자 !