module DataMapper::Aggregates::Functions
Public Instance Methods
Perform aggregate queries
@example the count of friends
Friend.aggregate(:all.count)
@example the minimum age, the maximum age and the total age of friends
Friend.aggregate(:age.min, :age.max, :age.sum)
@example the average age, grouped by gender
Friend.aggregate(:age.avg, :fields => [ :gender ])
@param aggregates [Symbol, …] operators to aggregate with @param query [Hash] the conditions
@return [Array,Numeric,DateTime,Date,Time] the results of the
aggregate query
@api public
# File lib/dm-aggregates/functions.rb, line 153 def aggregate(*args) query = args.last.kind_of?(Hash) ? args.pop : {} query[:fields] ||= [] query[:fields] |= args query[:fields].map! { |f| normalize_field(f) } raise ArgumentError, 'query[:fields] must not be empty' if query[:fields].empty? unless query.key?(:order) # the current collection/model is already sorted by attributes # and since we are projecting away some of the attributes, # and then performing aggregate functions on the remainder, # we need to honor the existing order, as if it were already # materialized, and we are looping over the rows in order. directions = direction_map query[:order] = [] # use the current query order for each property if available query[:fields].each do |property| next unless property.kind_of?(Property) query[:order] << directions.fetch(property, property) end end query = scoped_query(query) if query.fields.any? { |p| p.kind_of?(Property) } query.repository.aggregate(query.update(:unique => true)) else query.repository.aggregate(query).first # only return one row end end
Get the average value of a property
@example the average age of all friends
Friend.avg(:age)
@example the average age of all female friends
Friend.avg(:age, :conditions => [ 'gender = ?', 'female' ])
@param property [Symbol] the property you wish to get the average value of @param opts [Hash, Symbol] the conditions
@return [Integer] return the average value of a property given the conditions
@api public
# File lib/dm-aggregates/functions.rb, line 103 def avg(*args) query = args.last.kind_of?(Hash) ? args.pop : {} property_name = args.first assert_property_type property_name, ::Integer, ::Float, ::BigDecimal aggregate(query.merge(:fields => [ property_name.avg ])) end
Count results (given the conditions)
@example the count of all friends
Friend.count
@example the count of all friends older then 18
Friend.count(:age.gt => 18)
@example the count of all your female friends
Friend.count(:conditions => [ 'gender = ?', 'female' ])
@example the count of all friends with an address (NULL values are not included)
Friend.count(:address)
@example the count of all friends with an address that are older then 18
Friend.count(:address, :age.gt => 18)
@example the count of all your female friends with an address
Friend.count(:address, :conditions => [ 'gender = ?', 'female' ])
@param property [Symbol] of the property you with to count (optional) @param opts [Hash, Symbol] the conditions
@return [Integer] return the count given the conditions
@api public
# File lib/dm-aggregates/functions.rb, line 32 def count(*args) query = args.last.kind_of?(Hash) ? args.pop : {} property_name = args.first if property_name assert_kind_of 'property', property_by_name(property_name), Property end aggregate(query.merge(:fields => [ property_name ? property_name.count : :all.count ])).to_i end
Get the highest value of a property
@example the age of the oldest friend
Friend.max(:age)
@example the age of the oldest female friend
Friend.max(:age, :conditions => [ 'gender = ?', 'female' ])
@param property [Symbol] the property you wish to get the highest value of @param opts [Hash, Symbol] the conditions
@return [Integer] return the highest value of a property given the conditions
@api public
# File lib/dm-aggregates/functions.rb, line 80 def max(*args) query = args.last.kind_of?(Hash) ? args.pop : {} property_name = args.first assert_property_type property_name, ::Integer, ::Float, ::BigDecimal, ::DateTime, ::Date, ::Time aggregate(query.merge(:fields => [ property_name.max ])) end
Get the lowest value of a property
@example the age of the youngest friend
Friend.min(:age)
@example the age of the youngest female friend
Friend.min(:age, :conditions => [ 'gender = ?', 'female' ])
@param property [Symbol] the property you wish to get the lowest value of @param opts [Hash, Symbol] the conditions
@return [Integer] return the lowest value of a property given the conditions
@api public
# File lib/dm-aggregates/functions.rb, line 57 def min(*args) query = args.last.kind_of?(Hash) ? args.pop : {} property_name = args.first assert_property_type property_name, ::Integer, ::Float, ::BigDecimal, ::DateTime, ::Date, ::Time aggregate(query.merge(:fields => [ property_name.min ])) end
Get the total value of a property
@example the total age of all friends
Friend.sum(:age)
@example the total age of all female friends
Friend.max(:age, :conditions => [ 'gender = ?', 'female' ])
@param property [Symbol] the property you wish to get the total value of @param opts [Hash, Symbol] the conditions
@return [Integer] return the total value of a property given the conditions
@api public
# File lib/dm-aggregates/functions.rb, line 126 def sum(*args) query = args.last.kind_of?(::Hash) ? args.pop : {} property_name = args.first assert_property_type property_name, ::Integer, ::Float, ::BigDecimal aggregate(query.merge(:fields => [ property_name.sum ])) end
Private Instance Methods
# File lib/dm-aggregates/functions.rb, line 191 def assert_property_type(name, *types) if name.nil? raise ArgumentError, 'property name must not be nil' end property = property_by_name(name) type = property.primitive unless types.include?(type) raise ArgumentError, "#{name} must be #{types * ' or '}, but was #{type}" end end
# File lib/dm-aggregates/functions.rb, line 223 def direction_map direction_map = {} self.query.order.each do |direction| direction_map[direction.target] = direction end direction_map end
# File lib/dm-aggregates/functions.rb, line 204 def normalize_field(field) assert_kind_of 'field', field, DataMapper::Query::Operator, Symbol, Property case field when DataMapper::Query::Operator if field.target == :all field else field.class.new(property_by_name(field.target), field.operator) end when Symbol property_by_name(field) when Property field end end