Elasticsearch Examples

etc 2018. 1. 10. 00:05

<Elasticsearch?>

엘라스틱 서치란 Lucene-Based Search engine으로, data를 저장하고 indexing하여 빠르게 search할수있다

search engine으로 활용하거나 Mongodb처럼 NoSQL로도 활용할 수 있다

NoSQL로 활용할 때의 장점은 RDB보다 서치 퍼포먼스와 scalability(확장성)가 뛰어나다는 것이다

또한 REST API를 기본적으로 제공한다

REST API는 아래 형식의 curl로 사용할 수 있다

curl -H "Content-Type: application/json" -X<method> <url> -d '<json data>'


<Elasticsearch 6 Breaking changes>

5버전에서는 index당 여러개의 type을 가지고있었는데, 6버전에서는 index-type 1:1 관계이다

이것은 mapping을 remove하기 위한 first step이라고 문서에서 언급하고 있다

여러개의 type을 create하려고하면 reject 메세지를 받게된다


<python client library>

필자는 python에서 elasticsearch를 사용하고 있다

python에는 client library로 크게 두가지가 있다

low-level인 elasticsearch-py

high-level인 elasticsearch-dsl

DSL은 Domain Specific Language를 의미한다

elasticsearch-dsl은 ORM을 사용할 수 있다는 점이 큰 차이이다

주의할 점은 elasticsearch server version과 client library version을 sync해야 한다는 점이다


<구조>

사용법을 보기전에, 기본적인 구조를 보자

Elasticsearch의 구조를 URL 형태로 살펴보자

http://server/index/doc_type/doc_id

server가 있고

그 아래에 여러개의 index들

그 아래에 1개의 doc_type (Elasticsearch 6버전은 index가 type을 한개씩만 가짐)

그 아래에 여러개의 doc_id들

이 존재한다

이것은 논리적 배치이고, 피지컬 배치는 node/shard가 있는데 아래 포스트를 참고하자

http://ssaemo.tistory.com/126


<document version field>

RDB에는 transaction이 있는데, Elasticsearch에서는 어떻게 처리해야할까?를 생각했었는데,

version field가 그 역할을 해준다

document의 몇가지 필드를 살펴보자

_id: document id

_source: document data fields

version: document's version

document가 create되면 version: 1이다

그리고 update 될때마다 version이 1씩 증가한다

만약 동시에 같은 document를 update하려고 한다면?

version: 1인 document에 2개의 update request가 발생했다고 하자

2개의 update 중 먼저 끝나는 것이 document의 version을 2로 update할 것이다

그러면 그 다음에 끝난 update는 version이 이미 2로 update되었으므로 document version conflict가 발생한다

즉, 누가 먼저 request를 보냈든 먼저 완료된 update가 적용된다

_update API의 conflict 옵션으로 proceed를 주면 이것을 무시하고 document를 update한다

이렇게 elasticsearch에서는 version field를 통해 동시요청을 처리할 수 있다


<Elasticsearch examples>

postman으로 아래 예제들을 매우 편리하게 테스트해볼 수 있다 (또는 curl)

단 GET 메서드에서는 body를 입력할수 없게 되어있으므로 POST 메서드를 사용하자

index, search, random scoring, aggregation를 설명할 것이다


Elasticsearch에서 RDB의 Insert는 Index에 해당한다

구조상의 index와 api상의 index는 다른 의미이고, RDB의 insert에 대응되는건 후자의 index이다

아래는 index API example이다

URL: /index/doc_type

body: {

"_id": <id>

"_source": {

"my_field": "my_value",

...

}

}

_id 필드가 없을 경우 랜덤하게 할당된다


또는

URL: /index/doc_type/doc_id

body: {

"my_field": "my_value",

...

}


index 예제를 매우 심플하게 살펴보았고 search example을 소개하겠다

elasticsearch의 핵심이고 매우 다양한 syntax들이 존재한다

우선 모든 document들을 검색하는 예제

URL: /index/doc_type/_search

body: {

"from": 0,

"size": 10,

"query": {

"match_all": {}

},

"sort": {

"my_field": "asc",

...

}

}

(sort field 형식이 저게 맞는지 가물가물하다)

이하 search 예제들의 URL은 모두 위와 같다

body를 아예 비우거나, {}를 보내거나, 위와 같이 보낼 경우 10개의 document를 response로 받을 수 있다

from, size, sort 필드는 옵션이고, 각 필드가 없을경우 기본값은 위와같이 0과 10이다 / sort 필드는 기본값이 _score - _id 순으로 정렬일 것이다 아마

query 필드가 있는데, 이 필드에 따라 search 결과가 결정된다

모든 문서 출력 예제를 보았으니, Elasticsearch의 filter를 소개하겠다. 이것은 RDB의 WHERE절에 대응되는 키워드이다

아래 documentation을 참고하였다

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-filtered-query.html

{

"query": {

"bool": {

"filter": {

"term": {

"my_field": "my_term",

...

}

}

}

}

}

굉장히 복잡해보이는데(callback hell같다) 하나하나 풀어보자

일부 document를 search하기위해 query 필드를 사용하였고

bool은 document search를 할때 True or False로만 판단하기 위해서 사용한 것이다

자세한 설명은 아래의 _score를 참고하자

filter는 위에서 말했듯 RDB의 WHERE절의 기능을 한다

term은 말그대로 단어를 검색하는 것이다. 특정 필드에서 특정 단어를 검색한다

term과 비슷한 것으로 match가 있는데, match는 완벽일치하는 것만 필터링하고

term은 일부분만 일치해도 될때도 있다. 이건 또 analyzer 관련 내용이므로 패스하고

현재 analyzer를 사용하지 않는 상태에서는 term과 match 둘다 완벽일치한 결과만 response한다

결과적으로 my_field의 값이 my_term인 document만 search 결과로 리턴될것이다


term 뿐 아니라 terms도 존재한다

{

"query": {

"bool": {

"filter": {

"terms": {

"my_field": ["my_term1", "my_term2", ...],

...

}

}

}

}

}

(기억을 더듬는거라 틀릴지도)

my_field의 값이 my_term1 OR my_term2인 document들을 리턴한다


위에서 _score를 언급하기로 했었다

각 document의 _score는 relevance를 의미하는데, 유사성/일치성이다

search의 sort 기본값은 search result를 _score 순으로 sorting 함으로써 가장 유사한 document를 최상단에 배치시킨다

어쨌든 elasticsearch는 search를 할때 각 document의 relevance를 계산한다

match_all 또는 bool search를 할때는 모든 document의 _score가 1 또는 null로 세팅된다

즉 bool 필드를 사용할 경우, _score를 계산하지 않는다

그 결과 속도가 더 빨라지고, caching도 가능하다고 한다

bool 필드는 오버헤드를 줄이기 위해 사용한 것이다

오버헤드의 핵심은 _score이고, 이것은 partial search에서 relevance 정보를 표현한다


analyzer로 간단하게 언급하자면, partial search를 할때 "Good bye"라는 값을 "good"과 "bye"로 나눔으로써

partial search를 가능하게 해주는 것이 바로 analyzer이다

각 필드마다 analyzer를 할당할 수 있다

필자는 NoSQL 용도로 사용하고 있으므로, 이정도까지 간단하게 이해하였다


random을 설명하겠다

random도 여전히 search의 일부 기능일 뿐이다

아래 레퍼런스를 참고했다

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html

{

"size": 10,

"query": {

"function_score": {

"query": {

"match_all": {}

},

"random_score": {}

}

random search의 원리는 score를 랜덤으로 할당한 후 result를 리턴하는 것이다

구조가 간단하진 않지만 사용하지 못할정도는 아니다


드디어 마지막 aggregation 예제이다

aggregation은 집계라고 직역되는데, document들의 doc_count 등을 통계내기 위해서 사용하는 것이다

user가 각 document들의 값에는 관심이 없고 오직 통계에만 관심이 있을 때 aggregation은 매우 유용하다

아래 레퍼런스를 참고헀다

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-daterange-aggregation.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-datehistogram-aggregation.html

{

"aggs": {

"my_agg": {

"date_range": {

"field": "my_date",

"format": "yyyy-MM-dd",

"ranges": [

{"to": "2017-06-08"},

{"from": "2017-06-08"}

]

}

}

}

}

아까처럼 하나하나 살펴보자

aggs는 aggregations의 축약어이다

my_agg에는 자신이 원하는 aggregation name을 입력하면 된다

field에는 aggregation에 사용할 field name

ranges는 list로서 2개의 아이템을 가지고 있는데, 이는 각각 bucket이 된다

첫번째 bucket은 2017-06-08 이전의 document들의 aggregation

두번째 bucket은 2017-06-08 이후의 document들의 aggregation


하지만 필자가 필요로했던 aggregation은 이것이 아니라 data histogram aggregation이었다

doc_count per day

{

"aggs" : {

"my_agg" : {

"date_histogram" : {

"field" : "my_date",

"interval" : "day",

"min_doc_count": 1

}

}

}

}

interval은 aggregation의 interval(간격)을 의미하고

min_doc_count은 최소 doc_count (optional field)

특정 기간 내의 data histogram만 필요하다면, 위에서 사용한 aggs 필드와 함께 query-bool-filter 필드를 사용하면 된다


추가로, script가 있는데 painless 등의 언어로 elasticsearch에서 script를 실행시킬 수 있다

기본적인 문법은 C언어와 아예 같으므로 어려울 것은 없다

그러나 document 갯수에 따라 exec time이 linear하게 증가하므로, 웬만하면 사용하지 않는 것이 좋을 것 같다 (주관적인 의견)



WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/