[ElasticSearch] 계산된 필드 반환하기 - Runtime Mapping

조갱·2022년 10월 30일
0

ElasticSearch

목록 보기
4/7

이곳에 있는 데이터 모델을 기반으로 합니다.

이 사람이 몇년도 생이지..?

공무원 김공무(27)은, DB에 있는 사람들이 몇년도 생인지 궁금하다.

{
  "name":"김이름",
  "gender":"M",
  "age": 27,
  "address": {
    "personId": 1,
    "country": "KR",
    "city": "Suwon"
  }
}

27살은
2022(올해) - 27(나이) + 1(태어나자마자 1살이기에)
을 하면 1996년생임을 계산할 수 있다.

> ES 버전이 7.11 이상이라면
럭키! 인덱스를 수정하지 않고 search 쿼리만으로 가능하다.

> ES 버전이 7.11 미만이라면
저런,, 인덱스를 수정해야만 한다.

Runtime Mappings

인덱스에 구현

mappings 하위에 runtime 필드를 추가한다.

PUT /person
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "runtime": {
       "birthYear": {
        "type": "long",
        "script": {
          "source": "emit(2022-doc['age'].value+1)",
          "lang": "painless"
        }
      }
    },
    "properties": {
      "_class": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "personId": {
        "type": "keyword"
      },
      "name": {
        "type": "keyword"
      },
      "gender": {
        "type": "keyword"
      },
      "age": {
        "type": "integer"
      },
      "address": {
        "type": "nested",
        "properties": {
          "personId": {
            "type": "keyword"
          },
          "country": {
            "type": "keyword"
          },
          "city": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

쿼리에 구현

query 바깥에 runtime_mappings 필드를 추가해준다.

GET /person/_search
{
  "runtime_mappings": {
    "birthYear2": {
      "type": "long",
      "script": {
        "source": "emit(2022-doc['age'].value+1)",
        "lang": "painless"
      }
    }
  },
  "fields": [
    "birthYear2"
  ],
  "query": {
    "match_all": {}
  }
}

알아둘 것

  • script의 source 는 반드시 emit() 로 반환해야한다.
  • 단순히 런타임 필드를 출력하기 위해서는 fields 필드에서 함께 노출할 런타임 매핑 필드를 명시해야한다.

어디에 활용할 수 있을까?

사실 위 예제와 같이 단순히 계산 값을 출력할 때에는 runtime mappings 를 사용하지 않아도 된다. 오히려 코드레벨에서 계산하는게 유지보수가 더 쉽다.

더군다나, field 필드에 노출할 런타임 필드를 명시해주는것도 썩 불편하다. response 형식도 맘에 안들고.

Aggs로 집계된 데이터 내부에서 계산할 때 사용하면 좋다.

MySQL 쿼리로 치면,,

SELECT product_no, SUM(price * order_cnt) AS total_order_price
FROM statistic
GROUP BY product_no

ES 쿼리로 치면,,

GET /statistic/_search
{
  "runtime_mappings": {
    "totalOrderPrice": { // 여기있는 런타임 필드가
      "type": "long",
      "script": {
        "source": "emit(doc['price'].value * doc['order_cnt'].value)",
        "lang": "painless"
      }
    }
  },
  "aggs": {
    "byProductNo": {
      "terms": {
        "field": "productNo"
      }
    },
    "totalOrderPrice": {
      "sum": {
        "field": "totalOrderPrice" // 요기랑 매핑된다.
      }
    }
  }
}

Reference
Runtime mapping 공식 문서 : https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html

profile
A fast learner.

0개의 댓글