dynamoid 3.3.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +104 -1
- data/README.md +146 -52
- data/lib/dynamoid.rb +1 -0
- data/lib/dynamoid/adapter.rb +20 -7
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +70 -37
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +3 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +20 -12
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +5 -4
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +2 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +2 -3
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +2 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +4 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +4 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +1 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +2 -1
- data/lib/dynamoid/application_time_zone.rb +1 -0
- data/lib/dynamoid/associations.rb +182 -19
- data/lib/dynamoid/associations/association.rb +10 -2
- data/lib/dynamoid/associations/belongs_to.rb +2 -1
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +2 -1
- data/lib/dynamoid/associations/has_many.rb +2 -1
- data/lib/dynamoid/associations/has_one.rb +2 -1
- data/lib/dynamoid/associations/many_association.rb +68 -23
- data/lib/dynamoid/associations/single_association.rb +31 -4
- data/lib/dynamoid/components.rb +2 -0
- data/lib/dynamoid/config.rb +15 -3
- data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +1 -0
- data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +1 -0
- data/lib/dynamoid/config/options.rb +1 -0
- data/lib/dynamoid/criteria.rb +9 -1
- data/lib/dynamoid/criteria/chain.rb +421 -46
- data/lib/dynamoid/criteria/ignored_conditions_detector.rb +3 -3
- data/lib/dynamoid/criteria/key_fields_detector.rb +31 -10
- data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +3 -2
- data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +1 -1
- data/lib/dynamoid/dirty.rb +119 -64
- data/lib/dynamoid/document.rb +133 -46
- data/lib/dynamoid/dumping.rb +9 -0
- data/lib/dynamoid/dynamodb_time_zone.rb +1 -0
- data/lib/dynamoid/errors.rb +2 -0
- data/lib/dynamoid/fields.rb +251 -39
- data/lib/dynamoid/fields/declare.rb +86 -0
- data/lib/dynamoid/finders.rb +69 -32
- data/lib/dynamoid/identity_map.rb +6 -0
- data/lib/dynamoid/indexes.rb +86 -17
- data/lib/dynamoid/loadable.rb +2 -2
- data/lib/dynamoid/log/formatter.rb +26 -0
- data/lib/dynamoid/middleware/identity_map.rb +1 -0
- data/lib/dynamoid/persistence.rb +502 -104
- data/lib/dynamoid/persistence/import.rb +2 -1
- data/lib/dynamoid/persistence/save.rb +1 -0
- data/lib/dynamoid/persistence/update_fields.rb +5 -2
- data/lib/dynamoid/persistence/update_validations.rb +18 -0
- data/lib/dynamoid/persistence/upsert.rb +5 -3
- data/lib/dynamoid/primary_key_type_mapping.rb +1 -0
- data/lib/dynamoid/railtie.rb +1 -0
- data/lib/dynamoid/tasks.rb +3 -1
- data/lib/dynamoid/tasks/database.rb +1 -0
- data/lib/dynamoid/type_casting.rb +12 -2
- data/lib/dynamoid/undumping.rb +8 -0
- data/lib/dynamoid/validations.rb +6 -1
- data/lib/dynamoid/version.rb +1 -1
- metadata +48 -75
- data/.coveralls.yml +0 -1
- data/.document +0 -5
- data/.gitignore +0 -74
- data/.rspec +0 -2
- data/.rubocop.yml +0 -71
- data/.rubocop_todo.yml +0 -55
- data/.travis.yml +0 -44
- data/Appraisals +0 -22
- data/Gemfile +0 -8
- data/Rakefile +0 -46
- data/Vagrantfile +0 -29
- data/docker-compose.yml +0 -7
- data/dynamoid.gemspec +0 -57
- data/gemfiles/rails_4_2.gemfile +0 -9
- data/gemfiles/rails_5_0.gemfile +0 -8
- data/gemfiles/rails_5_1.gemfile +0 -8
- data/gemfiles/rails_5_2.gemfile +0 -8
- data/gemfiles/rails_6_0.gemfile +0 -8
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module Fields
|
5
|
+
# @private
|
6
|
+
class Declare
|
7
|
+
def initialize(source, name, type, options)
|
8
|
+
@source = source
|
9
|
+
@name = name.to_sym
|
10
|
+
@type = type
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
# Register new field metadata
|
16
|
+
@source.attributes = @source.attributes.merge(
|
17
|
+
@name => { type: @type }.merge(@options)
|
18
|
+
)
|
19
|
+
|
20
|
+
# Should be called before `define_attribute_methods` method because it
|
21
|
+
# defines an attribute getter itself
|
22
|
+
warn_about_method_overriding
|
23
|
+
|
24
|
+
# Dirty API
|
25
|
+
@source.define_attribute_method(@name)
|
26
|
+
|
27
|
+
# Generate getters and setters as well as other helper methods
|
28
|
+
generate_instance_methods
|
29
|
+
|
30
|
+
# If alias name specified - generate the same instance methods
|
31
|
+
if @options[:alias]
|
32
|
+
generate_instance_methods_for_alias
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def warn_about_method_overriding
|
39
|
+
warn_if_method_exists(@name)
|
40
|
+
warn_if_method_exists("#{@name}=")
|
41
|
+
warn_if_method_exists("#{@name}?")
|
42
|
+
warn_if_method_exists("#{@name}_before_type_cast?")
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_instance_methods
|
46
|
+
# only local variable is visible in `module_eval` block
|
47
|
+
name = @name
|
48
|
+
|
49
|
+
@source.generated_methods.module_eval do
|
50
|
+
define_method(name) { read_attribute(name) }
|
51
|
+
define_method("#{name}?") do
|
52
|
+
value = read_attribute(name)
|
53
|
+
case value
|
54
|
+
when true then true
|
55
|
+
when false, nil then false
|
56
|
+
else
|
57
|
+
!value.nil?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
define_method("#{name}=") { |value| write_attribute(name, value) }
|
61
|
+
define_method("#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def generate_instance_methods_for_alias
|
66
|
+
# only local variable is visible in `module_eval` block
|
67
|
+
name = @name
|
68
|
+
|
69
|
+
alias_name = @options[:alias].to_sym
|
70
|
+
|
71
|
+
@source.generated_methods.module_eval do
|
72
|
+
alias_method alias_name, name
|
73
|
+
alias_method "#{alias_name}=", "#{name}="
|
74
|
+
alias_method "#{alias_name}?", "#{name}?"
|
75
|
+
alias_method "#{alias_name}_before_type_cast", "#{name}_before_type_cast"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def warn_if_method_exists(method)
|
80
|
+
if @source.instance_methods.include?(method.to_sym)
|
81
|
+
Dynamoid.logger.warn("Method #{method} generated for the field #{@name} overrides already existing method")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/dynamoid/finders.rb
CHANGED
@@ -6,6 +6,7 @@ module Dynamoid
|
|
6
6
|
module Finders
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
# @private
|
9
10
|
RANGE_MAP = {
|
10
11
|
'gt' => :range_greater_than,
|
11
12
|
'lt' => :range_less_than,
|
@@ -19,9 +20,25 @@ module Dynamoid
|
|
19
20
|
module ClassMethods
|
20
21
|
# Find one or many objects, specified by one id or an array of ids.
|
21
22
|
#
|
22
|
-
#
|
23
|
-
#
|
23
|
+
# By default it raises +RecordNotFound+ exception if at least one model
|
24
|
+
# isn't found. This behavior can be changed with +raise_error+ option. If
|
25
|
+
# specified +raise_error: false+ option then +find+ will not raise the
|
26
|
+
# exception.
|
24
27
|
#
|
28
|
+
# When a document schema includes range key it always should be specified
|
29
|
+
# in +find+ method call. In case it's missing +MissingRangeKey+ exception
|
30
|
+
# will be raised.
|
31
|
+
#
|
32
|
+
# Please note that +find+ doesn't preserve order of models in result when
|
33
|
+
# passes multiple ids.
|
34
|
+
#
|
35
|
+
# Supported following options:
|
36
|
+
# * +consistent_read+
|
37
|
+
# * +range_key+
|
38
|
+
# * +raise_error+
|
39
|
+
#
|
40
|
+
# @param ids [String|Array] hash key or an array of hash keys
|
41
|
+
# @param options [Hash]
|
25
42
|
# @return [Dynamoid::Document] one object or an array of objects, depending on whether the input was an array or not
|
26
43
|
#
|
27
44
|
# @example Find by partition key
|
@@ -45,36 +62,45 @@ module Dynamoid
|
|
45
62
|
# @since 0.2.0
|
46
63
|
def find(*ids, **options)
|
47
64
|
if ids.size == 1 && !ids[0].is_a?(Array)
|
48
|
-
_find_by_id(ids[0], options.
|
65
|
+
_find_by_id(ids[0], options.reverse_merge(raise_error: true))
|
49
66
|
else
|
50
|
-
_find_all(ids.flatten(1), options.
|
67
|
+
_find_all(ids.flatten(1), options.reverse_merge(raise_error: true))
|
51
68
|
end
|
52
69
|
end
|
53
70
|
|
54
|
-
#
|
71
|
+
# Find several models at once.
|
72
|
+
#
|
73
|
+
# Returns objects found by the given array of ids, either hash keys, or
|
74
|
+
# hash/range key combinations using +BatchGetItem+.
|
75
|
+
#
|
55
76
|
# Returns empty array if no results found.
|
56
77
|
#
|
57
|
-
# Uses backoff specified by
|
78
|
+
# Uses backoff specified by +Dynamoid::Config.backoff+ config option.
|
58
79
|
#
|
59
|
-
# @param [Array
|
60
|
-
# @param [Hash]
|
80
|
+
# @param ids [Array] array of primary keys
|
81
|
+
# @param options [Hash]
|
82
|
+
# @option options [true|false] :consistent_read
|
83
|
+
# @option options [true|false] :raise_error
|
61
84
|
#
|
62
85
|
# @example
|
63
|
-
#
|
86
|
+
# # Find all the user with hash key
|
64
87
|
# User.find_all(['1', '2', '3'])
|
65
88
|
#
|
66
|
-
#
|
67
|
-
# Tweet.find_all([['1', 'red'], ['1', 'green']], :
|
89
|
+
# # Find all the tweets using hash key and range key with consistent read
|
90
|
+
# Tweet.find_all([['1', 'red'], ['1', 'green']], consistent_read: true)
|
68
91
|
def find_all(ids, options = {})
|
69
92
|
ActiveSupport::Deprecation.warn('[Dynamoid] .find_all is deprecated! Call .find instead of')
|
70
93
|
|
71
94
|
_find_all(ids, options)
|
72
95
|
end
|
73
96
|
|
74
|
-
# Find one object directly by
|
75
|
-
#
|
76
|
-
# @param [String] id the id of the object to find
|
97
|
+
# Find one object directly by primary key.
|
77
98
|
#
|
99
|
+
# @param id [String] the id of the object to find
|
100
|
+
# @param options [Hash]
|
101
|
+
# @option options [true|false] :consistent_read
|
102
|
+
# @option options [true|false] :raise_error
|
103
|
+
# @option options [Scalar value] :range_key
|
78
104
|
# @return [Dynamoid::Document] the found object, or nil if nothing was found
|
79
105
|
#
|
80
106
|
# @example Find by partition key
|
@@ -90,7 +116,10 @@ module Dynamoid
|
|
90
116
|
_find_by_id(id, options)
|
91
117
|
end
|
92
118
|
|
119
|
+
# @private
|
93
120
|
def _find_all(ids, options = {})
|
121
|
+
raise Errors::MissingRangeKey if range_key && ids.any? { |pk, sk| sk.nil? }
|
122
|
+
|
94
123
|
if range_key
|
95
124
|
ids = ids.map do |pk, sk|
|
96
125
|
sk_casted = TypeCasting.cast_field(sk, attributes[range_key])
|
@@ -131,7 +160,10 @@ module Dynamoid
|
|
131
160
|
end
|
132
161
|
end
|
133
162
|
|
163
|
+
# @private
|
134
164
|
def _find_by_id(id, options = {})
|
165
|
+
raise Errors::MissingRangeKey if range_key && options[:range_key].nil?
|
166
|
+
|
135
167
|
if range_key
|
136
168
|
key = options[:range_key]
|
137
169
|
key_casted = TypeCasting.cast_field(key, attributes[range_key])
|
@@ -149,10 +181,10 @@ module Dynamoid
|
|
149
181
|
end
|
150
182
|
end
|
151
183
|
|
152
|
-
# Find one object directly by hash and range keys
|
184
|
+
# Find one object directly by hash and range keys.
|
153
185
|
#
|
154
|
-
# @param [
|
155
|
-
# @param [
|
186
|
+
# @param hash_key [Scalar value] hash key of the object to find
|
187
|
+
# @param range_key [Scalar value] range key of the object to find
|
156
188
|
#
|
157
189
|
def find_by_composite_key(hash_key, range_key, options = {})
|
158
190
|
ActiveSupport::Deprecation.warn('[Dynamoid] .find_by_composite_key is deprecated! Call .find instead of')
|
@@ -169,6 +201,7 @@ module Dynamoid
|
|
169
201
|
# range :level, :integer
|
170
202
|
# table :key => :chamber_type
|
171
203
|
# end
|
204
|
+
#
|
172
205
|
# ChamberType.find_all_by_composite_key('DustVault', range_greater_than: 1)
|
173
206
|
#
|
174
207
|
# @param [String] hash_key of the objects to find
|
@@ -183,7 +216,7 @@ module Dynamoid
|
|
183
216
|
def find_all_by_composite_key(hash_key, options = {})
|
184
217
|
ActiveSupport::Deprecation.warn('[Dynamoid] .find_all_composite_key is deprecated! Call .where instead of')
|
185
218
|
|
186
|
-
Dynamoid.adapter.query(table_name, options.merge(hash_value: hash_key)).flat_map{ |i| i }.collect do |item|
|
219
|
+
Dynamoid.adapter.query(table_name, options.merge(hash_value: hash_key)).flat_map { |i| i }.collect do |item|
|
187
220
|
from_database(item)
|
188
221
|
end
|
189
222
|
end
|
@@ -193,20 +226,22 @@ module Dynamoid
|
|
193
226
|
# @example
|
194
227
|
# class User
|
195
228
|
# include Dynamoid::Document
|
196
|
-
#
|
197
|
-
# field :age, :integer
|
198
|
-
# field :gender, :string
|
199
|
-
# field :rank :number
|
229
|
+
#
|
200
230
|
# table :key => :email
|
201
|
-
# global_secondary_index :
|
231
|
+
# global_secondary_index hash_key: :age, range_key: :rank
|
232
|
+
#
|
233
|
+
# field :email, :string
|
234
|
+
# field :age, :integer
|
235
|
+
# field :gender, :string
|
236
|
+
# field :rank :number
|
202
237
|
# end
|
238
|
+
#
|
203
239
|
# # NOTE: the first param and the second param are both hashes,
|
204
240
|
# # so curly braces must be used on first hash param if sending both params
|
205
|
-
# User.find_all_by_secondary_index({:
|
241
|
+
# User.find_all_by_secondary_index({ age: 5 }, range: { "rank.lte": 10 })
|
206
242
|
#
|
207
|
-
# @param [Hash]
|
208
|
-
# @param [Hash]
|
209
|
-
# @param [Hash] options - query filter, projected keys, scan_index_forward etc
|
243
|
+
# @param hash [Hash] conditions for the hash key e.g. +{ age: 5 }+
|
244
|
+
# @param options [Hash] conditions on range key e.g. +{ "rank.lte": 10 }, query filter, projected keys, scan_index_forward etc.
|
210
245
|
# @return [Array] an array of all matching items
|
211
246
|
def find_all_by_secondary_index(hash, options = {})
|
212
247
|
ActiveSupport::Deprecation.warn('[Dynamoid] .find_all_by_secondary_index is deprecated! Call .where instead of')
|
@@ -240,12 +275,13 @@ module Dynamoid
|
|
240
275
|
opts[range_op_mapped] = range_key_value
|
241
276
|
end
|
242
277
|
dynamo_options = opts.merge(options.reject { |key, _| key == :range })
|
243
|
-
Dynamoid.adapter.query(table_name, dynamo_options).flat_map{ |i| i }.map do |item|
|
278
|
+
Dynamoid.adapter.query(table_name, dynamo_options).flat_map { |i| i }.map do |item|
|
244
279
|
from_database(item)
|
245
280
|
end
|
246
281
|
end
|
247
282
|
|
248
|
-
# Find using exciting method_missing finders attributes. Uses criteria
|
283
|
+
# Find using exciting method_missing finders attributes. Uses criteria
|
284
|
+
# chains under the hood to accomplish this neatness.
|
249
285
|
#
|
250
286
|
# @example find a user by a first name
|
251
287
|
# User.find_by_first_name('Josh')
|
@@ -253,8 +289,9 @@ module Dynamoid
|
|
253
289
|
# @example find all users by first and last name
|
254
290
|
# User.find_all_by_first_name_and_last_name('Josh', 'Symonds')
|
255
291
|
#
|
256
|
-
# @return [Dynamoid::Document
|
292
|
+
# @return [Dynamoid::Document|Array] the found object, or an array of found objects if all was somewhere in the method
|
257
293
|
#
|
294
|
+
# @private
|
258
295
|
# @since 0.2.0
|
259
296
|
def method_missing(method, *args)
|
260
297
|
if method =~ /find/
|
@@ -267,9 +304,9 @@ module Dynamoid
|
|
267
304
|
chain = chain.where({}.tap { |h| attributes.each_with_index { |attr, index| h[attr.to_sym] = args[index] } })
|
268
305
|
|
269
306
|
if finder =~ /all/
|
270
|
-
|
307
|
+
chain.all
|
271
308
|
else
|
272
|
-
|
309
|
+
chain.first
|
273
310
|
end
|
274
311
|
else
|
275
312
|
super
|
@@ -13,6 +13,7 @@ module Dynamoid
|
|
13
13
|
@identity_map ||= {}
|
14
14
|
end
|
15
15
|
|
16
|
+
# @private
|
16
17
|
def from_database(attrs = {})
|
17
18
|
return super if identity_map_off?
|
18
19
|
|
@@ -29,6 +30,7 @@ module Dynamoid
|
|
29
30
|
document
|
30
31
|
end
|
31
32
|
|
33
|
+
# @private
|
32
34
|
def find_by_id(id, options = {})
|
33
35
|
return super if identity_map_off?
|
34
36
|
|
@@ -41,6 +43,7 @@ module Dynamoid
|
|
41
43
|
identity_map[key] || super
|
42
44
|
end
|
43
45
|
|
46
|
+
# @private
|
44
47
|
def identity_map_key(attrs)
|
45
48
|
key = attrs[hash_key].to_s
|
46
49
|
key += "::#{attrs[range_key]}" if range_key
|
@@ -60,6 +63,7 @@ module Dynamoid
|
|
60
63
|
self.class.identity_map
|
61
64
|
end
|
62
65
|
|
66
|
+
# @private
|
63
67
|
def save(*args)
|
64
68
|
return super if self.class.identity_map_off?
|
65
69
|
|
@@ -69,6 +73,7 @@ module Dynamoid
|
|
69
73
|
result
|
70
74
|
end
|
71
75
|
|
76
|
+
# @private
|
72
77
|
def delete
|
73
78
|
return super if self.class.identity_map_off?
|
74
79
|
|
@@ -76,6 +81,7 @@ module Dynamoid
|
|
76
81
|
super
|
77
82
|
end
|
78
83
|
|
84
|
+
# @private
|
79
85
|
def identity_map_key
|
80
86
|
key = hash_key.to_s
|
81
87
|
key += "::#{range_value}" if self.class.range_key
|
data/lib/dynamoid/indexes.rb
CHANGED
@@ -15,19 +15,43 @@ module Dynamoid
|
|
15
15
|
# Defines a Global Secondary index on a table. Keys can be specified as
|
16
16
|
# hash-only, or hash & range.
|
17
17
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
18
|
+
# class Post
|
19
|
+
# include Dynamoid::Document
|
20
|
+
#
|
21
|
+
# field :category
|
22
|
+
#
|
23
|
+
# global_secondary_indexes hash_key: :category
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# The full example with all the options being specified:
|
27
|
+
#
|
28
|
+
# global_secondary_indexes hash_key: :category,
|
29
|
+
# range_key: :created_at,
|
30
|
+
# name: 'posts_category_created_at_index',
|
31
|
+
# projected_attributes: :all,
|
32
|
+
# read_capacity: 100,
|
33
|
+
# write_capacity: 20
|
34
|
+
#
|
35
|
+
# Global secondary index should be declared after fields for mentioned
|
36
|
+
# hash key and optional range key are declared (with method +field+)
|
37
|
+
#
|
38
|
+
# The only mandatory option is +hash_key+. Raises
|
39
|
+
# +Dynamoid::Errors::InvalidIndex+ exception if passed incorrect
|
40
|
+
# options.
|
41
|
+
#
|
42
|
+
# @param [Hash] options the options to pass for this table
|
43
|
+
# @option options [Symbol] name the name for the index; this still gets
|
44
|
+
# namespaced. If not specified, will use a default name.
|
45
|
+
# @option options [Symbol] hash_key the index hash key column.
|
46
|
+
# @option options [Symbol] range_key the index range key column (if
|
23
47
|
# applicable).
|
24
|
-
# @option options [Symbol, Array<Symbol>]
|
25
|
-
# attributes to project for this index. Can be
|
48
|
+
# @option options [Symbol, Array<Symbol>] projected_attributes table
|
49
|
+
# attributes to project for this index. Can be +:keys_only+, +:all+
|
26
50
|
# or an array of included fields. If not specified, defaults to
|
27
|
-
#
|
28
|
-
# @option options [Integer]
|
51
|
+
# +:keys_only+.
|
52
|
+
# @option options [Integer] read_capacity set the read capacity for the
|
29
53
|
# index; does not work on existing indexes.
|
30
|
-
# @option options [Integer]
|
54
|
+
# @option options [Integer] write_capacity set the write capacity for
|
31
55
|
# the index; does not work on existing indexes.
|
32
56
|
def global_secondary_index(options = {})
|
33
57
|
unless options.present?
|
@@ -55,14 +79,38 @@ module Dynamoid
|
|
55
79
|
# Defines a local secondary index on a table. Will use the same primary
|
56
80
|
# hash key as the table.
|
57
81
|
#
|
82
|
+
# class Comment
|
83
|
+
# include Dynamoid::Document
|
84
|
+
#
|
85
|
+
# table hash_key: :post_id
|
86
|
+
# range :created_at, :datetime
|
87
|
+
# field :author_id
|
88
|
+
#
|
89
|
+
# local_secondary_indexes hash_key: :author_id
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# The full example with all the options being specified:
|
93
|
+
#
|
94
|
+
# local_secondary_indexes range_key: :created_at,
|
95
|
+
# name: 'posts_created_at_index',
|
96
|
+
# projected_attributes: :all
|
97
|
+
#
|
98
|
+
# Local secondary index should be declared after fields for mentioned
|
99
|
+
# hash key and optional range key are declared (with method +field+) as
|
100
|
+
# well as after +table+ method call.
|
101
|
+
#
|
102
|
+
# The only mandatory option is +range_key+. Raises
|
103
|
+
# +Dynamoid::Errors::InvalidIndex+ exception if passed incorrect
|
104
|
+
# options.
|
105
|
+
#
|
58
106
|
# @param [Hash] options options to pass for this index.
|
59
|
-
# @option options [Symbol]
|
107
|
+
# @option options [Symbol] name the name for the index; this still gets
|
60
108
|
# namespaced. If not specified, a name is automatically generated.
|
61
|
-
# @option options [Symbol]
|
62
|
-
# @option options [Symbol, Array<Symbol>]
|
63
|
-
# attributes to project for this index. Can be
|
109
|
+
# @option options [Symbol] range_key the range key column for the index.
|
110
|
+
# @option options [Symbol, Array<Symbol>] projected_attributes table
|
111
|
+
# attributes to project for this index. Can be +:keys_only+, +:all+
|
64
112
|
# or an array of included fields. If not specified, defaults to
|
65
|
-
#
|
113
|
+
# +:keys_only+.
|
66
114
|
def local_secondary_index(options = {})
|
67
115
|
unless options.present?
|
68
116
|
raise Dynamoid::Errors::InvalidIndex, 'empty index definition'
|
@@ -94,11 +142,28 @@ module Dynamoid
|
|
94
142
|
self
|
95
143
|
end
|
96
144
|
|
145
|
+
# Returns an index by its hash key and optional range key.
|
146
|
+
#
|
147
|
+
# It works only for indexes without explicit name declared.
|
148
|
+
#
|
149
|
+
# @param hash [scalar] the hash key used to declare an index
|
150
|
+
# @param range [scalar] the range key used to declare an index (optional)
|
151
|
+
# @return [Dynamoid::Indexes::Index, nil] index object or nil if it isn't found
|
97
152
|
def find_index(hash, range = nil)
|
98
153
|
index = indexes[index_key(hash, range)]
|
99
154
|
index
|
100
155
|
end
|
101
156
|
|
157
|
+
# Returns an index by its name
|
158
|
+
#
|
159
|
+
# @param name [string, symbol] the name of the index to lookup
|
160
|
+
# @return [Dynamoid::Indexes::Index, nil] index object or nil if it isn't found
|
161
|
+
def find_index_by_name(name)
|
162
|
+
string_name = name.to_s
|
163
|
+
indexes.each_value.detect{ |i| i.name.to_s == string_name }
|
164
|
+
end
|
165
|
+
|
166
|
+
|
102
167
|
# Returns true iff the provided hash[,range] key combo is a local
|
103
168
|
# secondary index.
|
104
169
|
#
|
@@ -150,6 +215,10 @@ module Dynamoid
|
|
150
215
|
local_secondary_indexes.merge(global_secondary_indexes)
|
151
216
|
end
|
152
217
|
|
218
|
+
# Returns an array of hash keys for all the declared Glocal Secondary
|
219
|
+
# Indexes.
|
220
|
+
#
|
221
|
+
# @return [Array[String]] array of hash keys
|
153
222
|
def indexed_hash_keys
|
154
223
|
global_secondary_indexes.map do |_name, index|
|
155
224
|
index.hash_key.to_s
|
@@ -165,8 +234,8 @@ module Dynamoid
|
|
165
234
|
DEFAULT_PROJECTION_TYPE = :keys_only
|
166
235
|
|
167
236
|
attr_accessor :name, :dynamoid_class, :type, :hash_key, :range_key,
|
168
|
-
|
169
|
-
|
237
|
+
:hash_key_schema, :range_key_schema, :projected_attributes,
|
238
|
+
:read_capacity, :write_capacity
|
170
239
|
|
171
240
|
validate do
|
172
241
|
validate_index_type
|