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



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/dynamoid/finders.rb', line 259

def method_missing(method, *args)
  if method =~ /find/
    ActiveSupport::Deprecation.warn("[Dynamoid] .#{method} is deprecated! Call .where instead of")

    finder = method.to_s.split('_by_').first
    attributes = method.to_s.split('_by_').last.split('_and_')

    chain = Dynamoid::Criteria::Chain.new(self)
    chain = chain.where({}.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_all(ids, options = {}) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/dynamoid/finders.rb', line 93

def _find_all(ids, options = {})
  if range_key
    ids = ids.map do |pk, sk|
      sk_casted = TypeCasting.cast_field(sk, attributes[range_key])
      sk_dumped = Dumping.dump_field(sk_casted, attributes[range_key])

      [pk, sk_dumped]
    end
  end

  read_options = options.slice(:consistent_read)

  items = if Dynamoid.config.backoff
            items = []
            backoff = nil
            Dynamoid.adapter.read(table_name, ids, read_options) do |hash, has_unprocessed_items|
              items += hash[table_name]

              if has_unprocessed_items
                backoff ||= Dynamoid.config.build_backoff
                backoff.call
              else
                backoff = nil
              end
            end
            items
          else
            items = Dynamoid.adapter.read(table_name, ids, read_options)
            items ? items[table_name] : []
          end

  if items.size == ids.size || !options[:raise_error]
    items ? items.map { |i| from_database(i) } : []
  else
    ids_list = range_key ? ids.map { |pk, sk| "(#{pk},#{sk})" } : ids.map(&:to_s)
    message = "Couldn't find all #{name.pluralize} with primary keys [#{ids_list.join(', ')}] "
    message += "(found #{items.size} results, but was looking for #{ids.size})"
    raise Errors::RecordNotFound, message
  end
end

#_find_by_id(id, options = {}) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/dynamoid/finders.rb', line 134

def _find_by_id(id, options = {})
  if range_key
    key = options[:range_key]
    key_casted = TypeCasting.cast_field(key, attributes[range_key])
    key_dumped = Dumping.dump_field(key_casted, attributes[range_key])

    options[:range_key] = key_dumped
  end

  if item = Dynamoid.adapter.read(table_name, id, options.slice(:range_key, :consistent_read))
    from_database(item)
  elsif options[:raise_error]
    primary_key = range_key ? "(#{id},#{options[:range_key]})" : id
    message = "Couldn't find #{name} with primary key #{primary_key}"
    raise Errors::RecordNotFound, message
  end
end

#find(*ids, **options) ⇒ Dynamoid::Document

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

Examples:

Find by partition key

Document.find(101)

Find by partition key and sort key

Document.find(101, range_key: 'archived')

Find several documents by partition key

Document.find(101, 102, 103)
Document.find([101, 102, 103])

Find several documents by partition key and sort key

Document.find([[101, 'archived'], [102, 'new'], [103, 'deleted']])

Perform strong consistent reads

Document.find(101, consistent_read: true)
Document.find(101, 102, 103, consistent_read: true)
Document.find(101, range_key: 'archived', consistent_read: true)

Parameters:

  • *id (Array/String)

    an array of ids or one single id

  • options (Hash)

Returns:

  • (Dynamoid::Document)

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

Since:

  • 0.2.0



46
47
48
49
50
51
52
# File 'lib/dynamoid/finders.rb', line 46

def find(*ids, **options)
  if ids.size == 1 && !ids[0].is_a?(Array)
    _find_by_id(ids[0], options.merge(raise_error: true))
  else
    _find_all(ids.flatten(1), options.merge(raise_error: true))
  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.



68
69
70
71
72
# File 'lib/dynamoid/finders.rb', line 68

def find_all(ids, options = {})
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_all is deprecated! Call .find instead of')

  _find_all(ids, options)
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



183
184
185
186
187
188
189
# File 'lib/dynamoid/finders.rb', line 183

def find_all_by_composite_key(hash_key, options = {})
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_all_composite_key is deprecated! Call .where instead of')

  Dynamoid.adapter.query(table_name, options.merge(hash_value: hash_key)).flat_map{ |i| i }.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:



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/dynamoid/finders.rb', line 211

def find_all_by_secondary_index(hash, options = {})
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_all_by_secondary_index is deprecated! Call .where instead of')

  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 = find_index(hash_key_field, range_key_field)
  raise Dynamoid::Errors::MissingIndex, "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(table_name, dynamo_options).flat_map{ |i| i }.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



157
158
159
160
161
# File 'lib/dynamoid/finders.rb', line 157

def find_by_composite_key(hash_key, range_key, options = {})
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_by_composite_key is deprecated! Call .find instead of')

  _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.

Examples:

Find by partition key

Document.find_by_id(101)

Find by partition key and sort key

Document.find_by_id(101, range_key: 'archived')

Parameters:

  • id (String)

    the id of the object to find

Returns:

Since:

  • 0.2.0



87
88
89
90
91
# File 'lib/dynamoid/finders.rb', line 87

def find_by_id(id, options = {})
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_by_id is deprecated! Call .find instead of', caller[1..-1])

  _find_by_id(id, options)
end