[조인 쿼리]
- RDB보다는 몽고DB(조인을 아예 하지 않거나, 애플리케이션 수준에서 진행)같은 noSQL에서의 조인과 가깝다
- 비정규화는 데이터 저장소를 더 많이 먹지 않는가?
- ㅇㅇ 근데 es는 메인 저장소로 쓰지 않는 것을 추천하기 때문에 ㄱㅊ (검색만 빠르면 괜찮다 주의)
- es에서 간단한 조인 기능들을 제공하기는 하지만, 비효율적이기 때문에 많은 도큐먼트를 다룰 때는 좋지 않다
- mapping 옵션에서 join property를 줄 수 있다.
- 아래 예시를 참고. (이때 department가 꼭 조인하는 인덱스의 명칭과 똑같을 필요는 없다.)
PUT /department
{
"mappings": {
"properties": {
"join_field": {
"type": "join",
"relations": {
"department": "employee"
}
}
}
}
}
- 구조는 department 가 employee의 부모가 되는 형식이다. (하나의 department : 여러 employee)
- department에 도큐먼트를 추가하고 싶을 때의 예제
- 조인 필드를 사용해 관계를 위한 문서를 추가할 땐 해당 문서가 어떤 관계의 일부분인지를 명시해야 한다.
PUT /department2/_doc/1
{
"name": "Development",
"join_field": "department"
}
- employee 인덱스에 도큐먼트를 추가하고 싶을 때의 예제
PUT /department2/_doc/3?routing=1 // 부모 문서의 라우팅 값이 꼭 들어가야 한다 -> 부모& 자식이 같은 샤드 내에 저장되어야 하기 때문에
{
"name": "Yeonju Lee",
"age": 25,
"gender": "F",
"join_field": {
"name": "employee", // 이 문서가 employee의 문서라는 것
"parent": 1
}
}
- 부모 도큐먼트 id에 기반해서 자식 도큐먼트를 반환하는 쿼리
GET /department2/_search
{
"query": {
"parent_id": {
"type": "employee", -- 검색 결과의 타입
"id": 1 -- 검색 대상이 되는 부모 문서(여기서는 department)의 id 값
}
}
}
- 부모 도큐먼트의 ID를 모르거나 도큐먼트 id 외에 다른 조건을 추가하길 원할 때 사용하는 쿼리
- 부모 도큐먼트가 기준에 부합하는 경우 쿼리가 자녀 도큐먼트를 반환
GET /department2/_search
{
"query": {
"has_parent": {
"parent_type": "department",
"query": {
"term": {
"name.keyword": "Development"
}
}
}
}
}
- 기본적으로 쿼리는 일치하는 부모 도큐먼트의 관계도 점수를 무시한다. (= 일치하는 부모의 도큐먼트 관계도 점수가 자녀 도큐먼트의 관계도 점수와 무관하다.)
- 관계도 점수는 전부 1로 표기되는데, 이는 "score": true로 두면 관계도가 증가한다.
- 항상 하나의 부모 도큐먼트만 검색되는 것은 아니다. 용어 쿼리 등을 이용해서 매칭되는 도큐먼트가 여러 개 나올 수 있음.
- 이 때 부모 도큐먼트가 얼마나 쿼리에 매칭되는지가 중요한 척도가 될 수 있다.
- 반대로 has_child 쿼리를 이용하면 조건에 맞는 자식 도큐먼트를 가지고 있는 부모 도큐먼트를 찾을 수도 있다.
- 관계도 점수에 따른 조건을 추가할 수도 있다.
- min : 자식 도큐먼트들의 점수 중 가장 낮은 점수가 매핑됨
- max : 자식 도큐먼트들의 점수 중 가장 높은 점수가 매핑됨
- sum : 조건에 부합한 자식의 점수가 총합되어 매핑됨
- avg : 조건에 부합한 자식의 평균 점수가 매핑됨
- none : 자식 도큐먼트의 점수는 무시된다. (디폴트 설정)
- 갖고 있는 자식 문서의 수에 따른 조건을 추가할 수도 있다.
- min_children : 2 => 최소 두 개의 자식 도큐먼트를 가지고 있는 경우만 부모 도큐먼트를 반환
- max_children : 5 => 최대 다섯 개의 자식 도큐먼트를 가지고 있는 경우만 부모 도큐먼트를 반환
- 정렬을 위해서 function_score 라는 파라미터를 쓸 수도 있다.
GET /_search
{
"query": {
"has_child": {
"type": "child",
"query": {
"function_score": {
"script_score": {
"script": "_score * doc['click_count'].value"
}
}
},
"score_mode": "max"
}
}
}
[멀티 레벨 관계]
- 한 관계가 여러 레벨의 관계 타입을 가지고 있는 것
- 아래 예시는 Employee > Department > Company | Supplier > Company 의 관계 구조를 가지고 있음
PUT /company
{
"mappings": {
"_doc": {
"properties": {
"join_field": {
"type": "join",
"relations": {
"company": ["department", "supplier"],
"department": "employee"
}
}
}
}
}
}
- relations 객체에 지금 존재하는 관계 타입의 이름을 적어서 관계 구조를 명시
- 위의 예시에서 도큐먼트를 인덱싱하는 방법
PUT /company/_doc/2
{
"name": "MIRI.DIH",
"join_field": "company"
}
PUT /company/_doc/2?routing=1
{
"name": "Development",
"join_field": {
"name": "department",
"parent": 1
}
}
PUT /company/_doc/3?routing=1
{
"name": "Bo Andersen",
"join_field": {
"name": "employee",
"parent": 2
}
}
- 도큐먼트 구조 상 가장 상위 root와 동일한 샤드에 위치하기 위해서 routing을 이용하게 된다. (모든 하위 도큐먼트는 상위 도큐먼트와 같은 샤드에 위치해 있어야 함)
- 멀티 레벨 관계에서의 쿼리 (한 단계 조인과 동일)
- has_parent
- has_children
- parent_id
GET /company/_search
{
"query": {
"has_child": {
"type": "department",
"query": { // 쿼리 조건에 부합하는 department를 찾음
"has_child": {
"type": "employee",
"query": { // John Doe라는 이름을 가진 직원을 찾음
"term": {
"name.keyword": "John Doe"
}
}
}
}
}
}
}
- inner_hit은 query 필드에도 사용될 수 있다.
- "inner_hit" : {}를 추가
- 어떤 직원이 부서가 쿼리 조건에 부합하는 것에 영향을 미쳤는지 알 수 있음
[terms lookup]
- 쿼리에 조회 대상이 되는 용어가 여러 개 (몇 백개 이상) 될 수도 있다.
- 이 경우를 쿼리에 적기에는 쿼리가 너무 커지고 가독성이 떨어진다.
- => terms lookup mechanism 을 제공
- 용어를 검색하는 대상 인덱스 타입과 도큐먼트의 id를 명시하도록 한다
- 용어가 저장된 필드도 명확하게 알아야 한다.
- 맑은 정신일 때 다시 봐야겠다.
- es의 메모리와 계산 파워는 용어의 수에 비례해서 많이 필요하기 때문에 클러스터의 안정성 면에서 많은 용어들을 쿼리하는 것은 피하는 것이 좋다.
- 각 인덱스 당 최대 65000 용어만 쿼리가 가능하다.
[조인의 한계점]
- 모든 조인 관계는 같은 인덱스 내에 저장되어야 한다.
- 성능을 위한 제한임. 다른 인덱스랑 조인하려고 하면 굉장히 느려질 것
- root 부모 ~ leaf 자식까지는 다 같은 샤드 안에 있어야 한다.
- 인덱스 당 딱 하나의 조인 필드만 가질 수 있다. 하지만 조인 필드에 대해 원하는 만큼의 도큐먼트 관계를 매핑할 수 있고 기존 조인 필드에 항상 새로운 관계를 추가할 수 있기 때문에 큰 문제는 아니다.
- 자식 관계는 존재하는 부모에 대해서만 추가될 수 있다.
- 한 문서는 하나의 부모만 가진다. (e.g. 하나의 employee는 하나의 department 만 가질 수 있음 / 하나의 department는 여러 employee O)
[조인 필드 성능 고려지점]
- 가능하면 조인을 쓰지 말아라. (성능적으로 안 좋음)
- 조인 필드를 쓰는 인덱스에 도큐먼트를 추가할 수록 has_child, has_parent 쿼리는 느려진다.
- 멀티레벨 관계를 사용하면 각 레벨마다 오버헤드가 늘어난다. 따라서 진짜 웬만하면 쓰지 마라.
- 그럼에도 불구하고 조인 필드를 쓰는 게 적절하고 성능도 나쁘지 않을 때가 있다.
- 일대다 관계를 가지고, 한 타입이 다른 타입에 비해 훨씬 그 수가 많은 경우
e.g. 부모 도큐먼트 = 레시피, 자식 도큐먼트 = 재료
- 일대다 관계를 가지고, 한 타입이 다른 타입에 비해 훨씬 그 수가 많은 경우
- 그럼에도 불구하고 조인 필드를 쓰는 게 적절하고 성능도 나쁘지 않을 때가 있다.
- 그럼에도.. 왜 조인을 사용하게 되는가
- 도큐먼트의 수가 한정적이고 많이 변하지 않는 경우 : O
- 조인이 없으면 관계를 어떻게 표현해야 하는가
- nested data type 쓰기
- 여의치 않을 수 있음.. 필요한 내용과 다르거나..
- 일반적으로는.. 관계를 표현하지 마라
- es는 RDB랑 다르다. 다른 구조다. 그러니 es의 구조를 따라라.
- nested data type 쓰기
- 조인을 써야하는 특정 이유가 있는 게 아니라면
- 데이터를 비정규화 먼저 해보고 그게 소용 없을 때 join 필드를 사용해라
'TIL > 엘라스틱 서치' 카테고리의 다른 글
Elastic Search : Complete Guide to ElasticSearch - 매핑과 분석기에 대한 추천 (0) | 2024.02.04 |
---|---|
Elastic Search : Complete Guide to ElasticSearch - 리인덱싱 (0) | 2024.01.21 |
Elastic Search : Complete Guide to ElasticSearch - 매핑 (1) | 2024.01.21 |
Elastic Search : Complete Guide to ElasticSearch - Mapping & Analysis (0) | 2024.01.17 |
Elastic Search : Complete Guide to ElasticSearch - Bulk API (0) | 2024.01.16 |
댓글