본문 바로가기

데이터베이스/ELK

[ELK] Elasticsearch DSL 검색 쿼리 문법

1. 엘라스틱서치 검색 쿼리 종류

전문 쿼리

전문 검색에 사용

인덱스 매핑 시 텍스트 타입으로 매핑을 권장

  • match : 가장 기본이 되는 검색
  • match_phrase : 용어의 순서까지 완전 일치하는 검색
  • multi_match : 다중 필드에서 검색. 필드에 가중치 부여 가능
  • query string : 연산자를 중심으로 텍스트를 분할하여 검색

 

용어 수준 쿼리

대소문자까지 정확히 일치하는 용어(term) 검색에 사용

  • term
  • terms
  • fuzzy : 유사한 알파벳까지 검색 쿼리 (한국어 x)

 

범위 쿼리

날짜, 숫자 등 범위에 지정한 검색에 사용

  • range 

 

논리 쿼리 (복합 쿼리 = bool)

쿼리들을 조합해 사용하는 쿼리

  • must : AND
  • must_not : 거짓
  • should : OR
  • filter : 예 / 아니오 (필터 컨텍스트로 동작하니 주의)

 

 

2. Query VS Filter

기본적으로 ES는 일치하는 검색 결과의 관련성을 점수 별로 정렬하여 각 문서가 쿼리와 얼마나 잘 일치하는지 측정하는 원리이다.

 

관련성 점수는 소수점 숫자이며, 검색 API의 _score 메타 필드에 반환된다. 조금 더 풀어보면 _score가 높을수록 문서의 관련성이 높아진다는 의미이다.

각 쿼리 유형은 관련성 점수를 다르게 계산할 수 있지만 _score 계산은 query나 filter 문에서 실행되는 것에 따라 달라진다.

 

Query

query문은 "이 문서가 이 쿼리 절과 얼마나 잘 일치하는가?" 라는 질의를 답한다. 문서가 일치하는지 여부를 결정하는 것 외에도 query문은 _score 메타 필드에서 관련성 점수를 계산한다.

 

Filter

filter문은 query문과 같이 "이 문서가 이 쿼리 절과 일치하는가?" 라는 질의를 답한다. 차이점은 _score를 계산하지 않고 간단하게 예 또는 아니요 로 계산한다.

filter문은 주로 구조화 된 데이터를 필터링하는 데 사용된다. 예를 들어 "등록 시간이 2018년에서 2019년 사이에 있는 데이터나 데이터의 상태 값이 삭제가 안된 데이터" 와 같이 문서의 _score가 필요없는 데이터 형태를 필터링할 때 사용된다. 또한, ES는 filter문을 사용하게 되면 자동으로 캐싱하여 성능을 향상시킨다.

_score를 계산하지 않아도 되기 때문에 query보다 검색이 빠르다.

 

 

  Query Filter
검색 결과 관련성 Yes or No
검색 범위 전문 검색 주어진 질의에 해당하는 정확한 값
캐시여부 No Yes
관련성 점수 계산 Yes No

 

 

3. 검색 쿼리 DSL 문법

index 데이터 검색 쿼리

GET 인덱스명/_search

기본적으로 10000개 이하 데이터만 조회 가능, 화면에는 10개만 표현된다.

 

1개의 index 데이터 조회 쿼리

GET 인덱스명/_doc/도큐먼트ID(데이터ID)

 

10000개 이상 index 데이터 검색 쿼리

GET 인덱스명/_search
{
	"query" : {
    	"match_all" : {}
    },
    
    "size": "1000000",
    "track_total_hits": true	# 10000개 이상 검색
}
"track_total_hits": true 없이 100000개 검색 시 에러 발생
illegal_argument_exception
Result window is too large, from + size must be less than or equal to: [10000] but was [100000].

 

 

예시

GET 인덱스명/_search 
{
  "query": {
    "bool": { (1)      
      "must": [{
        "match": {
          "title": "Search"
        }
      }, {
        "match": {
          "content": "Elasticsearch"
        }
      }],
      "filter": [ (2)
        {
        "term": {
          "status": "published"
        }
      }, {
        "range": {
          "publish_date": {
            "gte": "2015-01-01"
          }
        }
      }]
    }
  }
}

(1) bool 하위 두 개의 match 절은 query문을 사용하므로 각 문서가 얼마나 잘 일치하는지 _score를 매기는 데 사용된다.

(2) filter 하위의 term과 range 절은 필터 문에서 사용된다. 위 조건에 일치하는 문서는 _score에 영향을 미치지 않고 결과를 반환한다.

 

검색하고자 하는 용도에 따라 query와 filter를 유연하게 사용해야 좋은 성능을 낼 수 있다.

 

 

3. Query

query 용어를 설명하기 앞서 형태소 분석기(애널라이저)를 통해 문장을 아래와 같이 토큰으로 쪼갰다고 가정.

 

GET /test-index/_analyze 
{
  "analyzer": "analyzer_mixed",
  "text": "여러개의 물건들"
} {
  "tokens": [{
    "token": "여러",
    "start_offset": 0,
    "end_offset": 2,
    "type": "MM",
    "position": 0
  }, {
    "token": "개",
    "start_offset": 2,
    "end_offset": 3,
    "type": "NNB",
    "position": 1
  }, {
    "token": "물건",
    "start_offset": 5,
    "end_offset": 7,
    "type": "NNG",
    "position": 2
  }, {
    "token": "물건들",
    "start_offset": 5,
    "end_offset": 8,
    "type": "NNG",
    "position": 2
  }]
}

 

term

term은 형태소 분석기에 의해 쪼개진 토큰글을 기반으로 동작한다. 즉, term은 주어진 질의문과 저장된 형태소의 토큰이 정확하게 일치하는 문장을 찾는다.

GET /test-index/_search 
{
  "query": {
    "term": {
      "name": "여러"
    }
  }
}
// 토큰 중에 "여러"가 있으므로 검색 결과 있음 

GET /test-index/_search 
{
  "query": {
    "term": {
      "name": "여러개"
    }
  }
}
// 토큰 중에 "여러개"가 없고 "여러"와 "개"로 나눠져 있으므로 검색 결과 없음

이러한 이유로 검색하고자 하는 데이터 타입이 텍스트 필드라면 term 쿼리를 사용하는 것을 지양하고, match 쿼리를 사용하는 것이 좋다.

 

terms

term의 경우, 질의문이 1개만 가능하지만 terms는 여러 개의 질의문을 사용할 수 있다.

GET /test-index/_search
{
  "query": {
    "terms": {
      "name": ["여러", "개"]
    }
  }
}

 

match

기본 동작은 term과 동일하게 형태소 분석기에 의해 쪼개진 토큰들을 기반으로 동작한다. 차이점은 match는 주어진 질의를 형태소 분석기를 거쳐 쪼갠 다음 조회를 한다.

예를 들어, "여러개" 라는 값이 들어왔다면 형태소 분석기에 의해 "여러", "개"로 쪼개져 토큰을 조회하게 된다.

이때, 1개라도 존재하는 토큰이 있으면 결과를 반환한다. 즉, 형태소 분석기에 의해 쪼개질 때 질의들은 or 조건문을 사용해서 검색한다.

GET /test-index/_search 
{
  "query": {
    "match": {
      "name": "여러개"
    }
  }
}
// "여러개" 라는 질의를 형태소 분석기를 통해 "여러", "개"로 쪼갠 후, 검색. 
GET /test-index/_search 
{
  "query": {
    "match": {
      "name": {
        "query": "주름원",
        "operator": "and"
      }
    }
  }
}
// operator: "and"를 명시하면 쪼개진 질의문들이 전부 일치해야만 됨. 이 때, 중괄호가 한 번 더 들어가게 됨

 

bool

bool 쿼리는 다른 쿼리들을 조합하여 결과와 score들을 결합하거나 동작을 변경한다. 즉, bool 하위의 여러 쿼리들을 결합하여 결과를 낼 때 사용한다. bool은 쿼리와 필터에서의 동작이 상이하다. 쿼리로 사용할 때는 score가 결합되어 이치하는 절이 많을 수록 더 좋다.

 

bool의 기본 쿼리는 must, must_not, should가 있다.

  • must:  모든 필터가 매치되어야 함 (and)
  • filter: 위에서 설명한 filter와 동작은 동일. score를 무시하고 결과가 캐싱됨
  • must_not: 문사거 필터에 매치되지 않아야 함 (not). 필터 절에서 실행되므로 score가 무시되고 캐싱이 고려. score가 무시되므로 score는 0으로 반환.
  • should: 최소 minimum_should_match개의 필터에 매치되어야 함 (or)
    • minimum_should_match: should에서 최소 match 개의 수 (기본값 1 - must가 함께 사용되면 기본값 0)
GET /test-index/_search
{
  "query": {
    "bool": {
      "must": {
        "term": {
          "user": "kimchy"
        }
      },
      "filter": {
        "term": {
          "tag": "tech"
        }
      },
      "must_not": {
        "range": {
          "age": {
            "gte": 10,
            "lte": 20
          }
        }
      },
      "should": [{
          "term": {
            "tag": "wow"
          }
        },
        {
          "term": {
            "tag": "elasticsearch"
          }
        }
      ],
      "minimum_should_match": 1,
    }
  }
}

 

 

4. filter

 

filter 쿼리는 사실상 위의 bool에서 설명이 끝났다. 현재 ES에서는 filtered 문법이 사라지고 bool 쿼리를 사용하도록 변경되었다.

## ES 7.4 기준으로 더이상 사용하지 않음
GET /_search
{
  "query": {
    "filtered": {
      "query": {
        "match": {
          "text": "quick brown fox"
        }
      },
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  }
}
 ## 아래와 같이 사용해야함 GET /_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "text": "quick brown fox"
        }
      },
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  }
}

 

 

 

 

 

 

 

 

 

[참고]

https://brownbears.tistory.com/462

https://ithayoung.tistory.com/76