module MongoMapper # = Important Note # This class is private to MongoMapper and should not be considered part of # MongoMapper's public API. # class FinderOptions OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order] def self.normalized_field(field) field.to_s == 'id' ? :_id : field end def self.normalized_order_direction(direction) direction ||= 'ASC' direction.upcase == 'ASC' ? 1 : -1 end def initialize(model, options) raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash) @model = model @options = {} @conditions = {} options.each_pair do |key, value| key = key.respond_to?(:to_sym) ? key.to_sym : key if OptionKeys.include?(key) @options[key] = value elsif key == :conditions @conditions.merge!(value) else @conditions[key] = value end end add_sci_scope end def criteria to_mongo_criteria(@conditions) end def options fields = @options[:fields] || @options[:select] skip = @options[:skip] || @options[:offset] || 0 limit = @options[:limit] || 0 sort = @options[:sort] || convert_order_to_sort(@options[:order]) {:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort} end def to_a [criteria, options] end private def to_mongo_criteria(conditions, parent_key=nil) criteria = {} conditions.each_pair do |field, value| field = self.class.normalized_field(field) if @model.object_id_key?(field) && value.is_a?(String) value = Mongo::ObjectID.from_string(value) end if field.is_a?(SymbolOperator) criteria.update(field.to_mm_criteria(value)) next end case value when Array criteria[field] = operator?(field) ? value : {'$in' => value} when Hash criteria[field] = to_mongo_criteria(value, field) when Time criteria[field] = value.utc else criteria[field] = value end end criteria end def operator?(field) field.to_s =~ /^\$/ end # adds _type single collection inheritance scope for models that need it def add_sci_scope if @model.single_collection_inherited? @conditions[:_type] = @model.to_s end end def to_mongo_fields(fields) return if fields.blank? if fields.respond_to?(:flatten, :compact) fields.flatten.compact else fields.split(',').map { |field| field.strip } end end def convert_order_to_sort(sort) return if sort.blank? if sort.respond_to?(:all?) && sort.all? { |s| s.respond_to?(:to_mm_order) } sort.map { |s| s.to_mm_order } elsif sort.respond_to?(:to_mm_order) [sort.to_mm_order] else pieces = sort.split(',') pieces.map { |s| to_mongo_sort_piece(s) } end end def to_mongo_sort_piece(str) field, direction = str.strip.split(' ') direction = FinderOptions.normalized_order_direction(direction) [field, direction] end end end