자식(has_child) 쿼리

자식(has_child) 쿼리

has_child 쿼리는 특정 쿼리와 일치하는 자식 문서를 가진 부모 문서를 반환해요. 같은 인덱스 안의 문서들 사이에 부모/자식 관계를 맺으려면 조인(join) 필드 유형을 사용하면 됩니다.

has_child 쿼리는 수행하는 조인 연산 때문에 다른 쿼리보다 느려요. 서로 다른 부모 문서를 가리키는 일치하는 자식 문서의 수가 늘어날수록 성능이 저하됩니다. 검색에 포함된 has_child 쿼리 하나하나가 쿼리 성능에 큰 영향을 줄 수 있어요. 속도를 우선시한다면 이 쿼리 사용을 피하거나 최대한 제한하는 게 좋아요.

출처: 문서

본문

예제 (Example)

has_child 쿼리를 실행하려면 먼저 인덱스에 부모/자식 관계를 맺기 위한 조인 필드가 있어야 해요. 인덱스 매핑 요청은 다음과 같은 형식을 사용해요.

PUT /example_index
{
  "mappings": {
    "properties": {
      "relationship_field": {
        "type": "join",
        "relations": {
          "parent_doc": "child_doc"
        }
      }
    }
  }
}

이 예제에서는 제품(products)과 해당 브랜드(brands)를 나타내는 문서가 담긴 인덱스를 구성해 보겠어요.

먼저 인덱스를 만들고 brand와 product 사이의 부모/자식 관계를 맺어요.

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

두 개의 부모(brand) 문서를 인덱싱해요.

PUT testindex1/_doc/1
{
  "name": "Luxury brand",
  "product_to_brand" : "brand" 
}
PUT testindex1/_doc/2
{
  "name": "Economy brand",
  "product_to_brand" : "brand" 
}

세 개의 자식(product) 문서를 인덱싱해요.

PUT testindex1/_doc/3?routing=1
{
  "name": "Mechanical watch",
  "sales_count": 150,
  "product_to_brand": {
    "name": "product", 
    "parent": "1" 
  }
}
PUT testindex1/_doc/4?routing=2
{
  "name": "Electronic watch",
  "sales_count": 300,
  "product_to_brand": {
    "name": "product", 
    "parent": "2" 
  }
}
PUT testindex1/_doc/5?routing=2
{
  "name": "Digital watch",
  "sales_count": 100,
  "product_to_brand": {
    "name": "product", 
    "parent": "2" 
  }
}

자식의 부모를 검색하려면 has_child 쿼리를 사용해요. 다음 쿼리는 시계(watch)를 만드는 부모 문서(브랜드)를 반환해요.

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

응답은 두 브랜드를 모두 반환해요.

{
  "took": 15,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "testindex1",
        "_id": "1",
        "_score": 1,
        "_source": {
          "name": "Luxury brand",
          "product_to_brand": "brand"
        }
      },
      {
        "_index": "testindex1",
        "_id": "2",
        "_score": 1,
        "_source": {
          "name": "Economy brand",
          "product_to_brand": "brand"
        }
      }
    ]
  }
}

내부 히트 가져오기 (Retrieving inner hits)

쿼리와 일치한 자식 문서를 반환하려면 inner_hits 파라미터를 제공해요.

GET testindex1/_search
{
  "query" : {
    "has_child": {
      "type":"product",
      "query": {
        "match" : {
            "name": "watch"
        }
      },
      "inner_hits": {}
    }
  }
}

응답에는 inner_hits 필드에 자식 문서가 포함돼요.

{
  "took": 52,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "testindex1",
        "_id": "1",
        "_score": 1,
        "_source": {
          "name": "Luxury brand",
          "product_to_brand": "brand"
        },
        "inner_hits": {
          "product": {
            "hits": {
              "total": {
                "value": 1,
                "relation": "eq"
              },
              "max_score": 0.53899646,
              "hits": [
                {
                  "_index": "testindex1",
                  "_id": "3",
                  "_score": 0.53899646,
                  "_routing": "1",
                  "_source": {
                    "name": "Mechanical watch",
                    "sales_count": 150,
                    "product_to_brand": {
                      "name": "product",
                      "parent": "1"
                    }
                  }
                }
              ]
            }
          }
        }
      },
      {
        "_index": "testindex1",
        "_id": "2",
        "_score": 1,
        "_source": {
          "name": "Economy brand",
          "product_to_brand": "brand"
        },
        "inner_hits": {
          "product": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": 0.53899646,
              "hits": [
                {
                  "_index": "testindex1",
                  "_id": "4",
                  "_score": 0.53899646,
                  "_routing": "2",
                  "_source": {
                    "name": "Electronic watch",
                    "sales_count": 300,
                    "product_to_brand": {
                      "name": "product",
                      "parent": "2"
                    }
                  }
                },
                {
                  "_index": "testindex1",
                  "_id": "5",
                  "_score": 0.53899646,
                  "_routing": "2",
                  "_source": {
                    "name": "Digital watch",
                    "sales_count": 100,
                    "product_to_brand": {
                      "name": "product",
                      "parent": "2"
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

내부 히트를 가져오는 방법에 대한 자세한 내용은 "Inner hits"를 참고하세요.

파라미터 (Parameters)

다음 표는 has_child 쿼리가 지원하는 모든 최상위 파라미터를 나열해요.

  • type (필수): 조인 필드 매핑에 정의된 자식 관계의 이름을 지정해요.
  • query (필수): 자식 문서에 실행할 쿼리예요. 자식 문서가 쿼리와 일치하면 부모 문서가 반환돼요.
  • ignore_unmapped (선택): 매핑되지 않은 type 필드를 무시하고 오류 대신 문서를 반환하지 않을지 나타내요. 일부 인덱스에 type 필드가 없을 수 있는 여러 인덱스를 쿼리할 때 이 파라미터를 제공할 수 있어요. 기본값은 false예요.
  • max_children (선택): 부모 문서에 대한 일치하는 자식 문서의 최대 수예요. 이 값을 초과하면 부모 문서는 검색 결과에서 제외돼요.
  • min_children (선택): 부모 문서가 결과에 포함되기 위해 필요한 일치하는 자식 문서의 최소 수예요. 충족하지 못하면 부모는 제외돼요. 기본값은 1이에요.
  • score_mode (선택): 일치하는 자식 문서의 점수가 부모 문서의 점수에 어떻게 영향을 주는지 정의해요. 유효한 값은 다음과 같아요.
    • none: 자식 문서의 관련성 점수를 무시하고 부모 문서에 0점을 부여해요.
    • avg: 일치하는 모든 자식 문서의 평균 관련성 점수를 사용해요.
    • max: 일치하는 자식 문서 중 가장 높은 관련성 점수를 부모에 부여해요.
    • min: 일치하는 자식 문서 중 가장 낮은 관련성 점수를 부모에 부여해요.
    • sum: 일치하는 모든 자식 문서의 관련성 점수를 합산해요.
    • 기본값은 none이에요.
  • inner_hits (선택): 제공하면 쿼리와 일치한 기본 히트(자식 문서)를 반환해요.

정렬 제한 (Sorting limitations)

has_child 쿼리는 표준 정렬 옵션을 사용한 결과 정렬을 지원하지 않아요. 자식 문서의 필드를 기준으로 부모 문서를 정렬해야 한다면 function_score 쿼리를 사용하고 부모 문서의 점수로 정렬할 수 있어요.

앞선 예제에서 자식 제품의 sales_count를 기준으로 부모 문서(브랜드)를 정렬할 수 있어요. 다음 쿼리는 점수에 자식 문서의 sales_count 필드를 곱하고, 일치하는 자식 문서 중 가장 높은 관련성 점수를 부모에 부여해요.

GET testindex1/_search
{
  "query": {
    "has_child": {
      "type": "product",
      "query": {
        "function_score": {
          "script_score": {
            "script": "_score * doc['sales_count'].value"
          }
        }
      },
      "score_mode": "max"
    }
  }
}

응답은 가장 높은 자식 sales_count 순으로 정렬된 브랜드를 포함해요.

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 300,
    "hits": [
      {
        "_index": "testindex1",
        "_id": "2",
        "_score": 300,
        "_source": {
          "name": "Economy brand",
          "product_to_brand": "brand"
        }
      },
      {
        "_index": "testindex1",
        "_id": "1",
        "_score": 150,
        "_source": {
          "name": "Luxury brand",
          "product_to_brand": "brand"
        }
      }
    ]
  }
}

다음 단계 (Next steps)

  • 내부 히트를 가져오는 방법에 대해 자세히 알아보세요.

더 알아보기 (Learn more)