5 minute read

ES 검섹앤진 만들기 (2) 엘라스틱 서치 쿼리

1. 간단한 쿼리

es 는 데이터 저장 혁식 뿐만 아니라, 조회 쿼리 부터 클러스터 설정등 모든 정보를 Json 형태로 주고 받는다.

이번장에선 이러한 쿼리들 사용 방법에 대해 알아본다.

1-1. URL

단일 도큐먼트 별로 고유한 URL 값을 갖는다. 도큐먼트에 접근하는 URL 의 구조는 아래와 같다.

http://<호스트>:<포트>/<인덱스>/_doc/<doc_id>

1.2. POST

데이터 입력시 PUT메서드 사용 동일한 URL에 다른 내용의 도큐먼트를 재입력 하는 경우 기존 도큐먼트는 삭제가 되고 새로운 도큐먼트로 덮어씌운다 이때 응답의 result는 create 대신 update 가 되므로 알아두도록 하자

또한 실수로 도큐먼트가 더퍼씌워지는 것을 방지하기 위해서는 _doc 대신 create 를 사용한다. (put _doc은 upsert 느낌) 이렇게 처리하면, 이미 있는 도큐먼트라면 409 입력 에러가 발생한다.

1-3 PUT

작업을 하다보면 POST 와 PUT 의 차이를 잘 모르고 사용하는 경우가 있다. 도큐먼트를 입력할때 POST 메서드롤 /_doc 까지만 입력하면 자동으로 임의의 id가 생성된다. 도큐먼트 id의 자동생성은 PUT 메서드로는 동작하지 않느다

  • update

    PUT을 사용하는 경우, __도큐먼트 전체가 새로운 값으로 __ 대치된다. 이전 도큐먼트에서 원하는 필드만 선택적으로 변경하고 싶은 경우 다음 URL을 사용하고, body에 doc 이라는 지정자를 사용하면 된다.

POST <인덱스>/_update/{doc_id}

그후 다시 GET 요청을 하면 _id 필드 아래 _version을 보면 2로 증가된것을 볼수 있다. 이는 단일 필드만 수정하는 경우에도 내부에서는 도큐먼트 내용을 가져와 _doc 에서 지정한 내용으로 변경한 새 도큐먼트를 put 으로 입력하는 작업을 진행하기 때문이다.

1-4 GET

다양한 정보가 함께 표시되며 문서의 내용은 _source 항목에 나타난다.

1-5 DELETE

도큐먼트 또는 인덱스 단위 의 삭제가 가능하다.

  • 하나의 도큐먼트 삭제

    DELETE test_index/_doc/{doc_id}
    
  • 전체 인덱스를 삭제

    DELETE test_index
    

    전체 인덱스를 삭제하고 index 에 GET 요청을 보낸다면 index_not_found_exception 이 발생한다.

  • 부분 삭제 쿼리 삭제

    ```text POST /test_index/_delete_by_query { “query”: { “match”: { “user.id”: “hsw90” } } }

test_index 모든 도큐먼트를 삭제 하는 방법, truncate 같이

POST /test_index/_delete_by_query { “query”: { “match_all”: {} } }



- #### 1-6. bulk api
여러 명령을 __배치로__ 수행하기 위해 _bulk api 사용이 가능하다 , index, create, update, delete 내용 입력이 필요없는 delete 제외 하고는 명령문과 데이터문을 한줄씩 순서대로 입력해야 한다.

모든 명령이 동일한 인덱스에서 수행되는 경우 아래와 같은 형식으로 사용 가능하다

POST test/_bulk {“index”: {“_id”:1}} {“field:”value one”}

대량의 데이터를 입력할 떄는 _bulk api를 사용해야 불필요한 오버헤드가 없다.

오버헤드란

주의!! es 에는 커밋이나 롤백 등의 트랜잭션 개념이 없다. _bulk 작업 수행중 동작이 중단되면 어느 동작까지 실행되었는지를 확인이 불가능 하다. 이런 경우 전체 인덱스를 삭제하고 처음부터 다시 작업을 하는것이 안전하다고 한다.


### 2. _search API

검색은 인덱스 단위로 이루어진다. GET {ndex}/_search 형식으로 사용하며 쿼리 입력하지 않으면 전체 도큐먼트를 찾는 match_all 검색을 한다.

GET test_index/_search { “query”: { “match_all”: {} } }

GET test_index/_search 는 동일하다.


- #### URI 검색
_search 뒤에 q 파라메터를 사용해 검색어를 입력가능하다. request url 에 검색어를 넣어 검색하는 방식을 
__URI 검색__ 이라고 한다.
  - GET test/_search?q=value

- URI 쿼리에서는 AND, OR ,NOT 의 사용이 가능하며 반드시 모두 대문자 입력을 해야한다.
  - GET test/_search?q=value AND three
- 특정 필드를 KEY, 검색어를 VALUE 로 하고 싶은 경우에는 key:value 골로 작성하면 된다.
  - GET test/_search?q=field:value


- #### _search 검색 결과
결과 출처 : https://www.elastic.co/guide/kr/elasticsearch/reference/current/gs-search-api.html

```json
{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : null,
    "hits" : [ {
      "_index" : "bank",
      "_type" : "account",
      "_id" : "0",
      "sort": [0],
      "_score" : null,
      "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
    }, {
      "_index" : "bank",
      "_type" : "account",
      "_id" : "1",
      "sort": [1],
      "_score" : null,
      "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
    }, 
    ]
  }
}
  • took : Elasticsearch가 검색을 실행하는데 걸린시간 (밀리초)
  • timed_out : 검색의 시간 초과 여부
  • _shard : 검색한 샤드 수 및 검색에 성공/실패한 샤드 수
  • hits : 검색 결과
  • hits.total : 검색 조건과 일치하는 문서의 총 개수
  • hits.hits : 검색 결과의 시제 배열 (기본 값은 10)
  • hits.sort : 결과 정렬 키 (점수 기준 정렬일 경우 표시 x)

3. Data Body 검색

  • 검색 쿼리를 바디에 입력하는 방식
  • QueryDSL 을 사용
  • Json 형식

  • Multitenancy

  • 여러 개의 인덱스를 한꺼번에 묶어서 검색이 가능
  • 쉼표 , 로 나열하거나 와일드카드 * 문자로 묶는다.

// 쉼표로 나열해서 여러 인덱스 검색 GET logs-2018-01,2018-02,2018-03/_search

// 와일드카드 * 를 이용해서 여러 인덱스 검색 GET logs-2018-*/_search

위 방법 대신 _all 지정자를 사용하여 GET _all/_search 와 같이 실행시 클러스터 내 모든 인덱스를 대상으로 검색이 가능하다. 
단 _all 불필요한 데이터까지 접근해 작업 부하를 초래하므로 되도록 사용 하지 말자.

4. 검색과 쿼리

  • 검색(Search)

    수많은 대상 데이터 중에 ‘조건’을 부합하는 데이터로 범위를 축소 하는 행위

    • es는 실제로 검색에 사용되는 검색어텀(Term) 으로 분석 과정을 거쳐 저장한다.
    • 검색 시 대소문자, 단수, 복수 상관없이 검색이 가능하다
    • 이러한 특징을 풀 텍스트 검색(FULL TEXT SEARCH) 전문 검색이라고 한다.
  • Query DSL (Domain Specific Language)
    • ES 의 쿼리 DSL 은 모두 JSON 형식으로 입력한다.

5. FULL TEXT QUERY

  • match_all

    해당 인덱스의 모든 도큐먼트를 검색한다. 검색시 쿼리를 넣지 않으면 자동으로 match_all 검색을 실시한다.

  • match

    Full Text Query에 사용되는 일반적인 쿼리

Get test_index/_search
{
"query: : {
  "match": {
    "message": "hello word"
  }
}
}

여러개의 검색어를 집어 넣으면 디폴트로 OR 조건 검색을 한다.

RDB SQL 로 본다면 대략
message like '%hello%' or message like '%word%'
와 같은 결과를 기대할 수 있다. 

만약 위 검색 조건을 AND로 변경하고 싶다면 operator 옵션을 사용한다.

{
"query": {
  "match": {
    "message": {
      "query" : "hello word",
      "operator" : "and" 
    }
  }
}
}
SQL: message like '%quick%' and message like '%dog%'

AND 조건이라 하여 like "%quick dog" 과는 조금 거리가 멀다
  • match_phrase

    그럼 like “%quick dog” 과 같은 효과를 내기 위해선 어떻게 해야 할까? 이럴때 match_phrase 를 사용한다. match_phrase 는 입력된 순서 까지 고려 하여 검색을 수행한다.

  • 이때 만약 __단어 사이에 다른 “검색어”를 지정된 값만큼 끼어드는 것” 을 컨트롤 하고 싶다면 slop 이라는 옵션을 사용한다.
GET test_index/_search
{
"query": {
  "message": {
    "match_phrase": {
        "query: "hello word",
      "slop": 1
    }
  }
}
}

결과

{
...
"_source": {
  "message": "hello elastic word"
} 
...
}

그런데 여기서 미리 공부를 해본사람은 그럼 term 쿼리와 match_phrase 의 차이가 없는거 아니야? 라고 할 수 있지만 이 둘은 analyze 를 하냐 안하냐 차이가 있다, wild card 검색 관련해서도, 이러한 기능들은 뒷 장에서 알아더 알아보도록 한다.

Bool 복합 쿼리 - Bool Query

  • query_string 은 여러 조건을 조합할수는 있지만 옵션이 한정적이다.
  • 그래서 여러 쿼리를 조합하기 위해 Bool query 를 사용할 수 있다.
  • 매우매우 중요한 내용!!!
GET <인덱스명>/_search
{
"query": {
  "bool": {
    "must": [
      { <쿼리> }, …
    ],
    "must_not": [
      { <쿼리> }, …
    ],
    "should": [
      { <쿼리> }, …
    ],
    "filter": [
      { <쿼리> }, …
    ]
  }
}
}

바로 위에서 말했듯 앞으로 할 예제에서 bool 쿼리의 인자 개념은 굉장히 중요하다. 반드시 숙지하고 사용하도록 하자

  1. must : 쿼리가 인 도큐먼트 검색
  2. must_not : 쿼리가__거짓__ 인 도큐먼트 검색
  3. should : 검색 결과 중 이 쿼리에 해당하는 도큐먼트 “점수” 를 증대!!
  4. filter : 쿼리가 인 도큐먼트를 검색한다 but 점수 x 점수를 계산하지 않는다. 그러므로 must 보다 검색 속도가 빠르고 캐싱이 가능하다
  • must 는 SQL 의 AND 연산자와 유사하게 동작하나, SQL의 OR과 정확하게 일치하는 동작하는 bool query 는 존재하지 않는다.
  • 표준 SQL 의 AND, OR 조건은 2개 조건값에 대한 이항 연산자
  • ES 의 must, must_not, should 등은 내부에 있는 각 쿼리들에 대해 참/거짓 으로 적용하는 단항 연산자
  이항연산자/단항연산자
  - 이항연산자 : 2개의 항을 대상으로 연산을 수행
  - 단항연산자 : 1개의 항을 대상으로 연산을 수행
public static void main(String[] args){
  // 이항 연산자
  int mod = 5 % 2;

  // 단항 연산자
  int a = 10;
  int num = a++;
}

표준sql&esbool