# encoding: utf-8 module Mongoid #:nodoc: module Contexts #:nodoc: class Enumerable include Paging attr_reader :selector, :options, :documents delegate :first, :last, :to => :execute # Return aggregation counts of the grouped documents. This will count by # the first field provided in the fields array. # # Returns: # # A +Hash+ with field values as keys, count as values def aggregate counts = {} group.each_pair { |key, value| counts[key] = value.size } counts end # Gets the number of documents in the array. Delegates to size. def count @count ||= @documents.size end # Groups the documents by the first field supplied in the field options. # # Returns: # # A +Hash+ with field values as keys, arrays of documents as values. def group field = @options[:fields].first @documents.group_by { |doc| doc.send(field) } end # Enumerable implementation of execute. Returns matching documents for # the selector, and adds options if supplied. # # Returns: # # An +Array+ of documents that matched the selector. def execute(paginating = false) limit(@documents.select { |document| document.matches?(@selector) }) end # Create the new enumerable context. This will need the selector and # options from a +Criteria+ and a documents array that is the underlying # array of embedded documents from a has many association. # # Example: # # Mongoid::Contexts::Enumerable.new(selector, options, docs) def initialize(selector, options, documents) @selector, @options, @documents = selector, options, documents end # Get the largest value for the field in all the documents. # # Returns: # # The numerical largest value. def max(field) determine(field, :>=) end # Get the smallest value for the field in all the documents. # # Returns: # # The numerical smallest value. def min(field) determine(field, :<=) end # Get one document. # # Returns: # # The first document in the +Array+ alias :one :first # Get the sum of the field values for all the documents. # # Returns: # # The numerical sum of all the document field values. def sum(field) sum = @documents.inject(nil) do |memo, doc| value = doc.send(field) memo ? memo += value : value end end protected # If the field exists, perform the comparison and set if true. def determine(field, operator) matching = @documents.inject(nil) do |memo, doc| value = doc.send(field) (memo && memo.send(operator, value)) ? memo : value end end # Limits the result set if skip and limit options. def limit(documents) skip, limit = @options[:skip], @options[:limit] if skip && limit return documents.slice(skip, limit) end documents end end end end