copy_to란?

Elasitcsearch의 index에는 여러 필드들이 존재합니다. copy_to는 여러 필드의 값을 그룹으로 묶어 하나의 필드로 검색할 수 있는 기능을 제공하는 것입니다. Elasticsearch 공식 문서에서는 first name과 last name을 예시를 들어 두 필드를 하나의 full name이라는 필드로 copy_to 하여 full name 하나의 필드만으로 first name과 last name을 모두 검색이 가능하도록 하였습니다. 이렇게 copy_to를 이용하면 검색을 할 때 "피트"라는 사람이 "피트"가 first name인지 last name인지 명확하게 알지 못할 때 검색이 유용할 뿐만 아니라 여러 필드를 자주 검색할 때 쿼리에 여러 필드를 검색하는 것보다 하나의 copy_to 필드를 검색하는 것이 검색 속도 향상에도 많은 도움이 된다고 합니다.

 

copy_to 사용해보기

Elasticsearch 공식 문서에서는 first name, last name을 예시로 들었지만 저 같은 경우 Elasticsearch를 로그를 저장하고 분석하는데 많이 사용하기 때문에 App name, Service name을 예시로 사용해 보겠습니다.

 

my-log 인덱스 필드 타입

필드명 타입
app keyword
service keyword
app_service keyword
level keyword
message text

 

my-log 인덱스 생성

curl -X PUT http://localhost:9200/my-log -H 'Content-Type: application/json' -d '
{
  "mappings": {
    "properties": {
      "app": {
        "type": "keyword",
        "copy_to": "app_service"
      },
      "service": {
        "type": "keyword",
        "copy_to": "app_service"
      },
      "app_service": {
        "type": "keyword"
      },
      "level": {
        "type": "keyword"
      },
      "message": {
        "type": "text"
      }
    }
  }
}
'

 

데이터는 아래와 같이 넣어줬습니다.

{"app":"payment", "service":"api_server", "level": "debug", "message": "A payment was successful"}
{"app":"payment", "service":"api_server", "level": "debug", "message": "B payment was successful"}
{"app":"client", "service":"server", "level": "debug", "message": "client connected"}

 

이제 app_service라는 필드 하나만으로 payment를 검색하여 2개의 데이터가 출력되는지 확인합니다.

curl -X POST http://localhost:9200/my-log/_search -H 'Content-Type: application/json' -d '
{
  "query": {
    "match": {
      "app_service": {
        "query": "payment"
      }
    }
  }
}
'

 

출력결과

{
  "took": 43,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.5908618,
    "hits": [
      {
        "_index": "my-log",
        "_id": "1",
        "_score": 0.5908618,
        "_source": {
          "app": "payment",
          "service": "api_server",
          "level": "debug",
          "message": "A payment was successful"
        }
      },
      {
        "_index": "my-log",
        "_id": "2",
        "_score": 0.5908618,
        "_source": {
          "app": "payment",
          "service": "api_server",
          "level": "debug",
          "message": "B payment was successful"
        }
      }
    ]
  }
}

위에서 설명했던 것과 같이 여러 필드를 검색 조건에 전부 기입하는 것 보다 이렇게 copy_to를 이용하면 개발자 입장에서 보다 간편하고 검색 성능을 높을 수 있습니다. copy_to를 잘 활용만 한다면 상황에 따라 개발하는데 많은 도움이 될 것 같습니다.

 

참고

https://www.elastic.co/guide/en/elasticsearch/reference/current/copy-to.html

 

copy_to | Elasticsearch Guide [8.10] | Elastic

copy_to is not supported for field types where values take the form of objects, e.g. date_range

www.elastic.co

Aggregations을 수행할 때 사용할 데이터

 

Sub Aggregations

aggregation을 통해 집계를 내고 그 결과에서 세부적인 집계를 위해 Sub Aggregation을 지원하고 있습니다.

 

level 필드별 duration의 평균 시간 구하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            },
            "aggs": {
                "avgDuration": {
                    "avg": {
                        "field": "duration"
                    }   
                }
            }
        }
    }
}

결과 보기

level의 "info"는 9개가 있으며 9개의 duration 평균은 167.777777입니다.

level의 "warn"는 6개가 있으며 6개의 duration 평균은 333.33333입니다.

level의 "debug"는 5개가 있으며 5개의 duration 평균은 258입니다.

 

level별 action 종류 및 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            },
            "aggs": {
                "byAction": {
                    "terms": {
                        "field": "action"
                    }   
                }
            }
        }
    }
}

결과 보기

{
    "took": 4,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 20,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "byLevel": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "info",
                    "doc_count": 9,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 4
                            },
                            {
                                "key": "open_file",
                                "doc_count": 4
                            },
                            {
                                "key": "open_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "warn",
                    "doc_count": 6,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 2
                            },
                            {
                                "key": "open_file",
                                "doc_count": 2
                            },
                            {
                                "key": "close_socket",
                                "doc_count": 1
                            },
                            {
                                "key": "open_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "debug",
                    "doc_count": 5,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 2
                            },
                            {
                                "key": "open_file",
                                "doc_count": 2
                            },
                            {
                                "key": "close_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                }
            ]
        }
    }
}

"info"는 9개이며 그중에 "close_file"는 4개, "open_file"은 4개, "open_socket"은 1개 입니다.

"warn"는 6개이며 그중에 "close_file"는 2개, "open_file"은 2개, "open_socket"은 1개, "close_socket"은 1개입니다.

"info"는 9개이며 그중에 "close_file"는 2개, "open_file"은 2개, "close_socket"은 1개입니다.

 

 

Aggregation을 수행할 때 사용할 데이터

terms

terms는 데이터의 keyword별 종류 및 개수를 집계하는데 사용합니다.

 

모든 도큐먼트의 level의 종류와 개수 구하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregation을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

level 필드의 데이터 종류와 각 종류별로 도큐먼트가 몇개 있는지 확인합니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            }
        }
    }
}

결과 보기

"byLevel" 내부의 buckets를 보면 "key"에는 level필드의 값이 "doc_count"에는 개수를 표현하고 있습니다.

즉 "info"는 9개가 있고, "warn"은 6개, "debug"는 5개가 있는 것을 확인할 수 있습니다.

 

range

값의 범위 별로 도큐먼트의 개수를 측정하는데 사용할 수 있습니다.

 

모든 도큐먼트의 duration의 범위별 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

duration의 범위 100이하, 100부터 200까지, 200부터 300까지, 300부터 400까지, 400이상 으로 범위를 지정하여 검색하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byDuration": {
            "range": {
                "field": "duration",
                "ranges": [
                    {
                        "to": 100
                    },
                    {
                        "from": 100,
                        "to": 200
                    },
                    {
                        "from": 200,
                        "to": 300
                    },
                    {
                        "from": 300,
                        "to": 400
                    },
                    {
                        "from": 400
                    }
                ]
            }
        }
    }
}

결과 보기

 

histogram

range의 경우 from, to를 통하여 범위를 지정했다면 histogram은 range와 다르게 interval옵션을 통하여 interval단위 별로 필드의 개수를 측정하는 Aggregations입니다.

 

모든 도큐먼트의 duration을 100 단위로 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byDuration": {
            "histogram": {
                "field": "duration",
                "interval": 100
            }
        }
    }
}

결과 보기

100 단위 별로 도큐먼트의 개수를 확인할 수 있습니다.

 

date_range

range처럼 date필드 또한 범위를 지정하여 집계를 할 수 있습니다.

 

모든 도큐먼트의 start_time 필드를 범위 별로 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

"start_time"필드의 값을 범위 별로 측정하도록 하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "date_range": {
            "date_range": {
                "field": "start_time",
                "ranges": [
                    {
                        "from": "2021-09-12 10:10:10",
                        "to": "2021-09-12 10:10:15"
                    },
                    {
                        "from": "2021-09-12 10:10:15",
                        "to": "2021-09-12 10:10:20"
                    },
                    {
                        "from": "2021-09-12 10:10:20"
                    }
                ]
            }
        }
    }
}

결과 보기

각 시간대별로 도큐먼트의 개수를 확인할 수 있습니다.

 

date_histogram

histogram과 같이 date_histogram도 interval 옵션을 넣어 각 interval별 집계를 측정할 수 있습니다.

interval옵션에 second, minute, hour, day, month, week 등을 넣을 수 있습니다.

 

모든 도큐먼트의 start_time 필드를 1분 별로 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "date_his": {
            "date_histogram": {
                "field": "start_time",
                "interval": "minute"
            }
        }
    }
}

결과 보기

 

 

 

 

Aggregation을 수행할 때 사용할 데이터

 

cardinality

cardinality는 데이터의 종류가 몇 가지 있는지 확인하는 데 사용됩니다.

 

user_id가 1인 도큐먼트에 action이 몇가지 종류가 있는지 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "cardinality_by_action": {
            "cardinality": {
                "field": "action"
            }
        }
    }
}

결과 보기

"cardinality_by_action"를 통해 user_id가 1인 도큐먼트에서는 action의 종류가 4개가 있다는 것을 확인할 수 있습니다.

실제로 "open_file", "close_file", "open_socket", "close_socket" 이렇게 4개입니다.

 

percentiles

각각의 값을 백분율로 보는 Aggregations입니다. 퍼센트 옵션을 설정하지 않으면 디폴트로 [1, 5, 25, 50, 75, 95, 99]로 설정됩니다.

 

user_id가 1인 도큐먼트에 duration의 백분율 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "percentiles_by_duration": {
            "percentiles": {
                "field": "duration"
            }
        }
    }
}

결과 보기

하위 1%의 값은 100이며, 상위 99%의 값은 430인 것을 확인할 수 있습니다.

 

퍼센트를 설정하여 확인하기

이제는 디폴트 퍼센트값이 아닌 퍼센트를 사용자가 [25, 50, 75]로 지정하여 검색하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "percentiles_by_duration": {
            "percentiles": {
                "field": "duration",
                "percents": [ 25, 50, 75 ]
            }
        }
    }
}

결과 보기

 

percentile_ranks

지정한 값이 백분율로 몇 퍼센트 안에 속하는지 확인할 수 있습니다.

values라는 옵션으로 값을 지정할 수 있습니다.

 

user_id가 1인 도큐먼트에 필드 duration에 100, 200, 300의 값이 있다면 백분율 몇 퍼센트인지 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "rank_by_duration": {
            "percentile_ranks": {
                "field": "duration",
                "values": [ 100, 200, 300 ]
            }
        }
    }
}

결과 보기

 

Aggregation을 수행할 때 사용할 데이터

Aggregations이란

저장된 데이터를 수치화시키는 여러 가지의 연산을 수행하는 역할을 합니다. 이러한 기능을 통하여 데이터를 분석하여 사용자가 보기 좋게 시각화를 할 수 있습니다.

 

이번 페이지에서는 min, max, sum, avg, stats에 대하여 알아보겠습니다.

min

query의 검색 결과에 특정 필드의 값이 가장 작은 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 최솟값 구하기

"size"를 0으로 설정한 이유는 우리는 데이터를 가져오는 것이 목적이 아닌 user_id가 1인 도큐먼트에서 duration이 가장 작은 값만 도출하는 것이 목적이기 때문에 불필요하게 데이터를 가져오는 것을 생략하기 위함입니다.

"aggs"를 통하여 쿼리에 Aggregation을 선언하고 "minDuration"이라는 Aggregation의 이름을 사용자가 지정합니다. 그리고 "min"을 통하여 duration이라는 필드의 최솟값을 측정하도록 합니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "minDuration": {
            "min": {
                "field": "duration"
            }
        }
    }
}

결과 보기

"hits"는 검색 결과를 나타내지만 우리는 "size"를 0으로 설정하였기 때문에 결과값은 나오지 않습니다.

"aggregations"에 우리가 지정한 "minDuration"이라는 명칭으로 최소값이 100이라는 결과가 나왔습니다.

 

max

query의 검색 결과에 특정 필드의 값이 가장 큰 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 최대값 구하기

"maxDuration"이라는 명칭으로 duration의 값이 가장 큰 값을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "maxDuration": {
            "max": {
                "field": "duration"
            }
        }
    }
}

결과 보기

maxDuration을 통하여 user_id가 1인 도큐먼트에서 duration이 가장 큰 값은 430이라는 결과를 알 수 있습니다.

 

sum

query의 검색 결과에 특정 필드의 값을 전부 더한 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 전부 더한 값 구하기

"sumDuration"이라는 명칭으로 user_id가 1인 duration의 값을 전부 더한 값을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "sumDuration": {
            "sum": {
                "field": "duration"
            }
        }
    }
}

결과 보기

sumDuration을 통하여 user_id가 1인 도큐먼트에서 duration을 전부 더한 값이 2600이라는 것을 알 수 있습니다.

 

avg

query의 검색 결과에 특정 필드의 값의 평균 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 전부 더한 값 구하기

"avgDuration"이라는 명칭으로 user_id가 1인 duration의 값의 평균을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "avgDuration": {
            "avg": {
                "field": "duration"
            }
        }
    }
}

결과 보기

avgDuration을 통하여 user_id가 1인 도큐먼트에서 duration의 평균값이 216.66666666이라는 것을 알 수 있습니다.

 

stats

stats를 통하여 위에서 본 count, min, max, sum, avg의 모든 결과를 가져올 수 있습니다.

 

user_id가 1인 도큐먼트의 duration 필드의 stats 결과 값 확인하기

"statsDuration"이라는 명칭으로 user_id가 1인 도큐먼트의 count, min, max, sum, avg의 값을 확인하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "statDuration": {
            "stats": {
                "field": "duration"
            }
        }
    }
}

결과 보기

"statDuration"을 통해 count, min, max, avg, sum의 결과 값을 확인할 수 있습니다.

 

그렇다면 min만 필요하더라도 stats하나로 모든 결과 값을 구하면 된다고 생각할 수 있습니다. 그러나 이러한 생각은 좋지 못한 생각입니다. min만 구하기 위해서 stats를 사용하는 것은 매우 안 좋은 선택입니다. 이 이유는 Elasticsearch에게 그만큼 많은 부하를 줄 수 있기 때문입니다. 그러나 반대로 min, max, sum을 구해야 될 경우에는 min, max, sum 각각 3번의 검색을 수행하는 것보다는 stats를 한 번을 사용하여 네트워크 I/O를 줄여 주는 것이 더 현명한 선택이라고 생각됩니다.

 

삽입된 데이터는 이전 페이지를 참조해주세요

https://stdhsw.tistory.com/entry/Elasticsearch-%EA%B2%80%EC%83%89%ED%95%98%EA%B8%B01

 

match 쿼리

match는 term과 비슷해 보이지만 미묘한 차이가 있습니다. match의 경우 기본적인 검색을 수행하는데 사용을 하지만 저 같은 경우 text 필드에서 보다 상세한 검색을 수행할 경우 주로 사용됩니다.  즉 문서의 내용에서 특정 단어가 들어 갔는지 검색할때 많이 사용됩니다.

 

text 필드 검색하기

message 필드에 "aaa"라는 문자열이 포함된 모든 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "aaa"
                    }
                }
            ]
        }
    }
}

 

term 쿼리

term의 경우 match와 비슷한 모습을 취하지만 term은 완전히 같은 데이터를 검색하는데 많이 사용됩니다. 저 같은 경우 주로 keyword타입에서 특정 단어를 검색하는데 많이 사용하고 있습니다.

 

keyword 타입 검색하기

level 필드의 값이 "info"인 값을 가진 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "level": "info"
                    }
                }
            ]
        }
    }
}

 

range 쿼리

range 쿼리는 값의 범위를 검색하는데 사용됩니다. 

 

range쿼리의 파라미터

파라미터명 설명
gte 크거나 같다
gt 크다
lte 작거나 같다
lt 작다

 

범위를 이용하여 검색하기

user_id가 2보다 크거나 같으면서 7보다 작은 도큐먼트 검색하기

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "range": {
                        "user_id": {
                            "gte": "2",
                            "lt": "7"
                        }
                    }
                }
            ]
        }
    }
}

 

 

데이터는 이전 페이지를 참조해주세요

https://stdhsw.tistory.com/entry/Elasticsearch-%EA%B2%80%EC%83%89%ED%95%98%EA%B8%B01

bool 쿼리

Elasticsearch에서 보다 자세한 검색을 위해 복합 쿼리가 필요합니다. 이럴 때 bool을 사용하게 됩니다. bool 쿼리에는 must, must_not, should가 있고 이러한 쿼리를 이용하여 score에 점수를 매길 수 있습니다. score의 점수가 높을수록 보다 정확한 결괏값을 도출할 수 있습니다.

must

must는 검색 조건에 반듯이 포함한 도큐먼트를 가져옵니다.

 

keyword타입 검색하기

level필드에 값이 "info"값을 가지는 모든 도큐먼트를 검색합니다.

참고로 level필드의 타입은 keyword입니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": {
                "match": {
                    "level": "info"
                }
            }
        }
    }
}

 

복합 쿼리 검색하기

level필드의 값이 "info"이며, message에 "aaa"값을 포함하는 도큐먼트를 검색합니다.

level필드는 keyword이며, message는 text입니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "level": "info"
                    }
                },
                {
                    "match": {
                        "message": "aaa"
                    }
                }
            ]
        }
    }
}

 

text타입 OR 검색하기

message필드의 값이 "aaa" 또는 "open" 중 하나 이상의 값을 가지는 도큐먼트를 검색합니다.

만약 "aaa"와 "open"의 값 모두 가지고 있다면 score의 점수가 더 높습니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "aaa open"
                    }
                }
            ]
        }
    }
}

 

text타입 AND 검색하기

message필드의 값이 "aaa"와 "open" 모든 값을 가지는 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "aaa"
                    }
                },
                {
                    "match": {
                        "message": "open"
                    }
                }
            ]
        }
    }
}

must_not

must_not은 검색 조건에 반듯이 포함하지 않은 도큐먼트를 가져옵니다.

 

must_not 쿼리 검색하기

level필드에 값이 "info"가 아닌 모든 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must_not": [
                {
                    "match": {
                        "level": "info"
                    }
                }
            ]
        }
    }
}

 

must_not 복합 쿼리 검색하기

message 필드의 값이 "open"을 가지며, level 필드의 값이 "info"가 아닌 도큐먼트를 검색합니다.

level은 keyword타입이며, message는 text타입입니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "open"
                    }
                }
            ],
            "must_not": [
                {
                    "match": {
                        "level": "info"
                    }
                }
            ]
        }
    }
}

should

should의 경우 반듯이 필요한 것은 아니지만 만약 있다면 score의 점수를 높여 줍니다.

keyword 타입의 경우 OR 검색을 지원하지 않습니다. 그러나 정상적인 방법은 아니지만 저 같은 경우 keyword 타입인 경우 should를 이용하여 OR 검색처럼 사용합니다.

 

should 쿼리 검색하기

level 필드의 값이 "debug"일 경우 보다 높은 점수를 부여하고 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "level": "debug"
                    }
                }
            ]
        }
    }
}

 

should 복합 쿼리로 검색하기

message필드에는 "open"의 값을 가진 도큐먼트 중에 level필드에는 "info"의 값을 가지면 보다 높은 score 점수를 부여하여 검색합니다.

(level필드에는 info 이외의 값도 같이 검색됩니다. 그러나 info의 값을 가진 도큐먼트가 더 높은 score 점수를 가집니다.)

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "open"
                    }
                }
            ],
            "should": [
                {
                    "match": {
                        "level": "info"
                    }
                }
            ]
        }
    }
}

 

 

참조

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

 

Boolean query | Elasticsearch Guide [7.14] | Elastic

A query that matches documents matching boolean combinations of other queries. The bool query maps to Lucene BooleanQuery. It is built using one or more boolean clauses, each clause with a typed occurrence. The occurrence types are: Occur Description must

www.elastic.co

 

 

 

데이터 넣기

지금 넣는 데이터로 앞으로 검색하는 데 사용하겠습니다.

(http request client를 사용하신다면 body에 반듯이 enter 한번 더 입력하세요)

api PUT http://localhost:9200/_bulk
header Content-type: application/json
body {"index": {"_index": "my-log-index-2021-08-29", "_id": 1}}
{"user_id":1, "level":"info", "timestamp": 1630230119, "action": "open_file", "message": "user1 aaa file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 2}}
{"user_id":2, "level":"debug", "timestamp": 1630230120, "action": "open_file", "message": "user2 bbb file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 3}}
{"user_id":3, "level":"info", "timestamp": 1630230121, "action": "open_socket", "message": "user3 socket open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 4}}
{"user_id":4, "level":"debug", "timestamp": 1630230121, "action": "open_socket", "message": "user4 ddd socket open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 5}}
{"user_id":5, "level":"info", "timestamp": 1630230122, "action": "open_file", "message": "user5 ccc file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 6}}
{"user_id":6, "level":"info", "timestamp": 1630230123, "action": "close_file", "message": "user6 aaa close open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 7}}
{"user_id":7, "level":"info", "timestamp": 1630230124, "action": "close_socket", "message": "user7 ccc close socket"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 8}}
{"user_id":8, "level":"debug", "timestamp": 1630230125, "action": "open_file", "message": "user8 aaa file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 9}}
{"user_id":9, "level":"info", "timestamp": 1630230126, "action": "open_file", "message": "user9 bbb file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 10}}
{"user_id":10, "level":"info", "timestamp": 1630230127, "action": "open_file", "message": "user10 aaa file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 11}}
{"user_id":11, "level":"warn", "timestamp": 1630230128, "action": "open_file", "message": "user11 bbb file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 12}}
{"user_id":12, "level":"info", "timestamp": 1630230128, "action": "open_file", "message": "user12 aaa file open"}
{"index": {"_index": "my-log-index-2021-08-29", "_id": 13}}
{"user_id":13, "level":"info", "timestamp": 1630230129, "action": "open_file", "message": "user13 bbb file open"}


GET으로 데이터 가져오기

일상적으로 많이 사용하였던 방법입니다. GET으로는 도큐먼트 아이디를 통하여 해당 데이터를 가져올 수 있습니다.

 

도큐먼트 아이디 1 데이터 가져오기

api GET http://localhost:9200/my-log-index-2021-08-29/_doc/1

출력 결과

query를 이용하여 가져오기

이번에는 도큐먼트 아이디가 아닌 query를 통하여 값을 가져오는 방법을 알아보겠습니다.

 

인덱스의 모든 데이터 가져오기

size는 가져올 수 있는 최대 개수입니다. 이번 결과에서는 size가 없어도 무방합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "size": 1000,
    "query": {
        "match_all": {}
    }
}

 

필드 값으로 검색하여 데이터 가져오기

active필드에 open_socket 값을 가지는 도큐먼트 가져오기

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "match": {
            "action": "open_socket"
        }
    }
}

출력 결과

출력 결과에서 각 태그의 의미는 다음과 같습니다.

took 쿼리 수행 시간
timed_out 쿼리 수행 시간이 넘었는지 확인
_shard 쿼리를 수행한 shard의 정보
_shard.total 쿼리를 수행한 shard의 수
_shard.successful 쿼리 수행을 성공한 shard의 수
_shard.skipped 쿼리를 건너뛴 shard의 수
_shard.failed 쿼리를 실패한 shard의 수
hits 쿼리 수행 결과
hits.total 쿼리 수행에 일치하는 도큐먼트 수
hits.max_score 쿼리 수행후 가장 높은 점수를 가진 값
hits.hits 수행 결과의 도큐먼트 정보들

 

단어가 아닌 문장으로 검색하기

match_phrase를 이용하여 message필드에 "aaa file"문장을 포함한 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "match_phrase": {
            "message": "aaa file"
        }
    }
}

 

 

 

Keyword 

Elasticsearch에는 문자열 필드 중 text와 keyword가 존재합니다. Keyword는 키워드라는 표현 그대로 데이터 자체를 분류하는데 사용하는 타입입니다. 즉 검색 필터로 사용되며, 데이터를 정렬하고 Aggregation으로 데이터를 집계하는데 사용되는 타입입니다. Keyword 타입으로 "hello my elasticsearch"라는 데이터가 색인되면 "hello my elasticsearch" 그대로 데이터가 색인되어 검색 시 "hello my elasticsearch" 그대로 데이터를 검색해야 합니다.

 

Text

Text 필드는 지정한 분석기를 이용하여 데이터를 분석하는데 사용합니다. 만약 Analyzer를 지정하지 않으면 Standard Analyzer로 설정되는데 "hello my elasticsearch"라는 데이터가 색인되면 "hello", "my", "elasticsearch"로 데이터가 색인되어 keyword와 다르게 "hello"라고 검색을 해도 검색이 됩니다. 즉 유사한 문자열을 검사하거나 Analyzer 설정을 통해 보다 더 전문적인 검색을 수행하는데 사용하는 타입입니다.

 

정리

간단하게 정리하면 다음과 같습니다.

  Keyword Text
사용 목적 통계, 정렬, 필터링 유사성 및 전문적인 분석
검색 방식 입력된 값과 정확히 일치하는 문자를 검색 설정한 Analyzer에 따라서 검색
색인 방식 입력한 문자열 그대로 색인 토큰으로 분리되어 색인
Aggregation  색인된 데이터로 집계 가능 집계 불가능

 

Keyword와 Text 타입을 둘다 적용

Text처럼 분석도 필요하지만 Keyword처럼 Aggregation 모두 필요할 때 필드의 타입을 Keyword와 Text 둘 다 적용할 수 있습니다. 사실 별 다른 타입을 지정하지 않으면 기본 값으로 Keyword와 Text 타입 둘 다 적용됩니다. 이런 경우 문득 다방면으로 사용할 수 있을 것 같아 좋아 보일 수 있지만 보다 더 많은 리소스를 사용하기 때문에 사용 목적에 맞게 필드 타입을 지정하여 사용하는 것을 권장합니다.

 

Array 형식으로 필드에 데이터 저장하기

하나의 필드에 같은 형식의 데이터를 여러 개 넣어야 되는 경우가 많이 발생합니다. 그럴 때 대표적으로 2가지 방식이 있습니다.

1번째 방식으로는 필드와 함께 값을 같이 저장하는 방식입니다.

2번째 방식은 값만 저장하는 방식입니다.

이번에는 이 2가지 방식 모두에 대하여 알아보겠습니다.

1. 필드에 Object형식으로 데이터 리스트 저장하기

하나의 필드와 같이 값을 저장하는 방식으로는 nested를 사용하는 것 입니다.

 

nested 맵핑 만들기

api PUT http://localhost:9200/object-list-index/
header Content-type: application/json
body {
    "mappings": {
        "properties": {
            "server": {
                "type": "keyword"
            },
            "users": {
                "type": "nested"
            }
        }
    }
}

 

데이터 넣기

api PUT http://localhost:9200/object-list-index/_doc/1
header Content-type: application/json
body {
    "server": "server1",
    "users": [{"id": "user1"}, {"id": "user2"}, {"id": "user3"}, {"id": "user4"}]
}

 

데이터 출력하기

api GET http://localhost:9200/object-list-index/_doc/1

 

2. 필드에 데이터만 리스트로 넣기

이번에는 위에 방식과 다른 그냥 값만 배열 형식으로 넣는 방식입니다.

 

맵핑만들기

api PUT http://localhost:9200/value-list-index/
header Content-type: application/json
body {
    "mappings": {
        "properties": {
            "server": {
                "type": "keyword"
            },
            "users": {
                "type": "keyword",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                }
            }
        }
    }
}

 

데이터 넣기

api PUT http://localhost:9200/value-list-index/_doc/1
header Content-type: application/json
body {
    "server": "server1",
    "users": ["user1","user2", "user3","user4"]
}

 

데이터 출력하기

api GET http://localhost:9200/value-list-index/_doc/1

 

Object처럼 데이터 저장하기

Elasticsearch을 사용하다 보면 데이터를 일반적인 필드 형식으로 저장하는 것이 아닌 트리 구조로 데이터를 저장하고 싶은 상황이 있습니다. 즉 object형식으로 데이터를 저장해야 되는데 이럴 때 사용하는 것이 properties입니다. 이제부터 properties 사용법에 대하여 알아보겠습니다. (참고로 해당 방식처럼 Object형식으로 데이터를 저장하게 되면 인덱스의 구조가 복잡해져 검색하는 상황에 따라 검색의 성능이 저하될 수 있습니다. 참고하시고 본인의 데이터 검색 방식에 대하여 확인하시고 적용하는 것을 추천드립니다.)

properties 사용하기

데이터의 구조는 다음과 같습니다.

  • 학과
  • 학생
    • 이름
    • 나이
    • 이메일
api PUT http://localhost:9200/student-index
header Content-type: application/json
body {
    "mappings": {
        "properties": {
            "subject": {
                "type": "keyword"
            },
            "student": {
                "properties": {
                    "name": {
                        "type": "text"
                    },
                    "age": {
                        "type": "short"
                    },
                    "email": {
                        "type": "text"
                    }
                }
            }
        }
    }
}

 

출력 결과

추가적으로 데이터를 넣고 student-index를 출력하였습니다.

Object 형식의 필드를 추천하는 상황과 추천하지 않는 상황

상황에 따라 object형식으로 데이터를 저장할 때가 좋은 경우가 있는 반면 그렇지 않은 경우도 있습니다.

 

추천하는 상황

  • object의 필드 별로 데이터를 집계 및 통계를 내야 되는 경우

추천하지 않는 상황

  • 그냥 데이터를 저장하고 읽기만 하는 경우 오히려 데이터 검색 성능에 저하가 될 수 있습니다. 만약 그냥 데이터를 검색하는 데 사용하지 않고 텍스트 형식으로 출력하는데만 사용하신다면 그냥 필드를 text로 하여 json데이터를 text로 저장하는 것을 추천합니다.

참조

https://www.elastic.co/guide/en/elasticsearch/reference/current/object.html

 

Object field type | Elasticsearch Guide [7.14] | Elastic

If you need to index arrays of objects instead of single objects, read Nested first.

www.elastic.co

 

 

bulk란

Elasticsearch를 사용하면서 여러개의 데이터를 넣을때 매번 하나씩 데이터를 넣는 일은 네트워크의 잦은 IO의 발생으로 성능을 저하 시킬 수 있는 요인입니다. 이러한 문제를 해결하는 것이 bulk입니다. bulk는 여러개의 처리를 모아 두었다가 한번에 처리하는 batch 처리 기능입니다.

 

bulk를 통해 데이터 넣기

bulk를 사용하여 여러개의 데이터를 한번의 요청으로 넣을 수 있습니다.

api POST http://localhost:9200/my-log-index-2021-08-25/_bulk
header Content-type: Application/json
body {"index": {"_index": "my-log-index-2021-08-25", "_id": 1}}
{"user_id":1, "level":"info", "timestamp": 1629893888, "action": "open_file", "message": "user1 file open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 2}}
{"user_id":2, "level":"info", "timestamp": 1629893890, "action": "open_file", "message": "user2 file open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 3}}
{"user_id":3, "level":"info", "timestamp": 1629893891, "action": "open_socket", "message": "user3 socket open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 4}}
{"user_id":4, "level":"info", "timestamp": 1629893892, "action": "open_socket", "message": "user4 socket open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 5}}
{"user_id":5, "level":"info", "timestamp": 1629893893, "action": "open_file", "message": "user5 file open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 6}}
{"user_id":6, "level":"info", "timestamp": 1629893893, "action": "close_file", "message": "user6 close open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 7}}
{"user_id":7, "level":"info", "timestamp": 1629893895, "action": "close_socket", "message": "user7 close socket"}

중요 ) 여기서 만약에 Postman, 크롬에 ARS와 같이 http요청을 보내는 RestClient 프로그램을 사용하신다면 body 마지막에 개행(\n) 즉 enter키를 한번 더 입력해야 됩니다.

 

데이터가 잘 들어갔는지 확인하기

api GET http://localhost:9200/my-log-index-2021-08-25/_search

 

bulk를 이용하여 데이터 입력, 수정, 삭제 한번에 하기

데이터 입력 뿐만 아니라 데이터 수정, 삭제를 한번의 bulk를 통하여 수행할 수 있습니다.

 

bulk로 입력, 수정, 삭제

api POST http://localhost:9200/my-log-index-2021-08-25/_bulk
header Content-type: Application/json
body {"index": {"_index": "my-log-index-2021-08-25", "_id": 8}}
{"user_id":8, "level":"debuf", "timestamp": 1629893993, "action": "close_file", "message": "user8 close open"}
{"index": {"_index": "my-log-index-2021-08-25", "_id": 9}}
{"user_id":9, "level":"warn", "timestamp": 1629893995, "action": "error", "message": "failed user9 connection"}
{"update": {"_index": "my-log-index-2021-08-25", "_id": 1}}
{"doc": {"level": "debug"}}
{"update": {"_index": "my-log-index-2021-08-25", "_id": 2}}
{"doc": {"level": "debug"}}
{"delete": {"_index": "my-log-index-2021-08-25", "_id": 6}}

중요 ) 여기서 만약에 Postman, 크롬에 ARS와 같이 http요청을 보내는 RestClient 프로그램을 사용하신다면 body 마지막에 개행(\n) 즉 enter키를 한번 더 입력해야 됩니다.

벌크의 내용

8번, 9번 유저의 데이터를 추가하고

1번, 2번 유저의 level필드의 값을 debug로 수정하고

6번 유저의 데이터를 삭제합니다.

 

결과 확인

크롬의 "Elasticsearch Head"라는 확장 프로그램을 사용하여 결과를 확인하겠습니다.

"Elasticsearch Head"의 설치 방법은 링크과 같습니다.

https://stdhsw.tistory.com/entry/Elasticsearch-%EC%9C%88%EB%8F%84%EC%9A%B010-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0

 

Elasticsearch 윈도우10에 설치하기

Elasticsearch 다운로드 https://www.elastic.co/kr/downloads/elasticsearch 해당 주소에서 elasticsearch를 다운로드합니다. Elasticsearch 실행하기 압축을 푼 폴더 위치에서 bin/elasticsearch.bat파일을 실..

stdhsw.tistory.com

 

인덱스의 구조는 이전 template 설정과 같습니다.
https://stdhsw.tistory.com/entry/Elasticsearch%EC%9D%98-Index-template-%EC%84%A4%EC%A0%95

현재 인덱스 구조

필드 타입
user_id long
level keyword
timestamp long
action keyword
message text

 

도큐먼트 생성하기

Elasticsearch index에 document를 생성하여 각각의 필드에 값을 설정하고 index에 데이터를 넣습니다.
user 1 추가하기

api PUT http://localhost:9200/my-log-index-2021-08-24/_doc/1
header Content-type: application/json
body {
    "user_id": 1,
    "level": "info",
    "timestamp": 1629792520,
    "action": "open_file",
    "message": "user file open"
}


user 2 추가하기

api PUT http://localhost:9200/my-log-index-2021-08-24/_doc/2
header Content-type: application/json
body {
    "user_id": 2,
    "level": "info",
    "timestamp": 1629792525,
    "action": "open_socket",
    "message": "user socket open"
}


덮어쓰기를 방지하기 위한 데이터 생성방법
기존처럼 PUT http://localhost:9200/my-log-index-2021-08-24/_doc/2 을 사용하여 데이터를 생성한다면 기존에 도큐먼트 아이디 2가 존재한다면 기존의 데이터는 지워지고 현재 데이터가 덮어써지는 문제가 발생합니다. 이러한 문제를 해결하기 위해 _create를 통하여 데이터를 생성할 수 있습니다.
만약 같은 도큐먼트 아이디가 존재 할 경우 status 409 version_conflict_engine_exception 에러를 반환합니다.

api POST http://localhost:9200/my-log-index-2021-08-24/_create/2
header Content-type: application/json
body {
    "user_id": 3,
    "level": "info",
    "timestamp": 1629792540,
    "action": "close_file",
    "message": "user close open"
}

 

도큐먼트 조회하기

지금까지 입력한 데이터를 조회하도록 하겠습니다.

인덱스에 모든 도큐먼트 조회하기

api GET http://localhost:9200/my-log-index-2021-08-24/_search

출력 결과


특정 도큐먼트 아이디를 이용하여 조회하기
이번에는 인덱스의 모든 데이터가 아닌 도큐먼트 아이디를 이용하여 해당 도큐먼트의 값을 가져오도록 하겠습니다.

api GET http://localhost:9200/my-log-index-2021-08-24/_doc/1

출력 결과


조건을 이용하여 데이터 조회하기
이번에는 특정 조건에 만족하는 데이터를 찾아 조회하도록 하겠습니다.
action 필드의 값이 "open_socket"인 것을 조회하도록 하겠습니다.

api GET http://localhost:9200/my-log-index-2021-08-24/_search?q=action:open_socket

출력 결과

 

도큐먼트 수정하기

데이터를 수정하기 전에 먼저 version의 값을 확인하면 좋습니다.

api GET http://localhost:9200/my-log-index-2021-08-24/_doc/2

출력 결과
출력 결과를 보면 알 수 있듯이 현재 수정을 하지 않은 상태에서는 version이 1입니다.


도큐먼트 아이디를 이용하여 수정하기
도큐먼트 수정하는 것도 조회와 마찬가지로 도큐먼트 아이디를 이용하여 level 필드의 값을 "debug"로 수정하도록 하겠습니다.
_update를 사용하면 기존에 도큐먼트 아이디가 존재하지 않는 다면 에러가 발생합니다.

api POST http://localhost:9200/my-log-index-2021-08-24/_update/2
header Content-type: application/json
body {
    "doc": {
        "level": "debug"
    }
}


버전 확인하기

api GET http://localhost:9200/my-log-index-2021-08-24/_doc/2

출력 결과
버전이 2로 증가하였습니다. 버전은 해당 도큐먼트 아이디의 변경된 횟수를 의미합니다. 버전을 통하여 해당 도큐먼트의 아이디가 수정된 적이 있는지 확인할 수 있습니다.

 

도큐먼트 개수 측정하기

인덱스의 모든 도큐먼트 개수 측정하기

api GET http://localhost:9200/my-log-index-2021-08-24/_count


특정 조건을 이용하여 개수 측정하기
level 필드에 값이 "debug"인 도큐먼트 개수 측정

api GET http://localhost:9200/my-log-index-2021-08-24/_count?q=level:debug

 

도큐먼트 삭제하기

특정 도큐먼트 삭제하기

api DELETE http://localhost:9200/my-log-index-2021-08-24/_doc/2


인덱스 삭제하기
인덱스를 삭제하여 인덱스의 모든 값을 삭제합니다.

api DELETE http://localhost:9200/my-log-index-2021-08-24



 

reindex를 사용하는 이유

Elasticsearch를 사용하다 보면 ilm(Index life management)로 인하여 일정 기간이 지나면 인덱스가 지워집니다. 그러한 상황에서 중요한 로그 데이터를 보관하기 위해서 기존의 인덱스를 복사해 두는 것이 중요합니다. 이럴 때 사용하는 것이 reindex입니다.

 

사용방법

api POST http://localhost:9200/_reindex
header Content-type: application/json
body {
  "source": {
    "index": "기존_인덱스_명"
  },
  "dest": {
    "index": "변경할_인덱스_명"
  }
}

 

reindex를 사용했던 상황

로그의 내용을 분석하기 위해 ilm을 통해 로그 인덱스가 삭제 되지 않도록 인덱스명 변경하기

api POST http://localhost:9200/_reindex
header Content-type: application/json
body {
  "source": {
    "index": "my-log-index-2021-08-24"
  },
  "dest": {
    "index": "backup-log-index"
  }
}

 

참조

https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html

 

Reindex API | Elasticsearch Guide [7.14] | Elastic

Deprecated in 7.6. Sort in reindex is deprecated. Sorting in reindex was never guaranteed to index documents in order and prevents further development of reindex such as resilience and performance improvements. If used in combination with max_docs, conside

www.elastic.co

 

Index ilm policy이란

ILM (Index Lifecycle Management)은 데이터 인덱스의 수명 주기를 관리하기 위한 기능입니다. ILM를 사용하면 데이터의 보존, 압축, 백업 및 삭제와 같은 다양한 관리 작업을 자동화하고 데이터 스토리지를 효율적으로 관리할 수 있는 기능을 제공하지만 이번 문서에서는 아주 간단하게 인덱스가 생성되고 일정 기간이 지나면 인덱스가 삭제되도록 하는 방법에 대하여 정리하였습니다.

보다 자세한 ILM 설명은 링크를 확인해주세요 (https://stdhsw.tistory.com/entry/Elasticsearch-%EC%B5%9C%EC%A0%81%ED%99%94-3-ilm-policy%EB%A1%9C-data-tiering-%ED%95%98%EA%B8%B0)

 

Index ilm policy 설정하기

PUT localhost:9200/_ilm/policy/my-policy
Content-Type: application/json

{
  "policy": {
    "phases": {
      "delete": {
        "min_age": "3d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

ilm policy 파라미터

  • delete.min_age : 인덱스가 생성되고 얼마만큼의 기간이 흐르면 지울지 설정합니다.

 

Index ilm policy 설정 확인 하기

GET localhost:9200/_ilm/policy/my-policy

### 결과
{
  "my-policy": {
    "version": 1,
    "modified_date": "2023-10-25T09:43:53.527Z",
    "policy": {
      "phases": {
        "delete": {
          "min_age": "3d",
          "actions": {
            "delete": {
              "delete_searchable_snapshot": true
            }
          }
        }
      }
    },
    "in_use_by": {
      "indices": [],
      "data_streams": [],
      "composable_templates": []
    }
  }
}

 

ILM Policy를 Index template에 적용하기

인덱스 template에 ILM 설정을 적용합니다. 인덱스 template에 대한 자세한 설명은 링크를 확인해 주세요 (https://stdhsw.tistory.com/entry/Elasticsearch%EC%9D%98-Index-template-%EC%84%A4%EC%A0%95)

PUT http://localhost:9200/_template/my-index-template
Content-Type: application/json

{
  "index_patterns": ["my-index*"],
  "settings": {
    "index": {
      "number_of_shards": "2",
      "number_of_replicas": "1",
      "refresh_interval": "5s",
      "lifecycle": {
        "name": "my-policy"
      }
    }
  },
  "mappings": {
    "properties": {
      "app": {
        "type": "keyword"
      },
      "level": {
        "type": "keyword"
      },
      "message": {
        "type": "text"
      }
    }
  }
}

Index template 파라미터

  • settings.index.lifecycle.name : 위에서 지정한 ILM의 명칭을 기입합니다.

위 설정을 적용하면 이제 "my-index"로 시작하는 모든 인덱스는 앞으로 생성되고 3일 뒤에 자동적으로 삭제가 되어 보다 효율적으로 디스크 사용량을 관리할 수 있습니다.

 

Index ilm policy 삭제하기

### delete
DELETE localhost:9200/_ilm/policy/my-policy

 

+ Recent posts