lib/dynamoid/criteria/chain.rb in dynamoid-0.2.0 vs lib/dynamoid/criteria/chain.rb in dynamoid-0.3.0

- old
+ new

@@ -1,44 +1,78 @@ # encoding: utf-8 module Dynamoid #:nodoc: module Criteria - # The class object that gets passed around indicating state of a building query. - # Also provides query execution. + # The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from + # chain to relation). It is a chainable object that builds up a query and eventually executes it either on an index + # or by a full table scan. class Chain attr_accessor :query, :source, :index, :values include Enumerable + # Create a new criteria chain. + # + # @param [Class] source the class upon which the ultimate query will be performed. def initialize(source) @query = {} @source = source end + # The workhorse method of the criteria chain. Each key in the passed in hash will become another criteria that the + # ultimate query must match. A key can either be a symbol or a string, and should be an attribute name or + # an attribute name with a range operator. + # + # @example A simple criteria + # where(:name => 'Josh') + # + # @example A more complicated criteria + # where(:name => 'Josh', 'created_at.gt' => DateTime.now - 1.day) + # + # @since 0.2.0 def where(args) args.each {|k, v| query[k] = v} self end + # Returns all the records matching the criteria. + # + # @since 0.2.0 def all records end - + + # Returns the first record matching the criteria. + # + # @since 0.2.0 def first records.first end - + + # Allows you to use the results of a search as an enumerable over the results found. + # + # @since 0.2.0 def each(&block) records.each(&block) end private + # The actual records referenced by the association. + # + # @return [Array] an array of the found records. + # + # @since 0.2.0 def records return records_with_index if index records_without_index end - + + # If the query matches an index on the associated class, then this method will retrieve results from the index table. + # + # @return [Array] an array of the found records. + # + # @since 0.2.0 def records_with_index ids = if index.range_key? Dynamoid::Adapter.query(index.table_name, index_query).collect{|r| r[:ids]}.inject(Set.new) {|set, result| set + result} else results = Dynamoid::Adapter.read(index.table_name, index_query[:hash_value]) @@ -53,18 +87,28 @@ else Array(source.find(ids.to_a)) end end + # If the query does not match an index, we'll manually scan the associated table to manually find results. + # + # @return [Array] an array of the found records. + # + # @since 0.2.0 def records_without_index if Dynamoid::Config.warn_on_scan Dynamoid.logger.warn 'Queries without an index are forced to use scan and are generally much slower than indexed queries!' Dynamoid.logger.warn "You can index this query by adding this to #{source.to_s.downcase}.rb: index [#{source.attributes.sort.collect{|attr| ":#{attr}"}.join(', ')}]" end - Dynamoid::Adapter.scan(source.table_name, query).collect {|hash| source.new(hash)} + Dynamoid::Adapter.scan(source.table_name, query).collect {|hash| source.new(hash).tap { |r| r.new_record = false } } end + # Format the provided query so that it can be used to query results from DynamoDB. + # + # @return [Hash] a hash with keys of :hash_value and :range_value + # + # @since 0.2.0 def index_query values = index.values(query) {}.tap do |hash| hash[:hash_value] = values[:hash_value] if index.range_key? @@ -89,16 +133,19 @@ raise Dynamoid::Errors::MissingRangeKey, 'This index requires a range key' end end end end - + + # Return an index that fulfills all the attributes the criteria is querying, or nil if none is found. + # + # @since 0.2.0 def index index = source.find_index(query.keys.collect{|k| k.to_s.split('.').first}) return nil if index.blank? index end end end -end \ No newline at end of file +end