module DataMapper module AggregateFunctions # 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 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 ])) 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 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 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 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 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 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 # 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 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 # 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 # @params query [Hash] the conditions # # @return [Array,Numeric,DateTime,Date,Time] the results of the # aggregate query # # @api public def aggregate(*args) query = args.last.kind_of?(Hash) ? args.pop : {} query[:fields] ||= [] query[:fields] |= args query[:fields].map! { |f| normalize_field(f) } query[:order] ||= query[:fields].select { |p| p.kind_of?(Property) } raise ArgumentError, 'query[:fields] must not be empty' if query[:fields].empty? 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 private def assert_property_type(name, *types) if name.nil? raise ArgumentError, 'property name must not be nil' end type = property_by_name(name).type unless types.include?(type) raise ArgumentError, "#{name} must be #{types * ' or '}, but was #{type}" end end def normalize_field(field) assert_kind_of 'field', field, Query::Operator, Symbol, Property case field when 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 end end