티스토리 뷰
본 문서는 공식 홈페이지 Elasticsearch 시작하기 를 보며 정리한 문서이다.
설치는 이전 글을 참고한다.
ELK Stack을 실행 후 키바나의 dev_tools 로 접속해 이곳에서 테스트를 진행한다.
문서 색인
우선 customer라는 인덱스에 식별자 "1", 이름이 "John Doe" 인 데이터를 삽입하고 조회한다.
PUT /customer/_doc/1
{
"name": "John Doe"
}
삽입한 결과는 다음과 같다.
{
"_index" : "customer", // index 정보
"_type" : "_doc", // 문서의 타입
"_id" : "1", // id = 1
"_version" : 4, // version 정보
"result" : "updated", // 결과
"_shards" : { // shard 정보
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 6, // 시퀀스 번호
"_primary_term" : 8
}
조회는 다음과 같다. 식별자가 1인 데이터를 조회한다.
GET /customer/_doc/1
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 4,
"_seq_no" : 6,
"_primary_term" : 8,
"found" : true,
"_source" : {
"name" : "John Doe"
}
}
검색 쿼리
검색을 위하여 다음 json파일을 저장한다. accounts.json 을 _bulk 요청으로 accounts.json을 elasticsearch에 저장한다.
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_bulk?pretty&refresh" --data-binary "@accounts.json"
먼저, accounts.json에서 하나의 레코드만 살펴보면 다음과 같이 구성되어 있다.
{"index":{"_id":"1"}}
{
"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"
}
모두 잘 들어 갔는지 확인한다.
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" }
]
}
'
쿼리를 살펴보면 보면 match_all: {} 이라는 구문이 들어가 있다. 쿼리 구조를 잘 모르는 상태에서 생각해보면 RDBMS에서
select * from table;
이라고 생각할 수 있을 것 같고, sort의 account_number : asc를 합쳐보면
select * from table order by account_number ASC;
같은 쿼리를 요청하는 듯한 느낌이다. elasticsearch에서도 sql을 지원하지만 현재 설치한 license에서는 sql을 지원하지 않아 나중에 확인해 보도록 한다.
응답 포멧은 다음과 같다.
took – Elasticsearch가 쿼리를 실행하는데 걸린 시간(밀리 초)
timed_out – 검색 요청 시간 초과 여부
_shards – 검색된 샤드 수와 성공, 실패 또는 건너 뛴 샤드 수에 대한 분석
max_score – 가장 관련성이 높은 문서의 점수
hits.total.value - 일치하는 문서가 몇개나 발견되었는지
hits.sort - 문서의 정렬 위치(관련성 점수로 정렬하지 않은 경우)
- 문서상에서는 hits.sort로 되어 있으나 쿼리 결과에는 hits.hits.{}.sort
hits._score - 문서의 관련성 점수(match_all 사용시 적용되지 않음)
결과
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1000,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "bank",
"_type" : "_doc",
"_id" : "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"
},
"sort" : [
0
]
},
...
]
}
}
차근히 결과에 대해서 분석해보면 다음과 같다.
took - 쿼리를 실행하는데 걸린 시간은 0.005초(5 밀리 초)
timed_out - 검색 요청 시간 초과 여부는 0으로 시간 초과가 되지 않았다.
_shards - 검색된 shard의 수는 ELK Stack 환경 구성 파일인 Docker-compose.yml 파일을 살펴보면
- discovery.type: single-node
- Single-node로 지정하여 shard의 수가 1개가 나온걸 확인할 수 있다.
max_score - 가장 관련성이 높은 문서의 점수에 대해서는 null값이 나왔는데 현재로써 추론하자면 account_number를 오름차순으로 정렬해 모든걸 가져오는 쿼리이다. 그래서 하나의 인덱스(bank)에서 가져와서 따로 점수를 매기지 못하는 듯한 느낌이 든다.(자세한건 확인해보아야 한다.)
hits.total.value - 1000개
Hits.sort - 이 내용은 따로 나와 있지 않다.
- hits.hits.{}.sort에 나와 있으며 account_number와 동일하다.
Hits._score - 쿼리에 match_all을 사용했으므로 적용되지 않아, 나오지 않는다.
처음 elasticsearch에 쿼리를 보면 account_number를 오름차순으로 모두 가져오라는 쿼리를 날렸는데 결과에는 10개만 가져온걸 확인할 수 있다. 이는 elasticsearch에서 기본적으로 10개만 가져온다. 추가로 데이터를 더 포함해서 결과를 받기 위해서는 다음과 같은 쿼리를 보내면된다.
GET /bank/_search?pretty
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" }
],
"from": 10,
"size": 10
}
from은 어디서 부터 가져올지, size는 갯수를 나타낸다.
주소가 mill lane인 데이터를 찾기 위한 쿼리는 다음과 같다.
GET /bank/_search
{
"query": { "match": { "address": "mill lane" } }
}
결과를 확인하면 총 19개의 데이터가 있다는걸 확인할 수 있다.
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 19,
"relation" : "eq"
},
"max_score" : 9.507477,
"hits" : [
{
"_index" : "bank",
"_type" : "_doc",
"_id" : "136",
"_score" : 9.507477,
"_source" : {
"account_number" : 136,
"balance" : 45801,
"firstname" : "Winnie",
"lastname" : "Holland",
"age" : 38,
"gender" : "M",
"address" : "198 Mill Lane",
"employer" : "Neteria",
"email" : "winnieholland@neteria.com",
"city" : "Urie",
"state" : "IL"
}
},
{
"_index" : "bank",
"_type" : "_doc",
"_id" : "970",
"_score" : 5.4032025,
"_source" : {
"account_number" : 970,
"balance" : 19648,
"firstname" : "Forbes",
"lastname" : "Wallace",
"age" : 28,
"gender" : "M",
"address" : "990 Mill Road",
"employer" : "Pheast",
"email" : "forbeswallace@pheast.com",
"city" : "Lopezo",
"state" : "AK"
}
},
...
]
}
}
쿼리는 "mill lane"이 포함된 데이터를 찾는 쿼리가 아닌 mill 이나 lane이 포함된 모든 데이터를 찾는 쿼리이다. "mill lane"이 포함되어 있는 주소를 찾기 위해서는 match가 아닌 match_phrase를 사용해야 한다.
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": { "match_phrase": { "address": "mill lane" } }
}
'
좀더 복잡한 쿼리를 하기 위해서는 bool을 사용한다.
GET /bank/_search
{
"query": {
"bool": {
"must": [
{ "match": { "age": "40" } }
],
"must_not": [
{ "match": { "state": "ID" } }
]
}
}
}
위 쿼리는 나이가 40세 이고, 사는곳이 ID(Idaho) 주가 아닌 사람을 찾는 쿼리이다.
다음 쿼리는 잔액이 20000이상 3000이하 인 사람을 찾는 쿼리이다.
GET /bank/_search?pretty
{
"query": {
"bool": {
"must": { "match_all": {} },
"filter": {
"range": {
"balance": {
"gte": 20000,
"lte": 30000
}
}
}
}
}
}
집계 쿼리
terms 키워드를 사용하여 RDBMS의 그룹핑을 할수 있다.
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
}
}
}
}
'
사용법은 위와 같다. 이 쿼리는 주에 대하여 그룹핑하고 해당 주에 사는 사람들에 대한 집계이다.
차근히 살펴보면 "aggs"는 elasticsearch 쿼리의 예약어이며 group_by_?? 은 ??에 어떤 필드, terms 안에는 어떠한 필드의 키워드로 그룹핑할지 쿼리를 정한다.
다음 쿼리는 주에 대하여 그룹핑하고 해당 주에 평균 balance를 계산하는 쿼리이다.
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
'
다음 쿼리는 주에 대하여 그룹핑하고 해당 주에 평균 balance를 계산, 평균 balance를 내림 차순하는 쿼리이다.
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"order": {
"average_balance": "desc"
}
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
'
'Elastic' 카테고리의 다른 글
Elasticsearch 란? (0) | 2021.02.28 |
---|---|
ELK Stack 설치 (docker) (0) | 2021.02.15 |
Elasticsearch, RDBMS, NoSQL 비교 (0) | 2021.02.08 |
ELK/Elastic Stack (0) | 2021.01.27 |