class Dynamoid::Indexes::Index

The class contains all the information an index contains, including its keys and which attributes it covers.

Attributes

hash_keys[RW]
name[RW]
range_key?[RW]
range_keys[RW]
source[RW]

Public Class Methods

new(source, name, options = {}) click to toggle source

Create a new index. Pass either :range => true or :range => :column_name to create a ranged index on that column.

@param [Class] source the source class for the index @param [Symbol] name the name of the index

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 16
def initialize(source, name, options = {})
  @source = source
  
  if options.delete(:range)
    @range_keys = sort(name)
  elsif options[:range_key]
    @range_keys = sort(options[:range_key])
  end
  @hash_keys = sort(name)
  @name = sort([hash_keys, range_keys])
  
  raise Dynamoid::Errors::InvalidField, 'A key specified for an index is not a field' unless keys.all?{|n| source.attributes.include?(n)}
end

Public Instance Methods

delete(obj, changed_attributes = false) click to toggle source

Delete an object from this index, preserving existing ids if there are any, and failing gracefully if for some reason the index doesn't already have this object in it.

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 93
def delete(obj, changed_attributes = false)
  values = values(obj, changed_attributes)
  return true if values[:hash_value].blank? || (!values[:range_value].nil? && values[:range_value].blank?)
  existing = Dynamoid::Adapter.read(self.table_name, values[:hash_value], { :range_key => values[:range_value]})
  return true unless existing && existing[:ids] && existing[:ids].include?(obj.id)
  Dynamoid::Adapter.write(self.table_name, {:id => values[:hash_value], :ids => (existing[:ids] - Set[obj.id]), :range => values[:range_value]})
end
keys() click to toggle source

Return the array of keys this index uses for its table.

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 43
def keys
  [Array(hash_keys) + Array(range_keys)].flatten.uniq
end
save(obj) click to toggle source

Save an object to this index, merging it with existing ids if there's already something present at this index location. First, though, delete this object from its old indexes (so the object isn't listed in an erroneous index).

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 80
def save(obj)
  self.delete(obj, true)
  values = values(obj)
  return true if values[:hash_value].blank? || (!values[:range_value].nil? && values[:range_value].blank?)
  existing = Dynamoid::Adapter.read(self.table_name, values[:hash_value], { :range_key => values[:range_value] })
  ids = ((existing and existing[:ids]) or Set.new)
  Dynamoid::Adapter.write(self.table_name, {:id => values[:hash_value], :ids => ids.merge([obj.id]), :range => values[:range_value]})
end
sort(objs) click to toggle source

Sort objects into alphabetical strings, used for composing index names correctly (since we always assume they're alphabetical).

@example find all users by first and last name

sort([:gamma, :alpha, :beta, :omega]) # => [:alpha, :beta, :gamma, :omega]

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 36
def sort(objs)
  Array(objs).flatten.compact.uniq.collect(&:to_s).sort.collect(&:to_sym)
end
table_name() click to toggle source

Return the table name for this index.

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 50
def table_name
  "#{Dynamoid::Config.namespace}_index_" + source.table_name.sub("#{Dynamoid::Config.namespace}_", '').singularize + "_#{name.collect(&:to_s).collect(&:pluralize).join('_and_')}"
end
values(attrs, changed_attributes = false) click to toggle source

Given either an object or a list of attributes, generate a hash key and a range key for the index. Optionally pass in true to changed_attributes for a list of all the object's dirty attributes in convenient index form (for deleting stale information from the indexes).

@param [Object] attrs either an object that responds to :attributes, or a hash of attributes

@return [Hash] a hash with the keys :hash_value and :range_value

@since 0.2.0

# File lib/dynamoid/indexes/index.rb, line 63
def values(attrs, changed_attributes = false)
  if changed_attributes
    hash = {}
    attrs.changes.each {|k, v| hash[k.to_sym] = (v.first || v.last)}
    attrs = hash
  end
  attrs = attrs.send(:attributes) if attrs.respond_to?(:attributes)
  {}.tap do |hash|
    hash[:hash_value] = hash_keys.collect{|key| attrs[key]}.join('.')
    hash[:range_value] = range_keys.inject(0.0) {|sum, key| sum + attrs[key].to_f} if self.range_key?
  end
end