Skip to content

Commit

Permalink
Moving aggregation into Criteria API
Browse files Browse the repository at this point in the history
  • Loading branch information
durran committed Oct 18, 2009
1 parent 3583534 commit 21e1066
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 29 deletions.
27 changes: 22 additions & 5 deletions lib/mongoid/criteria.rb
Expand Up @@ -14,8 +14,23 @@ module Mongoid #:nodoc:
#
# <tt>criteria.execute</tt>
class Criteria
attr_accessor :klass
attr_reader :selector, :options, :type

AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
# Aggregate the criteria. This will take the internally built selector and options
# and pass them on to the Ruby driver's +group()+ method on the collection. The
# collection itself will be retrieved from the class provided, and once the
# query has returned it will provided a grouping of keys with counts.
#
# Example:
#
# <tt>criteria.select(:field1).where(:field1 => "Title").aggregate(Person)</tt>
def aggregate(klass = nil)
@klass = klass if klass
@klass.collection.group(@options[:fields], @selector, { :count => 0 }, AGGREGATE_REDUCE)
end

# Adds a criterion to the +Criteria+ that specifies values that must all
# be matched in order to return results. Similar to an "in" clause but the
# underlying conditional logic is an "AND" and not an "OR". The MongoDB
Expand Down Expand Up @@ -67,9 +82,10 @@ def excludes(exclusions = {})
#
# If this is a +Criteria+ to find multiple results, will return an +Array+ of
# objects of the type of class provided.
def execute(klass)
return klass.new(klass.collection.find_one(@selector, @options)) if type == :first
return klass.collection.find(@selector, @options).collect { |doc| klass.new(doc) }
def execute(klass = nil)
@klass = klass if klass
return @klass.new(klass.collection.find_one(@selector, @options)) if type == :first
return @klass.collection.find(@selector, @options).collect { |doc| klass.new(doc) }
end

# Adds a criterion to the +Criteria+ that specifies additional options
Expand Down Expand Up @@ -129,8 +145,9 @@ def id(object_id)
# Options:
#
# type: One of :all, :first:, or :last
def initialize(type)
@selector, @options, @type = {}, {}, type
# klass: The class to execute on.
def initialize(type, klass = nil)
@selector, @options, @type, @klass = {}, {}, type, klass
end

# Adds a criterion to the +Criteria+ that specifies the maximum number of
Expand Down
10 changes: 1 addition & 9 deletions lib/mongoid/document.rb
Expand Up @@ -4,7 +4,6 @@ class Document
include Commands, Observable, Validatable
extend Associations

AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
GROUP_BY_REDUCE = "function(obj, prev) { prev.group.push(obj); }"

attr_accessor :parent
Expand All @@ -20,13 +19,6 @@ class Document

class << self

# Get an aggregate count for the supplied group of fields and the
# selector that is provided.
def aggregate(fields, params = {})
selector = params[:conditions]
collection.group(fields, selector, { :count => 0 }, AGGREGATE_REDUCE)
end

# Returns the collection associated with this +Document+. If the
# document is embedded, there will be no collection associated
# with it.
Expand Down Expand Up @@ -159,7 +151,7 @@ def paginate(params = {})
#
# Returns: <tt>Criteria</tt>
def select(*args)
Criteria.new(:all).select(*args)
Criteria.new(:all, self).select(*args)
end

end
Expand Down
35 changes: 35 additions & 0 deletions spec/unit/mongoid/criteria_spec.rb
Expand Up @@ -6,6 +6,41 @@
@criteria = Mongoid::Criteria.new(:all)
end

describe "#aggregate" do

context "when klass provided" do

before do
@reduce = "function(obj, prev) { prev.count++; }"
@criteria = Mongoid::Criteria.new(:all, Person)
@collection = mock
Person.expects(:collection).returns(@collection)
end

it "calls group on the collection with the aggregate js" do
@collection.expects(:group).with([:field1], {}, {:count => 0}, @reduce)
@criteria.select(:field1).aggregate
end

end

context "when klass not provided" do

before do
@reduce = "function(obj, prev) { prev.count++; }"
@collection = mock
Person.expects(:collection).returns(@collection)
end

it "calls group on the collection with the aggregate js" do
@collection.expects(:group).with([:field1], {}, {:count => 0}, @reduce)
@criteria.select(:field1).aggregate(Person)
end

end

end

describe "#all" do

it "adds the $all query to the selector" do
Expand Down
15 changes: 0 additions & 15 deletions spec/unit/mongoid/document_spec.rb
Expand Up @@ -14,21 +14,6 @@
@collection = nil
end

describe "#aggregate" do

before do
@reduce = "function(obj, prev) { prev.count++; }"
end

it "returns documents grouped by the supplied fields" do
results = [{ "title" => "Sir", "count" => 30 }]
@collection.expects(:group).with([:title], nil, {:count => 0}, @reduce).returns(results)
grouped = Person.aggregate([:title], {})
grouped.first["count"].should == 30
end

end

describe "#all" do

before do
Expand Down

0 comments on commit 21e1066

Please sign in to comment.