[엘라스틱서치] 스키마(Schema) 등록하기

엘라스틱서치의 장점 중 하나는 바로 스키마리스(Schemaless)가 가능하다는 점이다. 이로 인해서 DB를 설계가 편리해지고 개발하는 과정에서 힘든 부분을 상쇄할 수 있다. 하지만 스키마리스는 양날의 검이 될 수 있는데 원치 않는 스키마로 인해서 색인이 이상하게 될 경우, 검색에 문제가 있거나 퍼포먼스 측면에서 떨어질 수 있다.

 

개발을 진행중에는 스키마를 등록하지 않고 진행할 수 있겠지만, 데이터 설계가 끝날 즈음에는 스키마를 등록하여 데이터 구조를 고정시키고 원하는 형태의 검색이 가능하게 만들어야 될 것이다.

 

Schemaless 케이스

우선 스키마리스의 문제부터 파악해보도록 한다. 다음과 같이 주식에 관련된 데이터를 검색엔진에 넣었다고 가정을 해본다.

PUT /stock/_doc/1
{
  "stockId":"1",
  "stockNm":"삼성전자",
  "stockType":"보통주",
  "stockCd":"005930",
  "trade":"KOSPI",
  "current":"59700",
  "max":"62800",
  "min":"42300",
  "description":"삼성전자 주식회사는 전자 제품을 생산하며 정보통신기술에 대한 개발을 진행하고 있는 대한민국의 기업이다. 삼성전자는 삼성그룹 안에서도 가장 규모가 큰, 삼성그룹을 대표하는 기업이기도 하다. 본사는 경기도 수원시 영통구 삼성로 129 에 있다"
}

 

키바나에서 등록한 삼성전자 정보

위와 같이 삼성전자에 관련된 주식 정보를 키바나(Kibana)를 이용해서 엘라스틱서치에 넣었다. 결과는 당연히 잘 생성되었고, 여기까지 보면 문제가 없어보인다.

 

{
  "stock" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "current" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "description" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "max" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "min" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "stockCd" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "stockId" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "stockNm" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "stockType" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "trade" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1602814893565",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "d2mLrDURTBSDDvMIzezokA",
        "version" : {
          "created" : "7060299"
        },
        "provided_name" : "stock"
      }
    }
  }
}

그러나 생성된 스키마를 보게 되면 모든 컬럼이 동일한 타입 "text"와 "keyword"로 입력이 된 것을 볼 수 있다. 여기에는 가격과 같은 숫자값 뿐만 아니라 숫자형태의 아이디 그리고 자연어 문장으로 된 설명까지 모두 동일한 컬럼으로 되어 있다.

 

 

기본적으로 텍스트와 키워드 멀티 구조의 형태로 되어 있어 공간이 낭비되고 있으며, 형태소 분석이 필요할 수 있는 description 컬럼에는 어떤 형태소 분석을 사용할 것인지 정의되어 있지 않고, 숫자값을 문자 형태로 색인되어 있어서 공간낭비 뿐만 아니라 숫자 검색의 장점을 사용할 수 없게 된다.

 

그러면, 이 스키마는 어떻게 등록하는 것이 좋은지 스키마를 별도로 등록해보도록 해보자.

 

Schema를 등록하는 경우

stock의 스키마를 등록

PUT /stock
{
  "mappings":{
    "_doc":{
      "properties":{
        "stockId":{"type" : "integer"},
        "stockNm":{"type" : "keyword"},
        "stockType":{"type" : "keyword"},
        "stockCd":{"type" : "keyword"},
        "trade":{"type" : "keyword"},
        "current":{"type" : "integer"},
        "max":{"type" : "integer"},
        "min":{"type" : "integer"},
        "description":{"type" : "text"}
      }
    }
  }
}

stockId 숫자, 그리고 stockNm 등 trade까지는 키워드, 그리고 가격이 있는 current, max, min은 숫자값으로 설정하였다. 마지막 description은 형태소 분석을 위해 text로 설정을 하였다.

 

그러나 위의 스키마 등록을 시도하면 아래와 같이 에러가 떨어진다. 

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "The mapping definition cannot be nested under a type [_doc] unless include_type_name is set to true."
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "The mapping definition cannot be nested under a type [_doc] unless include_type_name is set to true."
  },
  "status" : 400
}

타입을 지정해서 발생하는 에러

위와 같은 에러가 왜 발생하냐면 엘라스틱 버전이 7.x부터는 하나의 인덱스당 하나의 타입만 가능하기 때문에 발생하는 에러이다. 위에서 설정한 _doc을 제거해보도록 한다. (6.x이하는 위의 예시를 실행하면 에러가 발생하지 않을 것이다)

 

PUT /stock
{
  "mappings":{
    "properties":{
      "stockId":{"type" : "integer"},
      "stockNm":{"type" : "keyword"},
      "stockType":{"type" : "keyword"},
      "stockCd":{"type" : "keyword"},
      "trade":{"type" : "keyword"},
      "current":{"type" : "integer"},
      "max":{"type" : "integer"},
      "min":{"type" : "integer"},
      "description":{"type" : "text"}
    }
  }
}
{
  "error" : {
    "root_cause" : [
      {
        "type" : "resource_already_exists_exception",
        "reason" : "index [stock/mPb8Zm3kR0ef2e05zlOe_Q] already exists",
        "index_uuid" : "mPb8Zm3kR0ef2e05zlOe_Q",
        "index" : "stock"
      }
    ],
    "type" : "resource_already_exists_exception",
    "reason" : "index [stock/mPb8Zm3kR0ef2e05zlOe_Q] already exists",
    "index_uuid" : "mPb8Zm3kR0ef2e05zlOe_Q",
    "index" : "stock"
  },
  "status" : 400
}

아직 실행이 되고 있진 않지만, 보다시피 다른 에러가 발생하며, 이미 인덱스가 존재한다고 표시되었다. 데이터 한건을 넣었을 때 스키마리스이지만 생성된 인덱스가 존재하기 때문인데 인덱스가 있을 경우 무조건 삭제를 해야 한다. 즉, 스키마를 지정할 때는 인덱스가 없는 상태에서 해야 한다는 의미이다.

 

DELETE /stock
{
  "acknowledged" : true
}

위와 같이 인덱스를 삭제 한 후

 

PUT /stock
{
  "mappings":{
    "properties":{
      "stockId":{"type" : "integer"},
      "stockNm":{"type" : "keyword"},
      "stockType":{"type" : "keyword"},
      "stockCd":{"type" : "keyword"},
      "trade":{"type" : "keyword"},
      "current":{"type" : "integer"},
      "max":{"type" : "integer"},
      "min":{"type" : "integer"},
      "description":{"type" : "text"}
    }
  }
}
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "stock"
}

인덱스를 실행하면 이제 인덱스가 생성되었다는 메세지가 뜨게 된다.

 

PUT /stock/_doc/1
{
  "stockId":"1",
  "stockNm":"삼성전자",
  "stockType":"보통주",
  "stockCd":"005930",
  "trade":"KOSPI",
  "current":"59700",
  "max":"62800",
  "min":"42300",
  "description":"삼성전자 주식회사는 전자 제품을 생산하며 정보통신기술에 대한 개발을 진행하고 있는 대한민국의 기업이다. 삼성전자는 삼성그룹 안에서도 가장 규모가 큰, 삼성그룹을 대표하는 기업이기도 하다. 본사는 경기도 수원시 영통구 삼성로 129 에 있다"
}
{
  "_index" : "stock",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

실패했던 레코드를 다시 추가하고

 

GET /stock
{
  "stock" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "current" : {
          "type" : "integer"
        },
        "description" : {
          "type" : "text"
        },
        "max" : {
          "type" : "integer"
        },
        "min" : {
          "type" : "integer"
        },
        "stockCd" : {
          "type" : "keyword"
        },
        "stockId" : {
          "type" : "integer"
        },
        "stockNm" : {
          "type" : "keyword"
        },
        "stockType" : {
          "type" : "keyword"
        },
        "trade" : {
          "type" : "keyword"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1602825001524",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "5eooVzvHQAWWKkC9to5cnA",
        "version" : {
          "created" : "7060299"
        },
        "provided_name" : "stock"
      }
    }
  }
}

인덱스 정보를 호출하면, 등록된 스키마로 데이터가 들어가고 있다는 것을 알 수 있다.

 

 

댓글

Designed by JB FACTORY