elasticsearch nested 타입과 다른 타입의 조건들과 함께 추가, 수정, 삭제, 조회(CRUD) 하는 법

개발새발·2022년 7월 31일
0

elasticsearch

목록 보기
31/54

nested type을..아주 많이..사용해야하는 경우가 생겨버렸다…. 🥺 디테일하게 살펴보쟈..!

기본 데이터

처음에 nested type 필드를 매핑해놓고 아래처럼 몇개 데이터를 미리 넣어뒀다.

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "6AfWQ4IB18RgfuY2grRT",
                "_score": 1.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2": "naoek"
                        }
                    ],
                    "test_key": "key1"
                }
            },
            {
                "_index": "text_index",
                "_type": "_doc",
                "_id": "6QfWQ4IB18RgfuY2grRT",
                "_score": 1.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2e": "sgsgs"
                        }
                    ],
                    "test_key": "key2"
                }
            }
        ]
    }
}

1. nested type안 여러 object 추가

기본데이터에 nested 타입에 object 데이터를 추가해보고자 했는데, 처음의 request 형태와 response 형태는 바로 아래 코드와 같았다.

Request

POST test_index/_update_by_query
{
 "script": {
    "source": "ctx._source.nestedtypefield.add(params.new_param),
    "lang": "painless",
    "params" : {
        "new_param" : [
            {"field1" : "asdf", "field2" : "asdf"},
						{"field1" : "1234", "field2" : "sfdsf"},
        ]
    }
  },
  "query": {
    "terms": {
      "test_key": ["key1", "key2"]
    }
  }
}

추가된 데이터 형태

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 2.0,
        "hits": [
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4gdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2": "naoek"
                        },                    
                        [
                            {"field1" : "asdf", "field2" : "asdf"},
                            {"field1" : "1234", "field2" : "sfdsf"},
                        ]
                    ],
                    "test_key": "key1"
                }
            },
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4wdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2e": "sgsgs"
                        }
                        [
                            {"field1" : "asdf", "field2" : "asdf"},
                            {"field1" : "1234", "field2" : "sfdsf"},
                        ]
                    ],
                    "test_key": "key2"
                }
            }
        ]
    }
}

그런데 이건 내가 원하던 형태가 아니였다. ㅠㅠ 넣어보니 위 response처럼 데이터가 nested 타입의 필드 안에 배열 그대로 들어간 게 아닌가..ㅂㄷㅂㄷ… 내가 원하는 건 nested 타입 필드에 object형태로 차곡차곡 쌓이는 형태였다.
다시 구글을 열심히 뒤져봐서 다른 addAll 이란 메소드를 사용해야한다는 것을 알게 되었다. 🤓 아래 코드를 통해 결국 내가 원하는 결과를 얻었다!

Request

POST  test_index/_update_by_query
{
 "script": {
    "source": "ctx._source.nestedtypefield.addAll(params.new_param)",
    "lang": "painless",
    "params" : {
        "new_param" : [
            {"field1" : "asdf", "field2" : "asdf"},
						{"field1" : "1234", "field2" : "sfdsf"},
        ]
    }
  },
  "query": {
    "terms": {
      "test_key": ["key1", "key2"]
    }
  }
}

추가된 데이터 형태

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 2.0,
        "hits": [
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4gdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2": "naoek"
                        },
												{
														"field1" : "asdf",
														"field2" : "asdf"
												},
                        {
														"field1" : "1234",
														"field2" : "sfdsf"
												}
                    ],
                    "test_key": "key1"
                }
            },
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4wdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2e": "sgsgs"
                        }
                        {
														"field1" : "asdf",
														"field2" : "asdf"
												},
                        {
														"field1" : "1234",
														"field2" : "sfdsf"
												}
                    ],
                    "test_key": "key2"
                }
            }
        ]
    }
}

2. nested type안 object 수정

이번에는 nested type안에 object를 수정하기 위한 작업을 위해 서치를 해서 쿼리문을 만들어봤다.
여기서, 이미 query 조건에 "nestedtypefield.field1": "asdf" 이게 있는데 왜 script에서 findAll 를 사용해서 타켓을 for문으로 하나씩 수정해주지? 라는 생각이 들 수 있다. 이건 아래 조회에서 더 자세히 볼 수 있을 텐데, query 조건으로 nested type을 검색해주면 inner_hits 라는 응답 부분에는 쿼리문 조건에 맞는 nested type의 object들만 노출되지만 기본적으로 사용하는 _source 응답 부분에는 조건에 맞는 nested type의 object 데이터 + 그 nested type에 존재하는 다른 object 데이터들이 존재한다. 그래서 script에서 사용하고 있는 ctx._source 를 이용하여 수정하기 위해서는, 일단 쿼리문으로 조건에 해당하는 데이터를 가져와서 nested type 필드안에서 직접 for문으로 데이터를 수정해줘야한다. 이게.. 성능에 많은 영향을 끼칠 것 같지만.. 일단 내가 생각한 최선이였다…ㅠㅠ

Request

POST test_index/_update_by_query  
{
   "script": {
    "source": "def targets = ctx._source.nestedtypefield.findAll(test -> test.field1== params.update_field1); for(update_target in targets) { update_target.field2 = params.update_field2 }",
    "lang": "painless",
    "params" : {
        "update_field1" : "asdf",
        "update_field2" : "update했다!"
     }
  }, 
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "nestedtypefield",
            "query": {
              "term": {
                "nestedtypefield.field1": "asdf"
              }
            }
          }
        }
      ]
    }
  }
}

수정된 데이터 형태

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 2.0,
        "hits": [
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4gdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2": "naoek"
                        },
												{
														"field1" : "asdf",
														"field2" : "update했다!"
												},
                        {
														"field1" : "1234",
														"field2" : "sfdsf"
												}
                    ],
                    "test_key": "key1"
                }
            },
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4wdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
                            "field1": "asdfsdf",
                            "field2": "update됨"
                        },
                        {
                            "field1": "asdfsdf",
                            "field2e": "sgsgs"
                        }
                        {
														"field1" : "asdf",
														"field2" : "update했다!"
												},
                        {
														"field1" : "1234",
														"field2" : "sfdsf"
												}
                    ],
                    "test_key": "key2"
                }
            }
        ]
    }
}

3. nested type 삭제

삭제도 update와 비슷한 방법인데, removeIf 라는 메소드를 써서 더 간단하게 작업할 수 있다.

Request

POST  test_index/_update_by_query
{
   "script": {
    "source": "ctx._source.nestedtypefield.removeIf(it->params.remove_field1 == it.field1)",
    "lang": "painless",
    "params" : {
        "remove_field1" : "asdfsdf"
    }
  },
    "query": {
        "bool": {
            "must": [{
                "terms": {
                    "test_key": ["key1","key2"]
                }
            }]
        }
    }
}

삭제된 데이터 형태

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 2.0,
        "hits": [
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4gdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
												{
														"field1" : "asdf",
														"field2" : "update했다!"
												},
                        {
														"field1" : "1234",
														"field2" : "sfdsf"
												}
                    ],
                    "test_key": "key1"
                }
            },
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4wdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
														"field1" : "asdf",
														"field2" : "update했다!"
												},
                        {
														"field1" : "1234",
														"field2" : "sfdsf"
												}
                    ],
                    "test_key": "key2"
                }
            }
        ]
    }
}

4. nested type 조회

이번에는 조회!
아래와 같이 query문으로 요청을 보낼 수 있고, must 안에 조건을 추가하거나 bool 안에 filter 등을 사용하여 추가로 조건을 붙일 수 있다.
또, 2. nested type안 object 수정 에서 설명했듯이 nested type 조건이 일치하는 데이터와 함께 inner_hits 에 노출하고자하는 nested type 필드명을 적고 nested type 안에 object들을 정렬할 수도 있다. 이때, 정렬은 inner_hits 안에서 된다.

Request

GET test_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "terms": {
            "_index": [
              "test_indx"
            ]
          }
        },
        {
             "nested": {
                "path": "nestedtypefield",
                "query": {
                    "term" : {
                        "nestedtypefield.field1" : "1234"
                     }
                },
                "inner_hits": {
                     "highlight": {
                        "fields": {
                            "nestedtypefield": {}
                        }
                     },
                     "sort" : {
                         "nestedtypefield.field2" : "asc" 
                     }
                }
            }
        }
      ]
    }
  },
  "from": 0,
  "size": 100
}

조회 결과

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 16.028084,
				"hits": [
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4gdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
												{
														"field1" : "asdf",
														"field2" : "update했다!"
												},
                        {
														"field1" : "1234",
														"field2" : "789"
												},
												{
														"field1" : "1234",
														"field2" : "456"
												}
                    ],
                    "test_key": "key1"
                },
								"inner_hits": {
                    "customer_tags": {
                        "hits": {
                            "total": {
                                "value": 2,
                                "relation": "eq"
                            },
                            "max_score": null,
                            "hits": [
                                {
                                    "_index": "test_index",
                                    "_type": "_doc",
                                    "_id": "5Qe5QoIB18RgfuY2grT6",
                                    "_nested": {
                                        "field": "nestedtypefield",
                                        "offset": 2
                                    },
                                    "_score": null,
                                    "_source": {
																				"field1" : "1234",
																				"field2" : "456"
																		},
                                    "sort": [
                                        1595203200000
                                    ]
                                },
                                {
                                    "_index": "test_index",
                                    "_type": "_doc",
                                    "_id": "5Qe5QoIB18RgfuY2grT6",
                                    "_nested": {
                                        "field": "nestedtypefield",
                                        "offset": 0
                                    },
                                    "_score": null,
                                    "_source": {
																				"field1" : "1234",
																				"field2" : "789"
																		},
                                    "sort": [
                                        1650412800000
                                    ]
                                }
                            ]
                        }
                    }
                }
            },
            {
                "_index": "test_index",
                "_type": "_doc",
                "_id": "4wdhQoIB18RgfuY28rTG",
                "_score": 2.0,
                "_source": {
                    "nestedtypefield": [
                        {
														"field1" : "asdf",
														"field2" : "update했다!"
												},
                        {
														"field1" : "1234",
														"field2" : "1000"
												},
												{
														"field1" : "1234",
														"field2" : "4500"
												}
                    ],
                    "test_key": "key2"
                },
								"inner_hits": {
                    "customer_tags": {
                        "hits": {
                            "total": {
                                "value": 3,
                                "relation": "eq"
                            },
                            "max_score": null,
                            "hits": [
                                {
                                    "_index": "test_index",
                                    "_type": "_doc",
                                    "_id": "5Qe5QoIB18RgfuY2grT6",
                                    "_nested": {
                                        "field": "nestedtypefield",
                                        "offset": 2
                                    },
                                    "_score": null,
                                    "_source": {
																				"field1" : "1234",
																				"field2" : "1000"
																		},
                                    "sort": [
                                        1595203200000
                                    ]
                                },
                                {
                                    "_index": "tmp_product10",
                                    "_type": "_doc",
                                    "_id": "5Qe5QoIB18RgfuY2grT6",
                                    "_nested": {
                                        "field": "customer_tags",
                                        "offset": 0
                                    },
                                    "_score": null,
                                    "_source": {
                                        "field1" : "1234",
																				"field2" : "4500"
																		},
                                    "sort": [
                                        1650412800000
                                    ]
                                }
                            ]
                        }
                    }
                }
            }
        ]
    }
}
profile
발새발개

0개의 댓글