lib/elastic_graph/graphql/aggregation/composite_grouping_adapter.rb
# Copyright 2024 Block, Inc. # # Use of this source code is governed by an MIT-style # license that can be found in the LICENSE file or at # https://opensource.org/licenses/MIT. # # frozen_string_literal: true module ElasticGraph class GraphQL module Aggregation # Grouping adapter that uses a `composite` aggregation. # # For now, only used for the outermost "root" aggregations but may be used for sub-aggregations in the future. module CompositeGroupingAdapter class << self def meta_name "comp" end def grouping_detail_for(query) sources = build_sources(query) inner_clauses = yield inner_clauses = nil if inner_clauses.empty? return AggregationDetail.new(inner_clauses, {}) if sources.empty? clauses = { query.name => { "composite" => { "size" => query.paginator.requested_page_size, "sources" => sources, "after" => composite_after(query) }.compact, "aggs" => inner_clauses }.compact } AggregationDetail.new(clauses, {"buckets_path" => [query.name]}) end def prepare_response_buckets(sub_agg, buckets_path, meta) sub_agg.dig(*buckets_path).fetch("buckets").map do |bucket| bucket.merge({"doc_count_error_upper_bound" => 0}) end end private def composite_after(query) return unless (cursor = query.paginator.search_after) expected_keys = query.groupings.map(&:key) if cursor.sort_values.keys.sort == expected_keys.sort cursor.sort_values else raise ::GraphQL::ExecutionError, "`#{cursor.encode}` is not a valid cursor for the current groupings." end end def build_sources(query) # We don't want documents that have no value for a grouping field to be omitted, so we set `missing_bucket: true`. # https://www.elastic.co/guide/en/elasticsearch/reference/8.11/search-aggregations-bucket-composite-aggregation.html#_missing_bucket grouping_options = if query.paginator.search_in_reverse? {"order" => "desc", "missing_bucket" => true} else {"missing_bucket" => true} end query.groupings.map do |grouping| {grouping.key => grouping.composite_clause(grouping_options: grouping_options)} end end end end end end end