ES 검색엔진 만들기 (2) 엘라스틱 서치 쿼리
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 메서드롤
-
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 쿼리의 인자 개념은 굉장히 중요하다. 반드시 숙지하고 사용하도록 하자
- must : 쿼리가 참 인 도큐먼트 검색
- must_not : 쿼리가__거짓__ 인 도큐먼트 검색
- should : 검색 결과 중 이 쿼리에 해당하는 도큐먼트 “점수” 를 증대!!
- 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++;
}