ElasticSearch Search API
필터 등 기본적인 검색 방법에 대해 소개. RDBMS 에서 쿼리한 것과 비교해보면서 ElasticSearch에서 쿼리해보면 좋을듯. Query client 는 Kibana Dev Tools 을 사용한다.
테스트 데이터
tpc-H 에서 생성된 데이터 중 supplier 테이블 데이터를 바탕으로 테스트 진행.
supplier index template
정수형, 문자형, 텍스트 형에 따라 검색 특성을 보기 위해 아래와 같이 index template 을 먼저 생성한다.
curl -X PUT "node7.dat:9200/_template/template_supplier?pretty" -H 'Content-Type: application/json' -d'
{
"template" : "supplier*",
"mappings" : {
"doc": {
"properties": {
"s_suppkey": {
"type": "long"
},
"s_name": {
"type": "keyword"
},
"s_address": {
"type": "text"
},
"s_nationkey": {
"type": "integer"
},
"s_phone": {
"type": "keyword"
},
"s_accbal": {
"type": "double"
},
"s_comment": {
"type": "text"
},
"timestamp": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
},
"settings": {
"number_of_shards": 6,
"number_of_replicas": "0",
"refresh_interval": "120s"
}
}
'
데이터 인덱싱
아래 파일을 내려받아서
supplier/
supplier/supplier.tbl.1
supplier/supplier.tbl.10
supplier/supplier.tbl.11
supplier/supplier.tbl.12
supplier/supplier.tbl.13
supplier/supplier.tbl.14
supplier/supplier.tbl.15
supplier/supplier.tbl.16
supplier/supplier.tbl.2
supplier/supplier.tbl.3
supplier/supplier.tbl.4
supplier/supplier.tbl.5
supplier/supplier.tbl.6
supplier/supplier.tbl.7
supplier/supplier.tbl.8
supplier/supplier.tbl.9
supplier/supplier.template.sh
supplier/supplier.post.sh
supplier/supplier.meta
supplier/supplier.post.log
es_bulk.py
supplier.post.sh
스크립트를 실행하여 초기 데이터를 인덱싱할 수 있다.
검색
필드 선택
- SQL
select s_name,s_address from supplier
- DSL
GET /supplier/_search
{
"query": { "match_all": {} },
"_source": ["s_name", "s_address"]
}
match
검색 대상 필드가 숫자인 경우
- SQL
select * from supplier
where s_suppkey = 20
- DSL
GET /supplier/_search
{
"query": { "match": { "s_suppkey": 20 } }
}
검색 대상 필드가 Keyword인 경우
- SQL
select * from supplier
where s_phone = '31-720-790-5245'
- DSL
GET /supplier/_search
{
"query": { "match": { "s_phone": "31-720-790-5245" } }
}
검색 대상 필드가 Text인 경우
이러한 검색에서 ElasticSearch 의 강점이 나타나는데, 검색 조건 text를 analyze 하여 나온 결과 term 들을 이용하여 검색을 수행한다. ElasticSearch에서 text field에 대해 다음과 같은 문장을 검색하면
“instructions integrate sometimes slyly pending instructions”,
SQL로 표현했을 때는 like 필터를 여러개 묶은것 과 같다.
- SQL
select * from supplier
where s_address like '%instructions%'
and s_address like '%integrate%'
and s_address like '%sometimes%'
and s_address like '%slyly%'
and s_address like '%pending%'
- DSL
GET /supplier/_search
{
"query": {
"match": {
"s_comment": {
"query": "instructions integrate sometimes slyly pending instructions",
"operator": "and"
}
}
}
}
range
- SQL
select * from supplier
where s_suppkey >= 20
and s_suppkey < 40
- DSL
GET /supplier/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"s_suppkey": {
"gte": "20",
"lt": "40"
}
}
}
]
}
}
}
다중 조건
Bool Query
작은 쿼리들을 묶어서 큰 쿼리로 만들어낼 수 있다.
bool-must
must 를 이용했을 때에는 하위 조건들이 모두 일치(and 연산)해야만 한다.
- SQL
select * from supplier
where s_suppkey >= 20
and s_suppkey < 40
and s_address like '%ssetugTcXc096qlD7%'
- DSL
GET /supplier/_search
{
"query": {
"bool": {
"must": [
{ "range": { "s_suppkey": {
"gte": "20",
"lt": "40"
}}},
{ "match": { "s_address": "ssetugTcXc096qlD7" } }
]
}
}
}
bool-should
should 를 사용하면 아래 조건들이 or 연산으로 묶인다.
- DSL
GET /supplier/_search
{
"query": {
"bool": {
"should": [
{ "range": { "s_suppkey": {
"gte": "20",
"lt": "40"
}}},
{ "match": { "s_address": "ssetugTcXc096qlD7" } }
]
}
}
}
bool-must_not
- SQL
select * from supplier
where s_suppkey >= 20
and s_suppkey < 40
and s_address not like '%ssetugTcXc096qlD7%'
- DSL
GET /supplier/_search
{
"query": {
"bool": {
"must": [
{ "range": { "s_suppkey": {
"gte": "20",
"lt": "40"
}}}],
"must_not": [
{ "match": { "s_address": "ssetugTcXc096qlD7" } }]
}
}
}
aggr
- SQL
select s_nationkey
, avg(s_acctbal)
, count(s_suppkey)
, min(s_suppkey)
, max(s_suppkey)
, avg(s_suppkey)
, sum(s_suppkey)
from supplier
where s_suppkey <= 10000
group by s_nationkey
- DSL
GET supplier/_search
{
"size": 0,
"query": {
"range": {
"s_suppkey": {
"from": null,
"to": "10000",
"include_lower": false,
"include_upper": true,
"boost": 1
}
}
},
"_source": false,
"stored_fields": "_none_",
"aggregations": {
"groupby": {
"composite": {
"size": 1000,
"sources": [
{
"s_nationkey": {
"terms": {
"field": "s_nationkey",
"missing_bucket": false,
"order": "asc"
}
}
}
]
},
"aggregations": {
"acctbal_avg": {
"avg": {
"field": "s_acctbal"
}
},
"suppkey_stats": {
"stats": {
"field": "s_suppkey"
}
}
}
}
}
}
aggregations.stats 를 호출하면 count,min,max,avg,sum 을 모두 던져준다.
script
앞선 aggregation 쿼리에서 나아가 sum( (1-field_A) * field_B )
값을 구하기 위해서는 script 를 이용하면 된다.
nested operation (?) 에 대한 좀 더 직관적인 쿼리가 있으면 그걸 써봐야할 것 같은데 script 밖에 못 찾음.
- SQL
select s_nationkey
, sum((1-s_acctbal)*s_suppkey)
from supplier
where s_suppkey <= 10000
group by s_nationkey
- DSL
GET supplier/_search
{
"size": 0,
"query": {
"range": {
"s_suppkey": {
"from": null,
"to": "10000",
"include_lower": false,
"include_upper": true,
"boost": 1
}
}
},
"_source": false,
"stored_fields": "_none_",
"aggregations": {
"groupby": {
"composite": {
"size": 1000,
"sources": [
{
"s_nationkey": {
"terms": {
"field": "s_nationkey",
"missing_bucket": false,
"order": "asc"
}
}
}
]
},
"aggregations": {
"something": {
"sum": {
"script": "doc['s_suppkey'].value * ( 1 - doc['s_acctbal'].value )"
}
}
}
}
}
}