[엘라스틱서치] 스키마(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