dynamoid 3.1.0 → 3.2.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 +5 -5
- data/.rubocop.yml +18 -0
- data/.travis.yml +5 -3
- data/CHANGELOG.md +15 -0
- data/README.md +113 -63
- data/Vagrantfile +2 -2
- data/docker-compose.yml +1 -1
- data/gemfiles/rails_4_2.gemfile +1 -1
- data/gemfiles/rails_5_0.gemfile +1 -1
- data/gemfiles/rails_5_1.gemfile +1 -1
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/lib/dynamoid/adapter.rb +1 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +26 -395
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +234 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +89 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +24 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +57 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +28 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +123 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +85 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +52 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +60 -0
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +1 -0
- data/lib/dynamoid/associations/has_many.rb +1 -0
- data/lib/dynamoid/associations/has_one.rb +1 -0
- data/lib/dynamoid/associations/single_association.rb +1 -0
- data/lib/dynamoid/criteria.rb +4 -4
- data/lib/dynamoid/criteria/chain.rb +86 -79
- data/lib/dynamoid/criteria/ignored_conditions_detector.rb +41 -0
- data/lib/dynamoid/criteria/key_fields_detector.rb +61 -0
- data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +41 -0
- data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +40 -0
- data/lib/dynamoid/document.rb +18 -13
- data/lib/dynamoid/dumping.rb +52 -40
- data/lib/dynamoid/fields.rb +4 -3
- data/lib/dynamoid/finders.rb +3 -3
- data/lib/dynamoid/persistence.rb +5 -6
- data/lib/dynamoid/primary_key_type_mapping.rb +1 -1
- data/lib/dynamoid/tasks.rb +1 -0
- data/lib/dynamoid/tasks/database.rake +2 -2
- data/lib/dynamoid/type_casting.rb +37 -19
- data/lib/dynamoid/undumping.rb +53 -42
- data/lib/dynamoid/validations.rb +2 -0
- data/lib/dynamoid/version.rb +1 -1
- metadata +17 -5
- data/lib/dynamoid/adapter_plugin/query.rb +0 -144
- data/lib/dynamoid/adapter_plugin/scan.rb +0 -107
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'middleware/backoff'
|
4
|
+
require_relative 'middleware/limit'
|
5
|
+
require_relative 'middleware/start_key'
|
6
|
+
|
7
|
+
module Dynamoid
|
8
|
+
module AdapterPlugin
|
9
|
+
class AwsSdkV3
|
10
|
+
class Scan
|
11
|
+
attr_reader :client, :table, :conditions, :options
|
12
|
+
|
13
|
+
def initialize(client, table, conditions = {}, options = {})
|
14
|
+
@client = client
|
15
|
+
@table = table
|
16
|
+
@conditions = conditions
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
request = build_request
|
22
|
+
|
23
|
+
Enumerator.new do |yielder|
|
24
|
+
api_call = -> (request) do
|
25
|
+
client.scan(request).tap do |response|
|
26
|
+
yielder << response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
middlewares = Middleware::Backoff.new(
|
31
|
+
Middleware::StartKey.new(
|
32
|
+
Middleware::Limit.new(api_call, record_limit: record_limit, scan_limit: scan_limit)
|
33
|
+
)
|
34
|
+
)
|
35
|
+
|
36
|
+
catch :stop_pagination do
|
37
|
+
loop do
|
38
|
+
middlewares.call(request)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def build_request
|
47
|
+
request = options.slice(
|
48
|
+
:consistent_read,
|
49
|
+
:exclusive_start_key,
|
50
|
+
:select
|
51
|
+
).compact
|
52
|
+
|
53
|
+
# Deal with various limits and batching
|
54
|
+
batch_size = options[:batch_size]
|
55
|
+
limit = [record_limit, scan_limit, batch_size].compact.min
|
56
|
+
|
57
|
+
request[:limit] = limit if limit
|
58
|
+
request[:table_name] = table.name
|
59
|
+
request[:scan_filter] = scan_filter
|
60
|
+
|
61
|
+
request
|
62
|
+
end
|
63
|
+
|
64
|
+
def record_limit
|
65
|
+
options[:record_limit]
|
66
|
+
end
|
67
|
+
|
68
|
+
def scan_limit
|
69
|
+
options[:scan_limit]
|
70
|
+
end
|
71
|
+
|
72
|
+
def scan_filter
|
73
|
+
conditions.reduce({}) do |result, (attr, cond)|
|
74
|
+
condition = {
|
75
|
+
comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
|
76
|
+
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
|
77
|
+
}
|
78
|
+
result[attr] = condition
|
79
|
+
result
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module AdapterPlugin
|
5
|
+
class AwsSdkV3
|
6
|
+
# Represents a table. Exposes data from the "DescribeTable" API call, and also
|
7
|
+
# provides methods for coercing values to the proper types based on the table's schema data
|
8
|
+
class Table
|
9
|
+
attr_reader :schema
|
10
|
+
|
11
|
+
#
|
12
|
+
# @param [Hash] schema Data returns from a "DescribeTable" call
|
13
|
+
#
|
14
|
+
def initialize(schema)
|
15
|
+
@schema = schema[:table]
|
16
|
+
end
|
17
|
+
|
18
|
+
def range_key
|
19
|
+
@range_key ||= schema[:key_schema].find { |d| d[:key_type] == RANGE_KEY }.try(:attribute_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def range_type
|
23
|
+
range_type ||= schema[:attribute_definitions].find do |d|
|
24
|
+
d[:attribute_name] == range_key
|
25
|
+
end.try(:fetch, :attribute_type, nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
def hash_key
|
29
|
+
@hash_key ||= schema[:key_schema].find { |d| d[:key_type] == HASH_KEY }.try(:attribute_name).to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Returns the API type (e.g. "N", "S") for the given column, if the schema defines it,
|
34
|
+
# nil otherwise
|
35
|
+
#
|
36
|
+
def col_type(col)
|
37
|
+
col = col.to_s
|
38
|
+
col_def = schema[:attribute_definitions].find { |d| d[:attribute_name] == col.to_s }
|
39
|
+
col_def && col_def[:attribute_type]
|
40
|
+
end
|
41
|
+
|
42
|
+
def item_count
|
43
|
+
schema[:item_count]
|
44
|
+
end
|
45
|
+
|
46
|
+
def name
|
47
|
+
schema[:table_name]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module AdapterPlugin
|
5
|
+
class AwsSdkV3
|
6
|
+
class UntilPastTableStatus
|
7
|
+
attr_reader :table_name, :status
|
8
|
+
|
9
|
+
def initialize(table_name, status = :creating)
|
10
|
+
@table_name = table_name
|
11
|
+
@status = status
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
counter = 0
|
16
|
+
resp = nil
|
17
|
+
begin
|
18
|
+
check = { again: true }
|
19
|
+
while check[:again]
|
20
|
+
sleep Dynamoid::Config.sync_retry_wait_seconds
|
21
|
+
resp = client.describe_table(table_name: table_name)
|
22
|
+
check = check_table_status?(counter, resp, status)
|
23
|
+
Dynamoid.logger.info "Checked table status for #{table_name} (check #{check.inspect})"
|
24
|
+
counter += 1
|
25
|
+
end
|
26
|
+
# If you issue a DescribeTable request immediately after a CreateTable
|
27
|
+
# request, DynamoDB might return a ResourceNotFoundException.
|
28
|
+
# This is because DescribeTable uses an eventually consistent query,
|
29
|
+
# and the metadata for your table might not be available at that moment.
|
30
|
+
# Wait for a few seconds, and then try the DescribeTable request again.
|
31
|
+
# See: http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#describe_table-instance_method
|
32
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
|
33
|
+
case status
|
34
|
+
when :creating then
|
35
|
+
if counter >= Dynamoid::Config.sync_retry_max_times
|
36
|
+
Dynamoid.logger.warn "Waiting on table metadata for #{table_name} (check #{counter})"
|
37
|
+
retry # start over at first line of begin, does not reset counter
|
38
|
+
else
|
39
|
+
Dynamoid.logger.error "Exhausted max retries (Dynamoid::Config.sync_retry_max_times) waiting on table metadata for #{table_name} (check #{counter})"
|
40
|
+
raise e
|
41
|
+
end
|
42
|
+
else
|
43
|
+
# When deleting a table, "not found" is the goal.
|
44
|
+
Dynamoid.logger.info "Checked table status for #{table_name}: Not Found (check #{check.inspect})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def check_table_status?(counter, resp, expect_status)
|
52
|
+
status = PARSE_TABLE_STATUS.call(resp)
|
53
|
+
again = counter < Dynamoid::Config.sync_retry_max_times &&
|
54
|
+
status == TABLE_STATUSES[expect_status]
|
55
|
+
{ again: again, status: status, counter: counter }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/dynamoid/criteria.rb
CHANGED
@@ -8,17 +8,17 @@ module Dynamoid
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
-
%i[where all first last each record_limit scan_limit batch start scan_index_forward].each do |meth|
|
11
|
+
%i[where all first last each record_limit scan_limit batch start scan_index_forward find_by_pages].each do |meth|
|
12
12
|
# Return a criteria chain in response to a method that will begin or end a chain. For more information,
|
13
13
|
# see Dynamoid::Criteria::Chain.
|
14
14
|
#
|
15
15
|
# @since 0.2.0
|
16
|
-
define_method(meth) do |*args|
|
16
|
+
define_method(meth) do |*args, &blk|
|
17
17
|
chain = Dynamoid::Criteria::Chain.new(self)
|
18
18
|
if args
|
19
|
-
chain.send(meth, *args)
|
19
|
+
chain.send(meth, *args, &blk)
|
20
20
|
else
|
21
|
-
chain.send(meth)
|
21
|
+
chain.send(meth, &blk)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -1,12 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'key_fields_detector'
|
4
|
+
require_relative 'ignored_conditions_detector'
|
5
|
+
require_relative 'overwritten_conditions_detector'
|
6
|
+
require_relative 'nonexistent_fields_detector'
|
7
|
+
|
8
|
+
module Dynamoid
|
4
9
|
module Criteria
|
5
10
|
# The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
|
6
11
|
# chain to relation). It is a chainable object that builds up a query and eventually executes it by a Query or Scan.
|
7
12
|
class Chain
|
8
|
-
|
9
|
-
|
13
|
+
attr_reader :query, :source, :consistent_read, :key_fields_detector
|
14
|
+
|
10
15
|
include Enumerable
|
11
16
|
# Create a new criteria chain.
|
12
17
|
#
|
@@ -22,6 +27,9 @@ module Dynamoid #:nodoc:
|
|
22
27
|
if @source.attributes.key?(type)
|
23
28
|
@query[:"#{type}.in"] = @source.deep_subclasses.map(&:name) << @source.name
|
24
29
|
end
|
30
|
+
|
31
|
+
# we should re-initialize keys detector every time we change query
|
32
|
+
@key_fields_detector = KeyFieldsDetector.new(@query, @source)
|
25
33
|
end
|
26
34
|
|
27
35
|
# The workhorse method of the criteria chain. Each key in the passed in hash will become another criteria that the
|
@@ -36,7 +44,26 @@ module Dynamoid #:nodoc:
|
|
36
44
|
#
|
37
45
|
# @since 0.2.0
|
38
46
|
def where(args)
|
39
|
-
|
47
|
+
detector = IgnoredConditionsDetector.new(args)
|
48
|
+
if detector.found?
|
49
|
+
Dynamoid.logger.warn(detector.warning_message)
|
50
|
+
end
|
51
|
+
|
52
|
+
detector = OverwrittenConditionsDetector.new(@query, args)
|
53
|
+
if detector.found?
|
54
|
+
Dynamoid.logger.warn(detector.warning_message)
|
55
|
+
end
|
56
|
+
|
57
|
+
detector = NonexistentFieldsDetector.new(args, @source)
|
58
|
+
if detector.found?
|
59
|
+
Dynamoid.logger.warn(detector.warning_message)
|
60
|
+
end
|
61
|
+
|
62
|
+
query.update(args.symbolize_keys)
|
63
|
+
|
64
|
+
# we should re-initialize keys detector every time we change query
|
65
|
+
@key_fields_detector = KeyFieldsDetector.new(@query, @source)
|
66
|
+
|
40
67
|
self
|
41
68
|
end
|
42
69
|
|
@@ -53,7 +80,7 @@ module Dynamoid #:nodoc:
|
|
53
80
|
end
|
54
81
|
|
55
82
|
def count
|
56
|
-
if key_present?
|
83
|
+
if @key_fields_detector.key_present?
|
57
84
|
count_via_query
|
58
85
|
else
|
59
86
|
count_via_scan
|
@@ -74,13 +101,13 @@ module Dynamoid #:nodoc:
|
|
74
101
|
ids = []
|
75
102
|
ranges = []
|
76
103
|
|
77
|
-
if key_present?
|
78
|
-
Dynamoid.adapter.query(source.table_name, range_query).collect do |hash|
|
104
|
+
if @key_fields_detector.key_present?
|
105
|
+
Dynamoid.adapter.query(source.table_name, range_query).flat_map{ |i| i }.collect do |hash|
|
79
106
|
ids << hash[source.hash_key.to_sym]
|
80
107
|
ranges << hash[source.range_key.to_sym] if source.range_key
|
81
108
|
end
|
82
109
|
else
|
83
|
-
Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).collect do |hash|
|
110
|
+
Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).flat_map{ |i| i }.collect do |hash|
|
84
111
|
ids << hash[source.hash_key.to_sym]
|
85
112
|
ranges << hash[source.range_key.to_sym] if source.range_key
|
86
113
|
end
|
@@ -128,6 +155,10 @@ module Dynamoid #:nodoc:
|
|
128
155
|
records.each(&block)
|
129
156
|
end
|
130
157
|
|
158
|
+
def find_by_pages(&block)
|
159
|
+
pages.each(&block)
|
160
|
+
end
|
161
|
+
|
131
162
|
private
|
132
163
|
|
133
164
|
# The actual records referenced by the association.
|
@@ -136,42 +167,57 @@ module Dynamoid #:nodoc:
|
|
136
167
|
#
|
137
168
|
# @since 0.2.0
|
138
169
|
def records
|
139
|
-
|
140
|
-
|
170
|
+
pages.lazy.flat_map { |i| i }
|
171
|
+
end
|
172
|
+
|
173
|
+
# Arrays of records, sized based on the actual pages produced by DynamoDB
|
174
|
+
#
|
175
|
+
# @return [Enumerator] an iterator of the found records.
|
176
|
+
#
|
177
|
+
# @since 3.1.0
|
178
|
+
def pages
|
179
|
+
if @key_fields_detector.key_present?
|
180
|
+
pages_via_query
|
141
181
|
else
|
142
|
-
|
182
|
+
issue_scan_warning if Dynamoid::Config.warn_on_scan && query.present?
|
183
|
+
pages_via_scan
|
143
184
|
end
|
144
185
|
end
|
145
186
|
|
146
|
-
|
187
|
+
# If the query matches an index, we'll query the associated table to find results.
|
188
|
+
#
|
189
|
+
# @return [Enumerator] an iterator of the found pages. An array of records
|
190
|
+
#
|
191
|
+
# @since 3.1.0
|
192
|
+
def pages_via_query
|
147
193
|
Enumerator.new do |yielder|
|
148
|
-
Dynamoid.adapter.query(source.table_name, range_query).each do |
|
149
|
-
yielder.yield source.from_database(hash)
|
194
|
+
Dynamoid.adapter.query(source.table_name, range_query).each do |items, metadata|
|
195
|
+
yielder.yield items.map { |hash| source.from_database(hash) }, metadata.slice(:last_evaluated_key)
|
150
196
|
end
|
151
197
|
end
|
152
198
|
end
|
153
199
|
|
154
200
|
# If the query does not match an index, we'll manually scan the associated table to find results.
|
155
201
|
#
|
156
|
-
# @return [Enumerator] an iterator of the found records
|
202
|
+
# @return [Enumerator] an iterator of the found pages. An array of records
|
157
203
|
#
|
158
|
-
# @since
|
159
|
-
def
|
160
|
-
if Dynamoid::Config.warn_on_scan && query.present?
|
161
|
-
Dynamoid.logger.warn 'Queries without an index are forced to use scan and are generally much slower than indexed queries!'
|
162
|
-
Dynamoid.logger.warn "You can index this query by adding index declaration to #{source.to_s.downcase}.rb:"
|
163
|
-
Dynamoid.logger.warn "* global_secondary_index hash_key: 'some-name', range_key: 'some-another-name'"
|
164
|
-
Dynamoid.logger.warn "* local_secondary_indexe range_key: 'some-name'"
|
165
|
-
Dynamoid.logger.warn "Not indexed attributes: #{query.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
|
166
|
-
end
|
167
|
-
|
204
|
+
# @since 3.1.0
|
205
|
+
def pages_via_scan
|
168
206
|
Enumerator.new do |yielder|
|
169
|
-
Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |
|
170
|
-
yielder.yield source.from_database(hash)
|
207
|
+
Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |items, metadata|
|
208
|
+
yielder.yield(items.map { |hash| source.from_database(hash) }, metadata.slice(:last_evaluated_key))
|
171
209
|
end
|
172
210
|
end
|
173
211
|
end
|
174
212
|
|
213
|
+
def issue_scan_warning
|
214
|
+
Dynamoid.logger.warn 'Queries without an index are forced to use scan and are generally much slower than indexed queries!'
|
215
|
+
Dynamoid.logger.warn "You can index this query by adding index declaration to #{source.to_s.downcase}.rb:"
|
216
|
+
Dynamoid.logger.warn "* global_secondary_index hash_key: 'some-name', range_key: 'some-another-name'"
|
217
|
+
Dynamoid.logger.warn "* local_secondary_index range_key: 'some-name'"
|
218
|
+
Dynamoid.logger.warn "Not indexed attributes: #{query.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
|
219
|
+
end
|
220
|
+
|
175
221
|
def count_via_query
|
176
222
|
Dynamoid.adapter.query_count(source.table_name, range_query)
|
177
223
|
end
|
@@ -238,24 +284,24 @@ module Dynamoid #:nodoc:
|
|
238
284
|
opts = {}
|
239
285
|
|
240
286
|
# Add hash key
|
241
|
-
opts[:hash_key] = @hash_key
|
242
|
-
opts[:hash_value] = type_cast_condition_parameter(@hash_key, query[@hash_key])
|
287
|
+
opts[:hash_key] = @key_fields_detector.hash_key
|
288
|
+
opts[:hash_value] = type_cast_condition_parameter(@key_fields_detector.hash_key, query[@key_fields_detector.hash_key])
|
243
289
|
|
244
290
|
# Add range key
|
245
|
-
if @range_key
|
246
|
-
opts[:range_key] = @range_key
|
247
|
-
if query[@range_key].present?
|
248
|
-
value = type_cast_condition_parameter(@range_key, query[@range_key])
|
291
|
+
if @key_fields_detector.range_key
|
292
|
+
opts[:range_key] = @key_fields_detector.range_key
|
293
|
+
if query[@key_fields_detector.range_key].present?
|
294
|
+
value = type_cast_condition_parameter(@key_fields_detector.range_key, query[@key_fields_detector.range_key])
|
249
295
|
opts.update(range_eq: value)
|
250
296
|
end
|
251
297
|
|
252
|
-
query.keys.select { |k| k.to_s =~ /^#{@range_key}\./ }.each do |key|
|
298
|
+
query.keys.select { |k| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }.each do |key|
|
253
299
|
opts.merge!(range_hash(key))
|
254
300
|
end
|
255
301
|
end
|
256
302
|
|
257
|
-
(query.keys.map(&:to_sym) - [@hash_key.to_sym, @range_key.try(:to_sym)])
|
258
|
-
.reject { |k, _| k.to_s =~ /^#{@range_key}\./ }
|
303
|
+
(query.keys.map(&:to_sym) - [@key_fields_detector.hash_key.to_sym, @key_fields_detector.range_key.try(:to_sym)])
|
304
|
+
.reject { |k, _| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }
|
259
305
|
.each do |key|
|
260
306
|
if key.to_s.include?('.')
|
261
307
|
opts.update(field_hash(key))
|
@@ -284,53 +330,14 @@ module Dynamoid #:nodoc:
|
|
284
330
|
end
|
285
331
|
end
|
286
332
|
|
287
|
-
def key_present?
|
288
|
-
query_keys = query.keys.collect { |k| k.to_s.split('.').first }
|
289
|
-
|
290
|
-
# See if querying based on table hash key
|
291
|
-
if query.keys.map(&:to_s).include?(source.hash_key.to_s)
|
292
|
-
@hash_key = source.hash_key
|
293
|
-
|
294
|
-
# Use table's default range key
|
295
|
-
if query_keys.include?(source.range_key.to_s)
|
296
|
-
@range_key = source.range_key
|
297
|
-
return true
|
298
|
-
end
|
299
|
-
|
300
|
-
# See if can use any local secondary index range key
|
301
|
-
# Chooses the first LSI found that can be utilized for the query
|
302
|
-
source.local_secondary_indexes.each do |_, lsi|
|
303
|
-
next unless query_keys.include?(lsi.range_key.to_s)
|
304
|
-
@range_key = lsi.range_key
|
305
|
-
@index_name = lsi.name
|
306
|
-
end
|
307
|
-
|
308
|
-
return true
|
309
|
-
end
|
310
|
-
|
311
|
-
# See if can use any global secondary index
|
312
|
-
# Chooses the first GSI found that can be utilized for the query
|
313
|
-
# But only do so if projects ALL attributes otherwise we won't
|
314
|
-
# get back full data
|
315
|
-
source.global_secondary_indexes.each do |_, gsi|
|
316
|
-
next unless query.keys.map(&:to_s).include?(gsi.hash_key.to_s) && gsi.projected_attributes == :all
|
317
|
-
@hash_key = gsi.hash_key
|
318
|
-
@range_key = gsi.range_key
|
319
|
-
@index_name = gsi.name
|
320
|
-
return true
|
321
|
-
end
|
322
|
-
|
323
|
-
# Could not utilize any indices so we'll have to scan
|
324
|
-
false
|
325
|
-
end
|
326
|
-
|
327
333
|
# Start key needs to be set up based on the index utilized
|
328
334
|
# If using a secondary index then we must include the index's composite key
|
329
335
|
# as well as the tables composite key.
|
330
336
|
def start_key
|
331
337
|
return @start if @start.is_a?(Hash)
|
332
|
-
|
333
|
-
|
338
|
+
|
339
|
+
hash_key = @key_fields_detector.hash_key || source.hash_key
|
340
|
+
range_key = @key_fields_detector.range_key || source.range_key
|
334
341
|
|
335
342
|
key = {}
|
336
343
|
key[hash_key] = type_cast_condition_parameter(hash_key, @start.send(hash_key))
|
@@ -349,7 +356,7 @@ module Dynamoid #:nodoc:
|
|
349
356
|
|
350
357
|
def query_opts
|
351
358
|
opts = {}
|
352
|
-
opts[:index_name] = @index_name if @index_name
|
359
|
+
opts[:index_name] = @key_fields_detector.index_name if @key_fields_detector.index_name
|
353
360
|
opts[:select] = 'ALL_ATTRIBUTES'
|
354
361
|
opts[:record_limit] = @record_limit if @record_limit
|
355
362
|
opts[:scan_limit] = @scan_limit if @scan_limit
|