Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Please mark backwards incompatible changes with an exclamation mark at the start

## [Unreleased]

### Added
- The `missing` parameter for Elasticsearch's `Terms` aggregation.

## [29.10.0] - 2026-08-21

### Added
Expand Down
6 changes: 2 additions & 4 deletions lib/jay_api/elasticsearch/query_builder/aggregations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
module Elasticsearch
class QueryBuilder
# The list of aggregations to be included in an Elasticsearch query.
class Aggregations

Check warning on line 24 in lib/jay_api/elasticsearch/query_builder/aggregations.rb

View workflow job for this annotation

GitHub Actions / lint

[rubocop] reported by reviewdog 🐶 Class has too many lines. [106/100] Raw Output: lib/jay_api/elasticsearch/query_builder/aggregations.rb:24:7: C: Metrics/ClassLength: Class has too many lines. [106/100]
extend Forwardable

def_delegators :aggregations, :any?, :none?
Expand All @@ -32,11 +32,9 @@

# Adds a +terms+ type aggregation. For information about the parameters
# @see JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms#initialize
def terms(name, field: nil, script: nil, size: nil, order: nil)
def terms(name, **params)
add(
::JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms.new(
name, field: field, script: script, size: size, order: order
)
::JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms.new(name, **params)
)
end

Expand Down
22 changes: 16 additions & 6 deletions lib/jay_api/elasticsearch/query_builder/aggregations/terms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Aggregations
# Information about this type of aggregation can be found in:
# https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html
class Terms < ::JayAPI::Elasticsearch::QueryBuilder::Aggregations::Aggregation
attr_reader :field, :script, :size, :order
attr_reader :field, :script, :size, :order, :missing

# @param [String] name The name used by Elasticsearch to identify each
# of the aggregations.
Expand All @@ -29,9 +29,15 @@ class Terms < ::JayAPI::Elasticsearch::QueryBuilder::Aggregations::Aggregation
# aggregation. By default, the +terms+ aggregation orders terms by
# descending document +_count+. This can be changed by providing a
# custom +order+ hash.
# @param [String] missing The value to use for documents that are
# missing a value in the given +field+ or for whom the +script+
# returns +null+. When +missing+ is not given, such documents are
# ignored.
# @raise [ArgumentError] If neither a +field+ nor a +script+ are given
# or if both of them are given. Only one should be present.
def initialize(name, field: nil, script: nil, size: nil, order: nil)
# rubocop:disable Metrics/ParameterLists -- Constraint by Elasticsearch's design
# :reek:LongParameterList -- Constraint by Elasticsearch's design
def initialize(name, field: nil, script: nil, size: nil, order: nil, missing: nil)
if (field.present? && script.present?) || (field.blank? && script.blank?)
raise ArgumentError, "Either 'field' or 'script' must be provided"
end
Expand All @@ -42,13 +48,16 @@ def initialize(name, field: nil, script: nil, size: nil, order: nil)
@script = script
@size = size
@order = order
@missing = missing
end

# rubocop:enable Metrics/ParameterLists

# @return [self] A copy of the receiver.
def clone
self.class.new(name, field: field, script: script, size: size, order: order&.deep_dup).tap do |copy|
copy.aggregations = aggregations.clone
end
self.class.new(
name, field: field, script: script, size: size, order: order&.deep_dup, missing: missing
).tap { |copy| copy.aggregations = aggregations.clone }
end

# @return [Hash] The Hash representation of the +Aggregation+.
Expand All @@ -60,7 +69,8 @@ def to_h
field: field,
size: size,
script: script&.to_h,
order: order
order: order,
missing: missing
}.compact
}
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@
end
end

context "when no 'missing' value has been given" do
it "haa the same value for 'missing' fields" do
expect(method_call.missing).to be(terms.missing)
end
end

context "when a 'missing' value has been given" do
let(:constructor_params) { super().merge(missing: 'N/A') }

it "haa the same value for 'missing' fields" do
expect(method_call.missing).to be(terms.missing) & eq('N/A')
end
end

it_behaves_like 'JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms::#clone'
end

Expand Down Expand Up @@ -230,5 +244,21 @@
expect(method_call).to eq(expected_hash)
end
end

context "when a value for 'missing' fields has been given" do
let(:constructor_params) { super().merge(missing: 'N/A') }

let(:expected_hash) do
{
'genres' => {
terms: { field: 'genre', missing: 'N/A' }
}
}
end

it "returns the expected hash (including the given 'missing' value)" do
expect(method_call).to eq(expected_hash)
end
end
end
end
23 changes: 19 additions & 4 deletions spec/jay_api/elasticsearch/query_builder/aggregations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@

it 'creates the Terms instance with the expected parameters' do
expect(JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms)
.to receive(:new).with(name, field: field, script: nil, size: nil, order: nil)
.to receive(:new).with(name, field: field)

method_call
end
Expand All @@ -90,7 +90,7 @@

it 'creates the Terms instance with the expected parameters' do
expect(JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms)
.to receive(:new).with(name, field: field, script: nil, size: 100, order: nil)
.to receive(:new).with(name, field: field, size: 100)

method_call
end
Expand All @@ -105,7 +105,7 @@

it 'creates the Terms instance with the expected parameters' do
expect(JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms)
.to receive(:new).with(name, field: field, script: nil, size: nil, order: { _key: :asc })
.to receive(:new).with(name, field: field, order: { _key: :asc })

method_call
end
Expand All @@ -126,7 +126,22 @@

it 'creates the Terms instance with the expected parameters' do
expect(JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms)
.to receive(:new).with(name, field: field, script: script, size: nil, order: nil)
.to receive(:new).with(name, field: field, script: script)

method_call
end
end

context "when a 'missing' value is provided" do
subject(:method_call) do
aggregations.terms(
name, field: field, missing: 'NULL'
)
end

it 'creates the Terms instance with the expected parameters' do
expect(JayAPI::Elasticsearch::QueryBuilder::Aggregations::Terms)
.to receive(:new).with(name, field: field, missing: 'NULL')

method_call
end
Expand Down
Loading