Запрос has_child

Запрос has_child возвращает родительские документы, дочерние документы которых соответствуют определенному запросу.

Запрос has_child возвращает родительские документы, дочерние документы которых соответствуют определенному запросу. Вы можете установить родительско-дочерние отношения между документами в одном индексе, используя тип поля join.

Запрос has_child медленнее, чем другие запросы, из-за операции соединения, которую он выполняет. Производительность снижается по мере увеличения количества соответствующих дочерних документов, указывающих на разные родительские документы. Каждый запрос has_child в вашем поиске может значительно повлиять на производительность запроса. Если вы придаете приоритет скорости, избегайте использования этого запроса или ограничьте его использование насколько возможно.

Пример

Перед тем как вы сможете выполнить запрос has_child, ваш индекс должен содержать поле join для установления родительско-дочерних отношений. Запрос на отображение индекса использует следующий формат:

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

В этом примере вы настроите индекс, который содержит документы, представляющие продукты и их бренды.

Сначала создайте индекс и установите родительско-дочерние отношения между брендом и продуктом:

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

Индексуйте два родительских (брендовых) документа:

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

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

Индексуйте три дочерних (продуктовых) документа:

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. Следующий запрос возвращает родительские документы (бренды), которые производят часы:

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"
        }
      }
    ]
  }
}

Извлечение внутренних результатов (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).

Параметры

Следующая таблица перечисляет все параметры верхнего уровня, поддерживаемые запросами has_child.

Параметр Обязательный/Необязательный Описание
type Обязательный Указывает имя дочерней связи, как определено в отображении поля join.
query Обязательный Запрос, который будет выполнен на дочерних документах. Если дочерний документ соответствует запросу, возвращается родительский документ.
ignore_unmapped Необязательный Указывает, следует ли игнорировать неотображаемые поля типов и не возвращать документы вместо того, чтобы вызывать ошибку. Этот параметр можно указать при запросе нескольких индексов, некоторые из которых могут не содержать поле типа. По умолчанию false.
max_children Необязательный Максимальное количество соответствующих дочерних документов для родительского документа. Если превышено, родительский документ исключается из результатов поиска.
min_children Необязательный Минимальное количество соответствующих дочерних документов, необходимых для включения родительского документа в результаты. Если не достигнуто, родитель исключается. По умолчанию 1.
score_mode Необязательный Определяет, как оценки соответствующих дочерних документов влияют на оценку родительского документа. Допустимые значения:
- none: Игнорирует оценки релевантности дочерних документов и присваивает родительскому документу оценку 0.
- avg: Использует среднюю оценку релевантности всех соответствующих дочерних документов.
- max: Присваивает наивысшую оценку релевантности из соответствующих дочерних документов родителю.
- min: Присваивает наименьшую оценку релевантности из соответствующих дочерних документов родителю.
- sum: Суммирует оценки релевантности всех соответствующих дочерних документов.
По умолчанию none.
inner_hits Необязательный Если указан, возвращает внутренние результаты (дочерние документы), которые соответствуют запросу.

Ограничения сортировки

Запрос 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"
        }
      }
    ]
  }
}