1) 필드 타입

  1. Object / Nested Type


  • object 필드 값들은 실제로 하나의 Document 안에 전부 포함되어 있다.
  • nested 필드 값들은 내부적으로 별도의 Document로 분리되어 저장되며 쿼리 결과에서 상위 Document와 합쳐져서 보여지게 된다.



  1. 위치 정보 필드 Geo Point Type

Geo Point 는 위도(latitude)와 경도(longitude) 두 개의 실수 값을 가지고 지도 위의 한 점을 나타내는 값입니다. Geo Point 필드의 값들은 다음과 같이 다양한 방법으로 입력이 가능합니다.

object 형식으로 geo_point 입력
PUT my_locations/_doc/1
{
"location": {
"lat": 41.12,
"lon": -71.34
}
}

text 형식으로 geo_point 입력
PUT my_index/_doc/2
{
"location": "41.12,-71.34"
}

geohash 형식으로 geo_point 입력
PUT my_index/_doc/3
{
"location": "drm3btev3e86"
}

실수의 배열 형식으로 geo_point 입력
PUT my_index/_doc/4
{
"location": [
-71.34,
41.12
]
}

Geo Point 필드는 매핑에서 다음과 같이 "type": "geo_point" 로 선언합니다.

object 형식으로 geo_point 입력
PUT my_geo
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}


  1. IP , Range, Binary Type


IP

IP 주소 형식을 저장합니다. 매핑은 "type": "ip" 으로 선언합니다. 값은 "192.168.1.1" 같은 IPv4 형식과 "0:0:0:0:0:ffff:c0a8:105" 같은 IPv6 형식을 문자열 처럼 입력합니다.


Range

숫자나 날짜, IP 등을 시작과 끝이 있는 2차원의 범위 형태로 저장합니다. 매팽의 "type" 에 선언 가능한 값은 integer_range, float_range, long_range, double_range, date_range, ip_range 들이 있습니다. 데이터의 범위는 다음과 같이 gt, gte, lt, lte 를 사용해서 지정합니다.

integer_range 와 date_range 타입의 필드 선언
PUT my_range
{
"mappings": {
"properties": {
"amount": {
"type": "integer_range"
},
"days": {
"type": "date_range"
}
}
}
}
쿼리를 테스트 하기 위해 먼저 다음 도큐먼트를 입력하겠습니다.

integer_range, date_range 타입의 값을 가진 도큐먼트 입력
PUT my_range/_doc/1
{
"amount": {
"gte": 19,
"lt": 28
},
"days": {
"gt": "2019-06-01T09:00:00",
"lt": "2019-06-20"
}
}
Range 필드의 쿼리는 일반적인 숫자나 날짜 처럼 range 쿼리를 사용합니다. 다만 범위 데이터를 range 쿼리로 검색 할 때는 추가로 relation 옵션의 값을 입력해야 하며 입력하지 않으면 오류가 납니다. relation 옵션에 지정 가능한 값은 within, contains, intersects 3가지가 있습니다.

  • within : 도큐먼트 범위 값이 쿼리한 범위 안에 완전히 포함되는 도큐먼트들을 가져옵니다.
  • contains : within과 반대로 쿼리 범위가 도큐먼트 범위 값 안에 완전히 포함되는 도큐먼트들을 가져옵니다.
  • Intersects : 도큐먼트 범위 값과 쿼리 범위에 공통적인 부분이 있는 도큐먼트들을 가져옵니다.
사용 예제는 다음과 같습니다.

request
"relation": "intersects" 으로 range 쿼리
GET my_range/_search
{
"query": {
"range": {
"amount": {
"gte": "16",
"lte": "25",
"relation": "intersects"
}
}
}
}
다른 값을 가진 도큐먼트를 더 입력하고 relation 값을 contains, within 으로 변경 해 가면서 더 실습 해 보시기 바랍니다.

response

"relation": "intersects" 으로 range 쿼리 결과
{
"took" : 950,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "my_range",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"amount" : {
"gte" : 19,
"lt" : 28
},
"days" : {
"gt" : "2019-06-01T09:00:00",
"lt" : "2019-06-20"
}
}
}
]
}
}

여행이나 출장 정보를 담은 도큐먼트가 있다면 시작일, 종료일 두 필드를 지정하는 대신 출장기간 이라는 date_range 타입의 단일 필드로 값을 저장해서 더욱 편하고 유용하게 사용할 수 있습니다.


Binary

"type": "binary" 로 지정해서 시스템 파일이나 이미지 정보 같은 바이너리 값을 저장할 수 있습니다. binary 필드는 기본적으로 색인이 되지 않아 검색이나 집계가 불가능하고 _source에만 남아 있습니다.

바이너리 정보들은 일반적으로 용량이 크기 때문에 elasticsearch 도큐먼트에 저장하는 것은 불필요한 저장소와 통신 데이터의 낭비가 될 수 있습니다. 가능하면 바이너리 데이터의 저장은 S3 또는 HDFS 같은 저렴한 저장소를 이용하고 elasticsearch 도큐먼트에는 해당 자원에 접근 가능한 키 또는 URL 등만 저장해서 따로 가져오도록 하는 것이 바람직합니다.

지금까지 설명한 필드 외에도 수많은 종류의 필드 타입들이 있습니다. 새 버전이 나올 때 마다 새로운 필드 타입들이 추가되거나 기존에 있던 타입들의 사용이 만료되고 있기 때문에 항상 공식 도큐먼트를 잘 참고하시기 바랍니다.


2) 검색 쿼리

  1. match , match_all (_search 와 동일)

  • match 는 Full-Text-Search에 이용
    • match 쿼리는 풀 텍스트 검색에 사용되는 가장 일반적인 쿼리
    • message 필드를 이용해서 해당 message가 포함되어있는 모든 문서를 검색


match 쿼리로 message 필드에서 dog 검색
GET my_index/_search
{
"query": {
"match": {
"message": "dog"
}
}
}


  • 검색어 조건 변경 필드 operator 옵션 이용 ( <필드 명>: { "query":<검색어>, "operator": } )

match 쿼리 AND 조건으로 quick dog 검색
GET my_index/_search
{
"query": {
"match": {
"message": {
"query": "quick dog",
"operator": "and"
}
}
}
}


  • 위의 예시의 반대로 공백 전체를 검색하기 위한 쿼리 match_pharse 이용 ( ex) quick dog 사이의 공백)
    • slop 이라는 옵션을 이용하여 slop에 지정된 값 만큼 단어 사이에 다른 검색어가 끼어드는 것을 허용


match_phrase 쿼리로 "lazy dog" 구문 검색
GET my_index/_search
{
"query": {
"match_phrase": {
"message": "lazy dog"
}
}
}

match_phrase 쿼리에 slop:1 로 "lazy dog" 구문 검색
GET my_index/_search
{
"query": {
"match_phrase": {
"message": {
"query": "lazy dog",
"slop": 1
}
}
}
}

response .....
"message" : "Lazy jumping dog"


  • Filter : score 값에 영향을 주지 않도록 제어할 때 사용

지금까지 살펴본 풀 텍스트 검색은 스코어 점수 기반으로 정확도(relevancy)가 높은 결과부터 가져옵니다. Elasticsearch는 정확도를 고려하는 풀 텍스트 외에도 검색 조건의 참 / 거짓 여부만 판별해서 결과를 가져오는 것이 가능합니다. 풀 텍스트와 상반되는 이 특성을 정확값(Exact Value) 이라고 하는데 말 그대로 값이 정확히 일치 하는지의 여부 만을 따지는 검색입니다. Exact Value 에는 term, range 와 같은 쿼리들이 이 부분에 속하며, 스코어를 계산하지 않기 때문에 보통 bool 쿼리의 filter 내부에서 사용하게 됩니다.

세부 검색 ex)
must 로 fox 검색 후 must_not 으로 dog 제거
GET my_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"message": "fox"
}
}
],
"filter": [
{
"bool": {
"must_not": [
{
"match": {
"message": "dog"
}
}
]
}
}
]
}
}
}


  • keyword

문자열 데이터는 keyword 형식으로 저장하여 정확 값 검색이 가능합니다. 아래의 쿼리는 message 필드 값이 "Brown fox brown dog" 문자열과 공백, 대소문자까지 정확히 일치하는 데이터 만을 결과로 리턴

request

keyword 필드 검색
GET my_index/_search
{
"query": {
"bool": {
"filter": [
{
"match": {
"message.keyword": "Brown fox brown dog"
}
}
]
}
}
}

response

keyword 필드 검색 결과
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.0,
"_source" : {
"message" : "Brown fox brown dog"
}
}
]
}
}

keyword 타입으로 저장된 필드는 스코어를 계산하지 않고 정확 값의 일치 여부 만을 따지기 때문에 스코어가 "_score" : 0.0 으로 나오게 됩니다. 스코어를 계산하지 않기 때문에 keyword 값을 검색 할 때는 filter 구문 안에 넣도록 합니다.


3) Bulk API

여러 명령을 배치로 수행하기 위해서 _bulk API의 사용이 가능 _bulk API로 index, create, update, delete의 동작이 가능하며 delete를 제외하고는 명령문과 데이터 문을 한 줄씩 순서대로 입력해야 한다. delete는 내용 입력이 필요 없기 때문에 명령문만 있다.

_bulk 의 명령문과 데이터 문은 반드시 한 줄 안에 입력이 되어야 하며 줄 바꿈을 허용 하지 않음.

다음은 _bulk 명령을 실행한 예제입니다. 각 명령의 결과가 items에 배열로 리턴


_bulk 명령 실행
POST _bulk
{"index":{"_index":"test", "_id":"1"}}
{"field":"value one"}
{"index":{"_index":"test", "_id":"2"}}
{"field":"value two"}
{"delete":{"_index":"test", "_id":"2"}}
{"create":{"_index":"test", "_id":"3"}}
{"field":"value three"}
{"update":{"_index":"test", "_id":"1"}}
{"doc":{"field":"value two"}}

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


인덱스 단위로 _bulk 사용
벌크 동작은 따로따로 수행하는 것 보다 속도가 훨씬 빠르다. 특히 대량의 데이터를 입력 할 때는 반드시 _bulk API를 사용해야 불필요한 오버헤드가 없다. Logstash Beats 그리고 Elastic 웹페이지에서 제공하는 대부분의 언어 별 클라이언트에서는 데이터를 입력할 때 _bulk를 사용하도록 개발되어 있다.



4) Update API

  • upsert document