dynamoid 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|