Elasticsearch Enrich 프로세서란?
Elasticsearch Enrich 프로세서는 데이터에 추가적인 정보를 자동으로 결합해주는 기능입니다. 이 기능은 Elasticsearch 7.5 버전부터 제공되며, 특정 데이터를 사전에 정의된 정책(policy)에 따라 색인화 과정에서 다른 인덱스의 데이터와 결합할 수 있게 해줍니다.
Enrich 프로세서를 사용하기 위해서는 Elasticsearch 클러스터 내에 인제스트(Ingest) 노드가 필요합니다. Ingest 노드는 Ingest Pipeline을 통해 문서가 처리될 때 필수적인 역할을 하며, 데이터를 통합하거나 변환하는 과정을 처리합니다.
Enrich Index의 특성
- Enrich Index는 Elasticsearch에 의해 내부적으로 관리되는 시스템 인덱스입니다. 이 인덱스는 일반 사용자가 직접 접근하여 변경하는 것은 불가능합니다.
- 모든 Enrich Index는 .enrich-* 형식으로 시작하는 이름을 가집니다.
- Enrich Index는 읽기 전용으로 설정되어 있어, 직접적인 데이터 변경이 불가능합니다. 데이터 수정이 필요한 경우 원본 인덱스 데이터를 수정하고 Enrich Policy를 다시 실행하여 Enrich Index를 갱신해야 합니다.
- Policy를 통해 생성된 Enrich 데이터는 시스템 인덱스에 저장된 후 자동으로 업데이트되지 않습니다. 즉, 소스 데이터(예: 카테고리 정보)가 변경될 경우, 변경된 데이터를 인덱싱에 반영하기 위해서는 Policy를 수동으로 다시 실행해주어야 합니다. 이를 통해 Enrich 인덱스가 갱신됩니다.
- 검색 속도를 최적화하기 위해, Enrich Index는 force merge 되어 저장됩니다.
Enrich를 활용해 다중 문서 통합해보기
이제 제가 구성한 시나리오를 기반으로, Enrich 프로세서를 활용해 상품 정보에 카테고리 정보를 통합하는 실습을 진행해보겠습니다.
이 실습에서 각 상품은 여러 카테고리에 속할 수 있으며 해당 카테고리들의 다양한 속성들이 함께 저장 된다고 가정합니다.
1. 카테고리 인덱스 생성 및 데이터 입력
먼저, 다양한 카테고리 정보를 담고 있는 category_index를 생성하고 데이터를 추가합니다. 이 데이터는 나중에 상품 정보와 통합될 소스 데이터로 사용됩니다.
PUT /category_index/_doc/1
{
"category_code1": "001",
"category_name1": "패션",
"category_title1": "패션",
"category_code2": "001001",
"category_name2": "남성패션",
"category_title2": "남성패션",
"category_code3": "001001001",
"category_name3": "상의",
"category_title3": "상의",
"category_code4": "001001001001",
"category_name4": "티셔츠",
"category_title4": "티셔츠",
"category_type": "ITEM",
"find_code": "001001001001@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/2
{
"category_code1": "002",
"category_name1": "스포츠",
"category_title1": "스포츠",
"category_code2": "002002",
"category_name2": "축구",
"category_title2": "축구",
"category_code3": "002002002",
"category_name3": "용품",
"category_title3": "용품",
"category_code4": "002002002002",
"category_name4": "축구공",
"category_title4": "축구공",
"category_type": "ITEM",
"find_code": "002002002002@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/3
{
"category_code1": "003",
"category_name1": "생활용품",
"category_title1": "생활용품",
"category_code2": "003003",
"category_name2": "주방용품",
"category_title2": "주방용품",
"category_code3": "003003003",
"category_name3": "그릇",
"category_title3": "그릇",
"category_code4": "003003003003",
"category_name4": "접시",
"category_title4": "접시",
"category_type": "ITEM",
"find_code": "003003003003@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/4
{
"category_code1": "004",
"category_name1": "전자제품",
"category_title1": "전자제품",
"category_code2": "004004",
"category_name2": "컴퓨터",
"category_title2": "컴퓨터",
"category_code3": "004004004",
"category_name3": "노트북",
"category_title3": "노트북",
"category_code4": "004004004004",
"category_name4": "게이밍노트북",
"category_title4": "게이밍노트북",
"category_type": "ITEM",
"find_code": "004004004004@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/5
{
"category_code1": "005",
"category_name1": "가전제품",
"category_title1": "가전제품",
"category_code2": "005005",
"category_name2": "청소기",
"category_title2": "청소기",
"category_code3": "005005005",
"category_name3": "무선청소기",
"category_title3": "무선청소기",
"category_code4": "005005005005",
"category_name4": "로봇청소기",
"category_title4": "로봇청소기",
"category_type": "ITEM",
"find_code": "005005005005@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/6
{
"category_code1": "006",
"category_name1": "자동차용품",
"category_title1": "자동차용품",
"category_code2": "006006",
"category_name2": "타이어",
"category_title2": "타이어",
"category_code3": "006006006",
"category_name3": "자동차타이어",
"category_title3": "자동차타이어",
"category_code4": "006006006006",
"category_name4": "겨울용타이어",
"category_title4": "겨울용타이어",
"category_type": "ITEM",
"find_code": "006006006006@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/7
{
"category_code1": "007",
"category_name1": "취미",
"category_title1": "취미",
"category_code2": "007007",
"category_name2": "사진촬영",
"category_title2": "사진촬영",
"category_code3": "007007007",
"category_name3": "카메라",
"category_title3": "카메라",
"category_code4": "007007007007",
"category_name4": "DSLR",
"category_title4": "DSLR",
"category_type": "ITEM",
"find_code": "007007007007@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/8
{
"category_code1": "008",
"category_name1": "도서",
"category_title1": "도서",
"category_code2": "008008",
"category_name2": "소설",
"category_title2": "소설",
"category_code3": "008008008",
"category_name3": "판타지",
"category_title3": "판타지",
"category_code4": "008008008008",
"category_name4": "하이판타지",
"category_title4": "하이판타지",
"category_type": "ITEM",
"find_code": "008008008008@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/9
{
"category_code1": "009",
"category_name1": "게임",
"category_title1": "게임",
"category_code2": "009009",
"category_name2": "비디오게임",
"category_title2": "비디오게임",
"category_code3": "009009009",
"category_name3": "콘솔게임",
"category_title3": "콘솔게임",
"category_code4": "009009009009",
"category_name4": "플레이스테이션",
"category_title4": "플레이스테이션",
"category_type": "ITEM",
"find_code": "009009009009@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
PUT /category_index/_doc/10
{
"category_code1": "010",
"category_name1": "음악",
"category_title1": "음악",
"category_code2": "010010",
"category_name2": "악기",
"category_title2": "악기",
"category_code3": "010010010",
"category_name3": "피아노",
"category_title3": "피아노",
"category_code4": "010010010010",
"category_name4": "그랜드 피아노",
"category_title4": "그랜드 피아노",
"category_type": "ITEM",
"find_code": "010010010010@#ITEM",
"use_yn": "Y",
"display_start_date": "2022-01-01 00:00:00",
"display_end_date": "2030-12-31 23:59:59"
}
2. 상품 인덱스 맵핑 구조 정의
공식 문서에서는 동적인 문서 맵핑으로 데이터 통합 예제를 제공하지만, 이번 실습을 통해 정적인 맵핑 구조로도 유연하게 데이터 통합이 가능하다는 점을 확인하기 위해 타겟 인덱스인 상품 인덱스(test-product)를 정적 구조로 정의합니다.
PUT /test-product
{
"settings": {
"analysis": {
"analyzer": {
"ngram_analyzer": {
"type": "custom",
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 3,
"token_chars": [
"letter",
"digit"
]
}
}
},
"index": {
"max_ngram_diff": 2
}
},
"mappings": {
"dynamic": "strict", // strict 맵핑 구조 설정
"properties": {
"goods_no": {
"type": "keyword"
},
"goods_name": {
"type": "keyword"
},
"brand_name": {
"type": "keyword"
},
"category_code": {
"type": "keyword"
},
"enriched_category": { // 통합된 카테고리 정보 저장
"properties": {
"find_code": {
"type": "keyword",
"fields": {
"analyzed": {
"type": "text",
"analyzer": "ngram_analyzer"
}
}
},
"category_code1": { "type": "keyword" },
"category_name1": { "type": "keyword" },
"category_title1": { "type": "keyword" },
"category_code2": { "type": "keyword" },
"category_name2": { "type": "keyword" },
"category_title2": { "type": "keyword" },
"category_code3": { "type": "keyword" },
"category_name3": { "type": "keyword" },
"category_title3": { "type": "keyword" },
"category_code4": { "type": "keyword" },
"category_name4": { "type": "keyword" },
"category_title4": { "type": "keyword" },
"category_type": {
"type": "keyword",
"fields": {
"analyzed": {
"type": "text",
"analyzer": "ngram_analyzer"
}
}
},
"display_start_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_second"
},
"display_end_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_second"
},
"use_yn": {
"type": "keyword"
}
}
}
}
}
}
3. Enrich Policy 설정
다음으로, Enrich Policy를 설정하여 카테고리 정보를 상품 정보에 통합할 수 있도록 준비합니다.
PUT /_enrich/policy/category_hierarchy_policy
{
"match": {
"indices": "category_index",
"match_field": "find_code",
"enrich_fields": [
"category_code1",
"category_name1",
"category_title1",
"category_code2",
"category_name2",
"category_title2",
"category_code3",
"category_name3",
"category_title3",
"category_code4",
"category_name4",
"category_title4",
"category_type",
"display_start_date",
"display_end_date",
"use_yn"
]
}
}
4. Enrich Policy 실행
POST /_enrich/policy/category_hierarchy_policy/_execute
앞서 언급했듯이, 소스 데이터(예: 카테고리 정보)가 변경되면 변경된 내용을 반영하기 위해 Enrich Policy를 수동으로 다시 실행해야 합니다. 이 작업을 통해 Enrich 인덱스가 갱신되어, 최신 데이터를 인덱싱할 수 있습니다.
5. Ingest Pipeline 설정
Enrich Policy가 설정된 후, 이를 실제 문서에 적용하기 위한 Ingest Pipeline을 구성해야 합니다. 이 Pipeline은 데이터를 인덱싱할 때 카테고리 정보를 상품 문서에 통합하는 역할을 합니다.
PUT _ingest/pipeline/enrich_category_pipeline
{
"processors": [
{
"enrich": {
"policy_name": "category_hierarchy_policy",
"field": "category_code",
"target_field": "enriched_category",
"max_matches": "100"
}
}
]
}
max_matches는 Enrich 프로세서에서 매우 중요한 설정으로, 매칭되는 데이터의 최대 개수를 정의합니다.이 설정은 Enrich Policy에서 지정한 match_field와 일치하는 여러 문서가 있을 때, 몇 개의 문서를 통합할지 결정합니다.
공식 문서에 따르면 최대값은 128이며, 그 이유는 문서의 크기가 지나치게 커지는 것을 방지하기 위함이라고 합니다.
- max_matches: 1로 설정할 경우, 매칭되는 데이터 중 하나만 통합되며,target_field는 JSON 객체 형태가 됩니다.
- max_matches: 2 이상으로 설정할 경우, 여러 매칭된 데이터를 통합할 수 있으며, 이때 target_field는 JSON 배열로 변환됩니다. 예를 들어, 여러 개의 카테고리 정보가 상품에 연결될 경우, 각각의 카테고리가 배열에 담기게 됩니다.
6. 상품 데이터 인덱싱
이제 상품 데이터를 인덱싱할 때, Enrich Pipeline을 사용하여 카테고리 정보를 통합합니다.
일반적인 ES 문서 색인과 동일하지만 URL 파라미터를 보면 pipeline=enrich_category_pipeline가 추가 된 것을 확인할 수 있습니다.
POST /test-product/_doc?pipeline=enrich_category_pipeline
{
"goods_no": "123456",
"goods_name": "멀티 카테고리 상품",
"brand_name": "브랜드명",
"category_code": [
"001001001001@#ITEM",
"002002002002@#ITEM",
"003003003003@#ITEM",
"004004004004@#ITEM",
"005005005005@#ITEM",
"006006006006@#ITEM",
"007007007007@#ITEM",
"008008008008@#ITEM",
"009009009009@#ITEM",
"010010010010@#ITEM"
]
}
7. 통합된 데이터 확인
인덱싱된 데이터를 조회하여 카테고리 정보가 통합된 상태를 확인할 수 있습니다.
GET /test-product/_search
{
"query": {
"match_all": {}
}
}
응답
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "test-product",
"_id": "bxMTb5EBZ_Pa7rwW3zqv",
"_score": 1,
"_source": {
"goods_name": "멀티 카테고리 상품",
"enriched_category": [
{
"category_title3": "콘솔게임",
"category_title4": "플레이스테이션",
"category_name4": "플레이스테이션",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "게임",
"category_title2": "비디오게임",
"use_yn": "Y",
"find_code": "009009009009@#ITEM",
"category_name1": "게임",
"category_type": "ITEM",
"category_name2": "비디오게임",
"category_name3": "콘솔게임",
"category_code4": "009009009009",
"category_code3": "009009009",
"category_code2": "009009",
"category_code1": "009",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "무선청소기",
"category_title4": "로봇청소기",
"category_name4": "로봇청소기",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "가전제품",
"category_title2": "청소기",
"use_yn": "Y",
"find_code": "005005005005@#ITEM",
"category_name1": "가전제품",
"category_type": "ITEM",
"category_name2": "청소기",
"category_name3": "무선청소기",
"category_code4": "005005005005",
"category_code3": "005005005",
"category_code2": "005005",
"category_code1": "005",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "노트북",
"category_title4": "게이밍노트북",
"category_name4": "게이밍노트북",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "전자제품",
"category_title2": "컴퓨터",
"use_yn": "Y",
"find_code": "004004004004@#ITEM",
"category_name1": "전자제품",
"category_type": "ITEM",
"category_name2": "컴퓨터",
"category_name3": "노트북",
"category_code4": "004004004004",
"category_code3": "004004004",
"category_code2": "004004",
"category_code1": "004",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "피아노",
"category_title4": "그랜드 피아노",
"category_name4": "그랜드 피아노",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "음악",
"category_title2": "악기",
"use_yn": "Y",
"find_code": "010010010010@#ITEM",
"category_name1": "음악",
"category_type": "ITEM",
"category_name2": "악기",
"category_name3": "피아노",
"category_code4": "010010010010",
"category_code3": "010010010",
"category_code2": "010010",
"category_code1": "010",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "백팩",
"category_title4": "노트북백팩",
"category_name4": "노트북백팩",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "패션잡화",
"category_title2": "가방",
"use_yn": "Y",
"find_code": "011011011011@#ITEM",
"category_name1": "패션잡화",
"category_type": "ITEM",
"category_name2": "가방",
"category_name3": "백팩",
"category_code4": "011011011011",
"category_code3": "011011011",
"category_code2": "011011",
"category_code1": "011",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "그릇",
"category_title4": "접시",
"category_name4": "접시",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "생활용품",
"category_title2": "주방용품",
"use_yn": "Y",
"find_code": "003003003003@#ITEM",
"category_name1": "생활용품",
"category_type": "ITEM",
"category_name2": "주방용품",
"category_name3": "그릇",
"category_code4": "003003003003",
"category_code3": "003003003",
"category_code2": "003003",
"category_code1": "003",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "판타지",
"category_title4": "하이판타지",
"category_name4": "하이판타지",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "도서",
"category_title2": "소설",
"use_yn": "Y",
"find_code": "008008008008@#ITEM",
"category_name1": "도서",
"category_type": "ITEM",
"category_name2": "소설",
"category_name3": "판타지",
"category_code4": "008008008008",
"category_code3": "008008008",
"category_code2": "008008",
"category_code1": "008",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "상의",
"category_title4": "티셔츠",
"category_name4": "티셔츠",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "패션",
"category_title2": "남성패션",
"use_yn": "Y",
"find_code": "001001001001@#ITEM",
"category_name1": "패션",
"category_type": "ITEM",
"category_name2": "남성패션",
"category_name3": "상의",
"category_code4": "001001001001",
"category_code3": "001001001",
"category_code2": "001001",
"category_code1": "001",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "카메라",
"category_title4": "DSLR",
"category_name4": "DSLR",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "취미",
"category_title2": "사진촬영",
"use_yn": "Y",
"find_code": "007007007007@#ITEM",
"category_name1": "취미",
"category_type": "ITEM",
"category_name2": "사진촬영",
"category_name3": "카메라",
"category_code4": "007007007007",
"category_code3": "007007007",
"category_code2": "007007",
"category_code1": "007",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "용품",
"category_title4": "축구공",
"category_name4": "축구공",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "스포츠",
"category_title2": "축구",
"use_yn": "Y",
"find_code": "002002002002@#ITEM",
"category_name1": "스포츠",
"category_type": "ITEM",
"category_name2": "축구",
"category_name3": "용품",
"category_code4": "002002002002",
"category_code3": "002002002",
"category_code2": "002002",
"category_code1": "002",
"display_end_date": "2030-12-31 23:59:59"
},
{
"category_title3": "자동차타이어",
"category_title4": "겨울용타이어",
"category_name4": "겨울용타이어",
"display_start_date": "2022-01-01 00:00:00",
"category_title1": "자동차용품",
"category_title2": "타이어",
"use_yn": "Y",
"find_code": "006006006006@#ITEM",
"category_name1": "자동차용품",
"category_type": "ITEM",
"category_name2": "타이어",
"category_name3": "자동차타이어",
"category_code4": "006006006006",
"category_code3": "006006006",
"category_code2": "006006",
"category_code1": "006",
"display_end_date": "2030-12-31 23:59:59"
}
],
"brand_name": "브랜드명",
"category_code": [
"001001001001@#ITEM",
"002002002002@#ITEM",
"003003003003@#ITEM",
"004004004004@#ITEM",
"005005005005@#ITEM",
"006006006006@#ITEM",
"007007007007@#ITEM",
"008008008008@#ITEM",
"009009009009@#ITEM",
"010010010010@#ITEM",
"011011011011@#ITEM"
],
"goods_no": "123456"
}
}
]
}
}
번외. Enrich 인덱스의 문서 내용 확인하는 법?
Kibana에서 Enrich 인덱스 문서를 확인하려면 다음 단계를 따릅니다:
- Kibana에서 Index Management로 이동합니다.
- 상단에서 Include hidden indices 옵션을 클릭한 후, 검색창에 enrich를 입력하여 생성된 Enrich 인덱스를 검색합니다.
- 우리가 생성한 .enrich-category_hierarchy_policy 인덱스를 클릭합니다.
- 우측 상단에 있는 나침반 아이콘을 클릭하면, 해당 Enrich 인덱스에 색인된 문서들을 확인할 수 있습니다.
이 방법을 통해 Enrich 인덱스의 내용을 쉽게 확인할 수 있습니다.
결론
이번 리서치와 실습을 통해 Elasticsearch의 Enrich 프로세서를 활용하여 데이터를 효과적으로 통합하는 방법을 익혔습니다. 특히, 공식 문서에서 주로 소개되는 동적 맵핑뿐만 아니라, 정적 맵핑 구조를 사용하여 데이터를 더 엄격하고 명확하게 관리할 수 있다는 점도 확인했습니다.
Enrich 프로세서를 활용할 때 몇 가지 유의사항만 잘 숙지한다면, 다양한 데이터 소스를 결합하고 복합적인 작업을 효율적으로 처리할 수 있을 것으로 보입니다.
참고