Join 필드 타입

Join 필드 타입

join 필드 타입은 같은 인덱스 안의 문서들 사이에 부모/자식 관계를 정의해요. 쿼리가 관련 문서를 연결할 수 있도록 문서들이 어떻게 관련되어 있는지 기록하는 다음 작업을 수행해요.

  • 관계 이름 정의: 부모와 자식 타입의 이름을 지정해요(예: brand와 product).
  • 관계 메타데이터 저장: 어떤 문서가 부모이고 어떤 문서가 자식이며 어떻게 연결되는지 식별해요.
  • 부모/자식 쿼리 지원: 자식으로 부모를 찾거나 부모로 자식을 찾는 join 쿼리를 지원해요.

출처: 문서

본문

예시

다음 예시에서는 브랜드가 부모이고 제품이 자식인 인덱스를 만들어요.

1단계: 매핑 만들기

제품과 브랜드 사이에 부모/자식 관계를 설정하는 매핑을 만들어 볼게요. name 필드는 제품 또는 브랜드 이름을 저장해요. product_to_brand 필드는 "brand": "product" 관계를 정의하는 join 필드로, 브랜드 문서가 제품 자식을 가질 수 있음을 나타내요.

PUT testindex1
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "product_to_brand": {
        "type": "join",
        "relations": {
          "brand": "product"
        }
      }
    }
  }
}

2단계: 부모 문서(브랜드) 색인

브랜드를 나타내는 부모 문서를 색인하고, product_to_brand 필드에서 부모/자식 관계에서의 역할("brand")을 정의해요.

PUT testindex1/_doc/1
{
  "name": "Brand 1",
  "product_to_brand": {
    "name": "brand" 
  }
}

객체 표기법 없이 줄여서 부모 문서를 색인할 수도 있어요.

PUT testindex1/_doc/1
{
  "name": "Brand 1",
  "product_to_brand" : "brand" 
}

3단계: 자식 문서(제품) 색인

자식 문서를 색인할 때는 routing 쿼리 파라미터를 지정해야 해요. 같은 부모/자식 계층에 있는 부모·자식 문서는 같은 샤드에 색인되어야 하기 때문이에요. 자세한 내용은 Routing 문서를 참고하세요.

각 자식 문서는 join 필드의 parent 필드에서 부모의 ID를 참조해요.

제품을 나타내는 자식 문서 두 개를 색인해 볼게요. 각 문서의 부모/자식 관계 역할("product")을 product_to_brand 필드에 정의하고, ID가 1인 브랜드에 속한다는 것을 지정해요.

PUT testindex1/_doc/3?routing=1
{
  "name": "Product 1",
  "product_to_brand": {
    "name": "product", 
    "parent": "1" 
  }
}
PUT testindex1/_doc/4?routing=1
{
  "name": "Product 2",
  "product_to_brand": {
    "name": "product",
    "parent": "1"
  }
}

이 단계가 끝나면 인덱스에는 이 구조의 문서 세 개가 들어 있어요.

문서 ID 문서 타입 name 필드 product_to_brand 필드
1 부모 (brand) "Brand 1" {"name": "brand"}
3 자식 (product) "Product 1" {"name": "product", "parent": "1"}
4 자식 (product) "Product 2" {"name": "product", "parent": "1"}

join 필드 쿼리

join 필드를 쿼리하면 응답에 반환된 문서가 부모인지 자식인지 지정하는 하위 필드가 포함돼요. 자식 객체의 경우 부모 ID도 함께 반환돼요.

모든 문서 검색

GET testindex1/_search
{
  "query": {
    "match_all": {}
  }
}

응답은 문서가 부모인지 자식인지를 나타내요.

{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "testindex1",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "Brand 1",
          "product_to_brand" : {
            "name" : "brand"
          }
        }
      },
      {
        "_index" : "testindex1",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.0,
        "_routing" : "1",
        "_source" : {
          "name" : "Product 1",
          "product_to_brand" : {
            "name" : "product",
            "parent" : "1"
          }
        }
      },
      {
        "_index" : "testindex1",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 1.0,
        "_routing" : "1",
        "_source" : {
          "name" : "Product 2",
          "product_to_brand" : {
            "name" : "product",
            "parent" : "1"
          }
        }
      }
    ]
  }
}

부모의 모든 자식 검색

Brand 1과 연관된 모든 제품을 찾아볼게요.

GET testindex1/_search
{
  "query" : {
    "has_parent": {
      "parent_type":"brand",
      "query": {
        "match" : {
          "name": "Brand 1"
        }
      }
    }
  }
}

응답에는 Brand 1과 연관된 Product 1과 Product 2가 포함돼요.

{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "testindex1",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.0,
        "_routing" : "1",
        "_source" : {
          "name" : "Product 1",
          "product_to_brand" : {
            "name" : "product",
            "parent" : "1"
          }
        }
      },
      {
        "_index" : "testindex1",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 1.0,
        "_routing" : "1",
        "_source" : {
          "name" : "Product 2",
          "product_to_brand" : {
            "name" : "product",
            "parent" : "1"
          }
        }
      }
    ]
  }
}

자식의 부모 검색

Product 1의 부모를 찾아볼게요.

GET testindex1/_search
{
  "query" : {
    "has_child": {
      "type":"product",
      "query": {
        "match" : {
            "name": "Product 1"
        }
      }
    }
  }
}

응답은 Brand 1을 Product 1의 부모로 반환해요.

{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "testindex1",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "Brand 1",
          "product_to_brand" : {
            "name" : "brand"
          }
        }
      }
    ]
  }
}

자식이 많은 부모 (Parent with many children)

하나의 부모는 많은 자식을 가질 수 있어요. 여러 자식을 가진 매핑을 만들어 볼게요.

PUT testindex1
{
  "mappings": {
    "properties": {
      "parent_to_child": {
        "type": "join",
        "relations": {
          "parent": ["child 1", "child 2"]  
        }
      }
    }
  }
}

join 필드 타입 참고 사항

  • 인덱스에는 join 필드 매핑이 하나만 있을 수 있어요.
  • 자식 문서를 검색, 업데이트, 삭제할 때는 routing 파라미터를 제공해야 해요. 같은 관계에 있는 부모·자식 문서는 같은 샤드에 색인되어야 하기 때문이에요.
  • 여러 부모는 지원되지 않아요.
  • 기존 문서가 이미 부모로 표시된 경우에만 그 문서에 자식 문서를 추가할 수 있어요.
  • 기존 join 필드에 새 관계를 추가할 수 있어요.

다음 단계 (Next steps)

  • join 필드에서의 조인 쿼리에 대해 알아보기.
  • inner hits 검색에 대해 자세히 알아보기.

더 알아보기 (Learn more)