본문 바로가기

Elasticsearch (ELK)/Elasticsearch

6. 쿼리

1. 풀 텍스트 쿼리

Match_all

// 쿼리 없이 실행
GET my_index/_search
{
  "query":{
    "match_all":{ }
  }
}


// match_all 쿼리로 실행
GET my_index/_search
{
  "query":{
    "match_all":{ }
  }
}

 

Match

GET my_index/_search
{
  "query": {
    "match": {
      "message": "dog"
    }
  }
}

만약 여러검색어를 아무 Operator 없이 검색하면 Default로 OR 조건으로 검색하게 된다 

GET my_index/_search
{
  "query": {
    "match": {
      "message": "quick dog"
    }
  }
}

만약 Operation를 넣기 원하면 operator field를 넣어주면 된다

GET my_index/_search
{
  "query": {
    "match": {
      "message": {
        "query": "quick dog",
        "operator": "and"
      }
    }
  }
}

Match_phrase

공백을 포함한 정확한 내용을 검색시 Match_phase를 사용한다.

GET my_index/_search
{
  "query": {
    "match_phrase": {
      "message": "lazy dog"
    }
  }
}

Slop을 이용해 검색어들 사이에 다른 단어가 끼어들게 만들 수 있다.

GET my_index/_search
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "lazy dog",
        "slop": 1
      }
    }
  }
}
// This will return "Lazy jumping dog" and "The quick brown fox jumps over the lazy dog"

Query_string

GET my_index/_search
{
  "query": {
    "query_string": {
      "default_field": "message",
      "query": "(jumping AND lazy) OR \"quick dog\""
    }
  }
}

 

2. Bool 복합 쿼리

 

  • must : 쿼리가 참인 도큐먼트들을 검색합니다.

  • must_not : 쿼리가 거짓인 도큐먼트들을 검색합니다.

  • should : 검색 결과 중 이 쿼리에 해당하는 도큐먼트의 점수를 높입니다.

  • filter : 쿼리가 참인 도큐먼트를 검색하지만 스코어를 계산하지 않습니다. must 보다 검색 속도가 빠르고 캐싱이 가능합니다.

 

GET <인덱스명>/_search
{
  "query": {
    "bool": {
      "must": [
        { <쿼리> }, …
      ],
      "must_not": [
        { <쿼리> }, …
      ],
      "should": [
        { <쿼리> }, …
      ],
      "filter": [
        { <쿼리> }, …
      ]
    }
  }
}

// bool 쿼리로 quick 그리고 "lazy dog" 검색
GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "quick"
          }
        },
        {
          "match_phrase": {
            "message": "lazy dog"
          }
        }
      ]
    }
  }
}

bool 쿼리로 quick 그리고 "lazy dog" 가 포함되지 않은 문서 검색
GET my_index/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "message": "quick"
          }
        },
        {
          "match_phrase": {
            "message": "lazy dog"
          }
        }
      ]
    }
  }
}

SQL 과 Bool 비교 1

 

SQL 과 Bool 비교 2

 

3. 정확도 - Relevancy

Elasticsearch는 검색 결과가 입력된 조건과 얼마나 일치하는지 계산하는 알고리즘을 가지고 있으며 

이를 이용해 계산된 정확도를 기반으로 사용자가 가장 원하는 겨로가를 먼저 보여줄 수 있다.

 

스코어(Score) 점수

Elasticsearch는 BM25라는 수식을 이용하여 점수를 계산한다.

BM25 계산식 - 출처: https://en.wikipedia.org/wiki/Okapi_BM25

복잡해 보이는 이 계산에는 크게 TF, IDF 그리고 Field Length 총 3가지 요소가 사용된다.

 

  • TF(Term Frequency) :
    해당 Document에 얼마나 많은 검색어(Term)가 들어있는지 
  • IDF(Inverse Document Frequency) :
    검색어를 포함하고 있는 Document가 많을수록 점수가 줄어듬 (많이 사용된 단어일수록 희소성이 줄어들기 때문)
  • Field Length :
    Document의 길이가 적을수록 더 많은 점수가 늘어남

4. Bool : Should

특정 단어에 가중치를 줘서 상위로 올릴수 있다 이 때 Should를 사용하면 되겠다.

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "fox"
          }
        }
      ],
      "should": [
        {
          "match": {
            "message": "lazy"
          }
        }
      ]
    }
  }
}

좀 더 활용하자면 should안에 match_phrase를 사용함으로 특정 phrase를 위로 올리고 나머지는 아래에 검색하게 만들 수 있다.

 

(예: 스키 장갑을 상단으로 그리고 다른종류의 장갑을 하단으로)

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": {
              "query": "스키 장갑"
            }
          }
        }
      ],
      "should": [
        {
          "match_phrase": {
            "message": "스키 장갑"
          }
        }
      ]
    }
  }
}

이렇게 shouldmatch_phrase를 응용하면 쇼핑몰에서 "스키 장갑" 같은 단어로 검색했을 때 스키 용품들과 각종 장갑들을 모두 가져오면서 그 중 스키 장갑을 가장 상위에 표시할 수 있습니다. slop:1을 이용하면 "스키 보드 장갑", "스키 벙어리 장갑" 같이 스키와 장갑 사이에 다른 값이 들어간 결과에도 가중치를 부여할 수 있습니다.

 

5. 정확한값 쿼리

Filter

참 / 거짓 여부만 판별해서 결과를 가져오는것이 가능하다(한마디로 점수에 영향을 주지 않고 추가적인 검색어를 설정할수 있다)

e.g. 보통 쇼핑몰에서 검색어로 정확도가 높은 상품명을 검색하면서 생산 업체를 다시 필터링 하는 등의 용도로 사용이 가능하다. 

// 1
GET my_index/_search
{
  "query": {
    "match": {
      "message": "fox"
    }
  }
}

// 2 - 3번과 같은 결과이지만 정확도가 더 높다.
GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "fox"
          }
        },
        {
          "match": {
            "message": "quick"
          }
        }
      ]
    }
  }
}

// 3 - 2번과 같은 결과이지만 정확도는 1번과 같다
GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "fox"
          }
        }
      ],
      "filter": [
        {
          "match": {
            "message": "quick"
          }
        }
      ]
    }
  }
}
// must 로 fox 검색 후 must_not 으로 dog 제거
GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "fox"
          }
        }
      ],
      "filter": [
        {
          "bool": {
            "must_not": [
              {
                "match": {
                  "message": "dog"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

 

Keyword 

문자열, 공백, 대소문자와 정확히 일치하는 데이터만을 가져온다

GET my_index/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "message.keyword": "Brown fox brown dog"
          }
        }
      ]
    }
  }
}

 

keyword 타입으로 저장된 필드는 스코어를 계산하지 않고 정확값의 일치 여부만을 따지기 때문에 스코어가 "_score" : 0.0 으로 나오게 됩니다. 스코어를 계산하지 않기 때문에 keyword 값을 검색 할 때는 filter 구문 안에 넣도록 합니다.

 

6. 범위 쿼리

숫자 검색

Range Query는 "range : { <필드명>: { <파라메터>:<값> } }"으로 입력되며 4가지 Parameter를 사용합니다.

 

  • gte (Greater-than or equal to) - 이상 (같거나 큼)

  • gt (Greater-than) – 초과 (큼)

  • lte (Less-than or equal to) - 이하 (같거나 작음)

  • lt (Less-than) - 미만 (작음)

 

GET phones/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 700,
        "lt": 900
      }
    }
  }
}

 

날짜 검색

기본적으로 Elasticsearch 에서 날짜 값은 2016-01-01 또는 2016-01-01T10:15:30 과 같이 JSON 에서 일반적으로 사용되는 ISO8601 형식을 사용합니다.

GET phones/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "2016-01-01"
      }
    }
  }
}

 

Format을 사용하여 여러 포맷을 이용할 수 있습니다.

// date 값이 2016-01-01 ~ 2018-01-01 사이의 데이터 검색
GET phones/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "31/12/2015",
        "lt": "2018",
        "format": "dd/MM/yyyy||yyyy"
      }
    }
  }
}

//date 값이 2016-01-01 의 6개월 후 부터 오늘 (2019-09-03) 의 365일 이전 사이 값 검색
GET phones/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "2016-01-01||+6M",
        "lt": "now-365d"
      }
    }
  }
}

range 쿼리의 스코어는 모두 "_score" : 1.0로 동일하다. 이를 통해 알 수 있는것은 range 쿼리는 기본적으로 정확도를 계산하지 않는다.