Module: Dynamoid::Finders::ClassMethods

Defined in:
lib/dynamoid/finders.rb

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Dynamoid::Document/Array

Find using exciting method_missing finders attributes. Uses criteria chains under the hood to accomplish this neatness.

Examples:

find a user by a first name

User.find_by_first_name('Josh')

find all users by first and last name

User.find_all_by_first_name_and_last_name('Josh', 'Symonds')

Returns:

  • (Dynamoid::Document/Array)

    the found object, or an array of found objects if all was somewhere in the method

Since:

  • 0.2.0



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/dynamoid/finders.rb', line 209

def method_missing(method, *args)
  if method =~ /find/
    finder = method.to_s.split('_by_').first
    attributes = method.to_s.split('_by_').last.split('_and_')

    chain = Dynamoid::Criteria::Chain.new(self)
    chain.query = Hash.new.tap {|h| attributes.each_with_index {|attr, index| h[attr.to_sym] = args[index]}}

    if finder =~ /all/
      return chain.all
    else
      return chain.first
    end
  else
    super
  end
end

Instance Method Details

#find(*ids) ⇒ Dynamoid::Document

Find one or many objects, specified by one id or an array of ids.

Parameters:

  • *id (Array/String)

    an array of ids or one single id

Returns:

  • (Dynamoid::Document)

    one object or an array of objects, depending on whether the input was an array or not

Since:

  • 0.2.0



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/dynamoid/finders.rb', line 28

def find(*ids)
  options = if ids.last.is_a? Hash
              ids.slice!(-1)
            else
              {}
            end
  expects_array = ids.first.kind_of?(Array)

  ids = Array(ids.flatten.uniq)
  if ids.count == 1
    result = self.find_by_id(ids.first, options)
    if result.nil?
      message = "Couldn't find #{self.name} with '#{self.hash_key}'=#{ids[0]}"
      raise Errors::RecordNotFound.new(message)
    end
    expects_array ? Array(result) : result
  else
    result = find_all(ids)
    if result.size != ids.size
      message = "Couldn't find all #{self.name.pluralize} with '#{self.hash_key}': (#{ids.join(', ')}) "
      message << "(found #{result.size} results, but was looking for #{ids.size})"
      raise Errors::RecordNotFound.new(message)
    end
    result
  end
end

#find_all(ids, options = {}) ⇒ Object

Return objects found by the given array of ids, either hash keys, or hash/range key combinations using BatchGetItem. Returns empty array if no results found.

Uses backoff specified by ‘Dynamoid::Config.backoff` config option

Examples:

find all the user with hash key
User.find_all(['1', '2', '3'])

find all the tweets using hash key and range key with consistent read
Tweet.find_all([['1', 'red'], ['1', 'green']], :consistent_read => true)

Parameters:

  • ids (Array<ID>)
  • options: (Hash)

    Passed to the underlying query.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/dynamoid/finders.rb', line 69

def find_all(ids, options = {})
  results = unless Dynamoid.config.backoff
    items = Dynamoid.adapter.read(self.table_name, ids, options)
    items ? items[self.table_name] : []
  else
    items = []
    backoff = nil
    Dynamoid.adapter.read(self.table_name, ids, options) do |hash, has_unprocessed_items|
      items += hash[self.table_name]

      if has_unprocessed_items
        backoff ||= Dynamoid.config.build_backoff
        backoff.call
      else
        backoff = nil
      end
    end
    items
  end

  results ? results.map {|i| from_database(i) } : []
end

#find_all_by_composite_key(hash_key, options = {}) ⇒ Array

Find all objects by hash and range keys.

Examples:

find all ChamberTypes whose level is greater than 1

class ChamberType
  include Dynamoid::Document
  field :chamber_type,            :string
  range :level,                   :integer
  table :key => :chamber_type
end
ChamberType.find_all_by_composite_key('DustVault', range_greater_than: 1)

Parameters:

  • hash_key (String)

    of the objects to find

  • options (Hash) (defaults to: {})

    the options for the range key

Options Hash (options):

  • :range_value (Range)

    find the range key within this range

  • :range_greater_than (Number)

    find range keys greater than this

  • :range_less_than (Number)

    find range keys less than this

  • :range_gte (Number)

    find range keys greater than or equal to this

  • :range_lte (Number)

    find range keys less than or equal to this

Returns:

  • (Array)

    an array of all matching items



137
138
139
140
141
# File 'lib/dynamoid/finders.rb', line 137

def find_all_by_composite_key(hash_key, options = {})
  Dynamoid.adapter.query(self.table_name, options.merge(hash_value: hash_key)).collect do |item|
    from_database(item)
  end
end

#find_all_by_secondary_index(hash, options = {}) ⇒ Array

Find all objects by using local secondary or global secondary index

Examples:

class User
  include Dynamoid::Document
  field :email,          :string
  field :age,            :integer
  field :gender,         :string
  field :rank            :number
  table :key => :email
  global_secondary_index :hash_key => :age, :range_key => :rank
end
# NOTE: the first param and the second param are both hashes,
#       so curly braces must be used on first hash param if sending both params
User.find_all_by_secondary_index({:age => 5}, :range => {"rank.lte" => 10})

Parameters:

  • eg: (Hash)

    => 5

  • eg: (Hash)

    => 10

  • options (Hash) (defaults to: {})
    • query filter, projected keys, scan_index_forward etc

Returns:

  • (Array)

    an array of all matching items

Raises:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/dynamoid/finders.rb', line 163

def find_all_by_secondary_index(hash, options = {})
  range = options[:range] || {}
  hash_key_field, hash_key_value = hash.first
  range_key_field, range_key_value = range.first
  range_op_mapped = nil

  if range_key_field
    range_key_field = range_key_field.to_s
    range_key_op = 'eq'
    if range_key_field.include?('.')
      range_key_field, range_key_op = range_key_field.split('.', 2)
    end
    range_op_mapped = RANGE_MAP.fetch(range_key_op)
  end

  # Find the index
  index = self.find_index(hash_key_field, range_key_field)
  raise Dynamoid::Errors::MissingIndex.new("attempted to find #{[hash_key_field, range_key_field]}") if index.nil?

  # query
  opts = {
    hash_key: hash_key_field.to_s,
    hash_value: hash_key_value,
    index_name: index.name,
  }
  if range_key_field
    opts[:range_key] = range_key_field
    opts[range_op_mapped] = range_key_value
  end
  dynamo_options = opts.merge(options.reject {|key, _| key == :range })
  Dynamoid.adapter.query(self.table_name, dynamo_options).map do |item|
    from_database(item)
  end
end

#find_by_composite_key(hash_key, range_key, options = {}) ⇒ Object

Find one object directly by hash and range keys

Parameters:

  • hash_key (String)

    of the object to find

  • range_key (String/Number)

    of the object to find



112
113
114
# File 'lib/dynamoid/finders.rb', line 112

def find_by_composite_key(hash_key, range_key, options = {})
  find_by_id(hash_key, options.merge(range_key: range_key))
end

#find_by_id(id, options = {}) ⇒ Dynamoid::Document

Find one object directly by id.

Parameters:

  • id (String)

    the id of the object to find

Returns:

Since:

  • 0.2.0



99
100
101
102
103
104
105
# File 'lib/dynamoid/finders.rb', line 99

def find_by_id(id, options = {})
  if item = Dynamoid.adapter.read(self.table_name, id, options)
    from_database(item)
  else
    nil
  end
end