티스토리 뷰

Elastic

Elasticsearch 시작하기

서보민 2021. 3. 10. 22:04

본 문서는 공식 홈페이지 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
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함