module ElasticGraph::GraphQL::Aggregation::TermGrouping

def composite_clause(grouping_options: {})

def composite_clause(grouping_options: {})
  {"terms" => terms_subclause.merge(grouping_options)}
end

def encoded_index_field_path

def encoded_index_field_path
  @encoded_index_field_path ||= FieldPathEncoder.join(field_path.filter_map(&:name_in_index))
end

def inner_meta

def inner_meta
  INNER_META
end

def key

def key
  @key ||= FieldPathEncoder.encode(field_path.map(&:name_in_graphql_query))
end

def non_composite_clause_for(query)

def non_composite_clause_for(query)
  clause_value = work_around_elasticsearch_bug(terms_subclause)
  {
    "terms" => clause_value.merge({
      "size" => query.paginator.desired_page_size,
      "show_term_doc_count_error" => query.needs_doc_count_error
    })
  }
end

def work_around_elasticsearch_bug(terms_clause)

[^2]: https://www.elastic.co/guide/en/elasticsearch/guide/current/_preventing_combinatorial_explosions.html#_depth_first_versus_breadth_first
[^1]: https://www.elastic.co/guide/en/elasticsearch/reference/8.11/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-collect

Long term, we're hoping to switch sub-aggregations to use a `composite` aggregation instead of `terms`, rendering this moot.

sane default. It may fall over in the case breadth-first is intended for, but we can cross that bridge when it comes.
So, for now we are forcing the collect mode to `depth_first`, as it avoids an issue with Elasticsearch and is a generally

> Breadth-first should be used only when you expect more buckets to be generated than documents landing in the buckets.
>
> ...
>
> Depth-first works well for the majority of aggregations, but can fall apart in situations like our actors and costars example.
> The strategy we outlined previously—building the tree fully and then pruning—is called depth-first and it is the default.

In addition, the docs[^2] make it clear that `depth_first` is usually what you want:

> (numeric fields or scripts for instance).
> The `breadth_first` is the default mode for fields with a cardinality bigger than the requested size or when the cardinality is unknown

whether `breadth_first` or `depth_first` is used when `collect_mode`is not specified:
locally. The Elasticsearch docs[^1] mention that a heuristic (partially based on if a field's cardinality is known!) is used to pick
able to reproduce the CI failure locally until I forced `"collect_mode" => "breadth_first"`, at which point I did see the same error
a better error than a NullPointerException (which is what used to happen). This error also appears to be non-deterministic; I wasn't
This specific exception message was introduced in https://github.com/elastic/elasticsearch/pull/89993, but that was done to provide

```
}
}
}
"reason": "score for different docid, nesting an aggregation under a children aggregation and terms aggregation with collect mode breadth_first isn't possible"
"type": "runtime_exception",
"caused_by": {
"reason": "score for different docid, nesting an aggregation under a children aggregation and terms aggregation with collect mode breadth_first isn't possible",
"type": "runtime_exception",
"caused_by": {
],
}
}
"reason": "score for different docid, nesting an aggregation under a children aggregation and terms aggregation with collect mode breadth_first isn't possible"
"type": "runtime_exception",
"reason": {
"node": "pDXJzLTsRJCRjKe83DqipA",
"index": "teams_camel",
"shard": 0,
{
"failed_shards": [
"grouped": true,
"phase": "query",
"reason": "all shards failed",
"type": "search_phase_execution_exception",
],
}
"reason": "score for different docid, nesting an aggregation under a children aggregation and terms aggregation with collect mode breadth_first isn't possible"
"type": "runtime_exception",
{
"root_cause": [
{
```

specs fail on CI when running against Elasticsearch 8.11 with an error like:
Here we force the `collect_mode` to `depth_first`. Without doing that, we've observed that some of our acceptance
def work_around_elasticsearch_bug(terms_clause)
  terms_clause.merge({"collect_mode" => "depth_first"})
end