Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

馃殌 [READY FOR REVIEW] MONGOID-5556: #tally should support splatting array results #5541

Closed
wants to merge 13 commits into from
20 changes: 15 additions & 5 deletions lib/mongoid/contextual/memory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,23 @@ def pick(*fields)
# context.tally(:name)
#
# @param [ String | Symbol ] field Field to tally.
# @param [ Boolean ] :splat_arrays Whether to tally array
# member values individually. Default false.
#
# @return [ Hash ] The hash of counts.
def tally(field)
return documents.each_with_object({}) do |d, acc|
v = retrieve_value_at_path(d, field)
acc[v] ||= 0
acc[v] += 1
def tally(field, splat_arrays: false)
johnnyshields marked this conversation as resolved.
Show resolved Hide resolved
documents.each_with_object({}) do |doc, tallies|
key = retrieve_value_at_path(doc, field)

if splat_arrays && value.is_a?(Array)
key.each do |array_value|
tallies[array_value] ||= 0
tallies[array_value] += 1
end
else
tallies[key] ||= 0
tallies[key] += 1
end
end
end

Expand Down
17 changes: 9 additions & 8 deletions lib/mongoid/contextual/mongo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -465,23 +465,25 @@ def take!
# # => { [ 1, 2 ] => 1 }
#
# @param [ String | Symbol ] field The field name.
# @param [ Boolean ] :splat_arrays Whether to tally array
# member values individually. Default false.
#
# @return [ Hash ] The hash of counts.
def tally(field)
def tally(field, splat_arrays: false)
name = klass.cleanse_localized_field_names(field)

fld = klass.traverse_association_tree(name)
pipeline = [ { "$group" => { _id: "$#{name}", counts: { "$sum": 1 } } } ]
pipeline.unshift("$match" => view.filter) unless view.filter.blank?
pipeline = []
pipeline << { "$match" => view.filter } unless view.filter.blank?
pipeline << { "$unwind" => "$#{name}" } if splat_arrays
pipeline << { "$group" => { _id: "$#{name}", counts: { "$sum": 1 } } }

collection.aggregate(pipeline).reduce({}) do |tallies, doc|
collection.aggregate(pipeline).each_with_object({}) do |doc, tallies|
is_translation = "#{name}_translations" == field.to_s
val = doc["_id"]

key = if val.is_a?(Array)
val.map do |v|
demongoize_with_field(fld, v, is_translation)
end
val.map { |v| demongoize_with_field(fld, v, is_translation) }
else
demongoize_with_field(fld, val, is_translation)
end
Expand All @@ -495,7 +497,6 @@ def tally(field)
# which can be the same across multiple of those unequal hashes.
tallies[key] ||= 0
tallies[key] += doc["counts"]
tallies
end
end

Expand Down
5 changes: 4 additions & 1 deletion lib/mongoid/contextual/none.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,12 @@ def pick(*_fields)
# context.tally(:name)
#
# @param [ String | Symbol ] _field Field to tally.
# @param [ Boolean ] :splat_arrays Whether to tally array
# member values individually. Default false.
# @param [ String | Symbol ] _field Field to tally.
#
# @return [ Hash ] An empty Hash.
def tally(_field)
def tally(_field, splat_arrays: false)
{}
end

Expand Down
14 changes: 14 additions & 0 deletions spec/mongoid/contextual/mongo_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,20 @@
end
end

context "when tallying a field of type array with splat_arrays true" do
let(:criteria) { Band.where(origin: "tally2") }

let(:tally) do
criteria.tally("genres", splat_arrays: true)
end

it "returns the correct hash" do
expect(tally).to eq(1 => 3,
2 => 2,
3 => 1)
end
end

context "when tallying an element from an array of hashes" do
let(:criteria) { Band.where(origin: "tally") }

Expand Down