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,234 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'until_past_table_status'
|
4
|
+
|
5
|
+
module Dynamoid
|
6
|
+
module AdapterPlugin
|
7
|
+
class AwsSdkV3
|
8
|
+
class CreateTable
|
9
|
+
attr_reader :client, :table_name, :key, :options
|
10
|
+
|
11
|
+
def initialize(client, table_name, key, options)
|
12
|
+
@client = client
|
13
|
+
@table_name = table_name
|
14
|
+
@key = key
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
read_capacity = options[:read_capacity] || Dynamoid::Config.read_capacity
|
20
|
+
write_capacity = options[:write_capacity] || Dynamoid::Config.write_capacity
|
21
|
+
|
22
|
+
secondary_indexes = options.slice(
|
23
|
+
:local_secondary_indexes,
|
24
|
+
:global_secondary_indexes
|
25
|
+
)
|
26
|
+
ls_indexes = options[:local_secondary_indexes]
|
27
|
+
gs_indexes = options[:global_secondary_indexes]
|
28
|
+
|
29
|
+
key_schema = {
|
30
|
+
hash_key_schema: { key => (options[:hash_key_type] || :string) },
|
31
|
+
range_key_schema: options[:range_key]
|
32
|
+
}
|
33
|
+
attribute_definitions = build_all_attribute_definitions(
|
34
|
+
key_schema,
|
35
|
+
secondary_indexes
|
36
|
+
)
|
37
|
+
key_schema = aws_key_schema(
|
38
|
+
key_schema[:hash_key_schema],
|
39
|
+
key_schema[:range_key_schema]
|
40
|
+
)
|
41
|
+
|
42
|
+
client_opts = {
|
43
|
+
table_name: table_name,
|
44
|
+
provisioned_throughput: {
|
45
|
+
read_capacity_units: read_capacity,
|
46
|
+
write_capacity_units: write_capacity
|
47
|
+
},
|
48
|
+
key_schema: key_schema,
|
49
|
+
attribute_definitions: attribute_definitions
|
50
|
+
}
|
51
|
+
|
52
|
+
if ls_indexes.present?
|
53
|
+
client_opts[:local_secondary_indexes] = ls_indexes.map do |index|
|
54
|
+
index_to_aws_hash(index)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if gs_indexes.present?
|
59
|
+
client_opts[:global_secondary_indexes] = gs_indexes.map do |index|
|
60
|
+
index_to_aws_hash(index)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
resp = client.create_table(client_opts)
|
64
|
+
options[:sync] = true if !options.key?(:sync) && ls_indexes.present? || gs_indexes.present?
|
65
|
+
UntilPastTableStatus.new(table_name, :creating).call if options[:sync] &&
|
66
|
+
(status = PARSE_TABLE_STATUS.call(resp, :table_description)) &&
|
67
|
+
status == TABLE_STATUSES[:creating]
|
68
|
+
# Response to original create_table, which, if options[:sync]
|
69
|
+
# may have an outdated table_description.table_status of "CREATING"
|
70
|
+
resp
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Builds aws attributes definitions based off of primary hash/range and
|
76
|
+
# secondary indexes
|
77
|
+
#
|
78
|
+
# @param key_data
|
79
|
+
# @option key_data [Hash] hash_key_schema - eg: {:id => :string}
|
80
|
+
# @option key_data [Hash] range_key_schema - eg: {:created_at => :number}
|
81
|
+
# @param [Hash] secondary_indexes
|
82
|
+
# @option secondary_indexes [Array<Dynamoid::Indexes::Index>] :local_secondary_indexes
|
83
|
+
# @option secondary_indexes [Array<Dynamoid::Indexes::Index>] :global_secondary_indexes
|
84
|
+
def build_all_attribute_definitions(key_schema, secondary_indexes = {})
|
85
|
+
ls_indexes = secondary_indexes[:local_secondary_indexes]
|
86
|
+
gs_indexes = secondary_indexes[:global_secondary_indexes]
|
87
|
+
|
88
|
+
attribute_definitions = []
|
89
|
+
|
90
|
+
attribute_definitions << build_attribute_definitions(
|
91
|
+
key_schema[:hash_key_schema],
|
92
|
+
key_schema[:range_key_schema]
|
93
|
+
)
|
94
|
+
|
95
|
+
if ls_indexes.present?
|
96
|
+
ls_indexes.map do |index|
|
97
|
+
attribute_definitions << build_attribute_definitions(
|
98
|
+
index.hash_key_schema,
|
99
|
+
index.range_key_schema
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
if gs_indexes.present?
|
105
|
+
gs_indexes.map do |index|
|
106
|
+
attribute_definitions << build_attribute_definitions(
|
107
|
+
index.hash_key_schema,
|
108
|
+
index.range_key_schema
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
attribute_definitions.flatten!
|
114
|
+
# uniq these definitions because range keys might be common between
|
115
|
+
# primary and secondary indexes
|
116
|
+
attribute_definitions.uniq!
|
117
|
+
attribute_definitions
|
118
|
+
end
|
119
|
+
|
120
|
+
# Builds an attribute definitions based on hash key and range key
|
121
|
+
# @params [Hash] hash_key_schema - eg: {:id => :string}
|
122
|
+
# @params [Hash] range_key_schema - eg: {:created_at => :datetime}
|
123
|
+
# @return [Array]
|
124
|
+
def build_attribute_definitions(hash_key_schema, range_key_schema = nil)
|
125
|
+
attrs = []
|
126
|
+
|
127
|
+
attrs << attribute_definition_element(
|
128
|
+
hash_key_schema.keys.first,
|
129
|
+
hash_key_schema.values.first
|
130
|
+
)
|
131
|
+
|
132
|
+
if range_key_schema.present?
|
133
|
+
attrs << attribute_definition_element(
|
134
|
+
range_key_schema.keys.first,
|
135
|
+
range_key_schema.values.first
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
attrs
|
140
|
+
end
|
141
|
+
|
142
|
+
# Builds an aws attribute definition based on name and dynamoid type
|
143
|
+
# @params [Symbol] name - eg: :id
|
144
|
+
# @params [Symbol] dynamoid_type - eg: :string
|
145
|
+
# @return [Hash]
|
146
|
+
def attribute_definition_element(name, dynamoid_type)
|
147
|
+
aws_type = api_type(dynamoid_type)
|
148
|
+
|
149
|
+
{
|
150
|
+
attribute_name: name.to_s,
|
151
|
+
attribute_type: aws_type
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
# Converts from symbol to the API string for the given data type
|
156
|
+
# E.g. :number -> 'N'
|
157
|
+
def api_type(type)
|
158
|
+
case type
|
159
|
+
when :string then STRING_TYPE
|
160
|
+
when :number then NUM_TYPE
|
161
|
+
when :binary then BINARY_TYPE
|
162
|
+
else raise "Unknown type: #{type}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Converts a Dynamoid::Indexes::Index to an AWS API-compatible hash.
|
167
|
+
# This resulting hash is of the form:
|
168
|
+
#
|
169
|
+
# {
|
170
|
+
# index_name: String
|
171
|
+
# keys: {
|
172
|
+
# hash_key: aws_key_schema (hash)
|
173
|
+
# range_key: aws_key_schema (hash)
|
174
|
+
# }
|
175
|
+
# projection: {
|
176
|
+
# projection_type: (ALL, KEYS_ONLY, INCLUDE) String
|
177
|
+
# non_key_attributes: (optional) Array
|
178
|
+
# }
|
179
|
+
# provisioned_throughput: {
|
180
|
+
# read_capacity_units: Integer
|
181
|
+
# write_capacity_units: Integer
|
182
|
+
# }
|
183
|
+
# }
|
184
|
+
#
|
185
|
+
# @param [Dynamoid::Indexes::Index] index the index.
|
186
|
+
# @return [Hash] hash representing an AWS Index definition.
|
187
|
+
def index_to_aws_hash(index)
|
188
|
+
key_schema = aws_key_schema(index.hash_key_schema, index.range_key_schema)
|
189
|
+
|
190
|
+
hash = {
|
191
|
+
index_name: index.name,
|
192
|
+
key_schema: key_schema,
|
193
|
+
projection: {
|
194
|
+
projection_type: index.projection_type.to_s.upcase
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
# If the projection type is include, specify the non key attributes
|
199
|
+
if index.projection_type == :include
|
200
|
+
hash[:projection][:non_key_attributes] = index.projected_attributes
|
201
|
+
end
|
202
|
+
|
203
|
+
# Only global secondary indexes have a separate throughput.
|
204
|
+
if index.type == :global_secondary
|
205
|
+
hash[:provisioned_throughput] = {
|
206
|
+
read_capacity_units: index.read_capacity,
|
207
|
+
write_capacity_units: index.write_capacity
|
208
|
+
}
|
209
|
+
end
|
210
|
+
hash
|
211
|
+
end
|
212
|
+
|
213
|
+
# Converts hash_key_schema and range_key_schema to aws_key_schema
|
214
|
+
# @param [Hash] hash_key_schema eg: {:id => :string}
|
215
|
+
# @param [Hash] range_key_schema eg: {:created_at => :number}
|
216
|
+
# @return [Array]
|
217
|
+
def aws_key_schema(hash_key_schema, range_key_schema)
|
218
|
+
schema = [{
|
219
|
+
attribute_name: hash_key_schema.keys.first.to_s,
|
220
|
+
key_type: HASH_KEY
|
221
|
+
}]
|
222
|
+
|
223
|
+
if range_key_schema.present?
|
224
|
+
schema << {
|
225
|
+
attribute_name: range_key_schema.keys.first.to_s,
|
226
|
+
key_type: RANGE_KEY
|
227
|
+
}
|
228
|
+
end
|
229
|
+
schema
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module AdapterPlugin
|
5
|
+
class AwsSdkV3
|
6
|
+
# Mimics behavior of the yielded object on DynamoDB's update_item API (high level).
|
7
|
+
class ItemUpdater
|
8
|
+
attr_reader :table, :key, :range_key
|
9
|
+
|
10
|
+
def initialize(table, key, range_key = nil)
|
11
|
+
@table = table
|
12
|
+
@key = key
|
13
|
+
@range_key = range_key
|
14
|
+
@additions = {}
|
15
|
+
@deletions = {}
|
16
|
+
@updates = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Adds the given values to the values already stored in the corresponding columns.
|
21
|
+
# The column must contain a Set or a number.
|
22
|
+
#
|
23
|
+
# @param [Hash] vals keys of the hash are the columns to update, vals are the values to
|
24
|
+
# add. values must be a Set, Array, or Numeric
|
25
|
+
#
|
26
|
+
def add(values)
|
27
|
+
@additions.merge!(sanitize_attributes(values))
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Removes values from the sets of the given columns
|
32
|
+
#
|
33
|
+
# @param [Hash] values keys of the hash are the columns, values are Arrays/Sets of items
|
34
|
+
# to remove
|
35
|
+
#
|
36
|
+
def delete(values)
|
37
|
+
@deletions.merge!(sanitize_attributes(values))
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Replaces the values of one or more attributes
|
42
|
+
#
|
43
|
+
def set(values)
|
44
|
+
@updates.merge!(sanitize_attributes(values))
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Returns an AttributeUpdates hash suitable for passing to the V2 Client API
|
49
|
+
#
|
50
|
+
def to_h
|
51
|
+
ret = {}
|
52
|
+
|
53
|
+
@additions.each do |k, v|
|
54
|
+
ret[k.to_s] = {
|
55
|
+
action: ADD,
|
56
|
+
value: v
|
57
|
+
}
|
58
|
+
end
|
59
|
+
@deletions.each do |k, v|
|
60
|
+
ret[k.to_s] = {
|
61
|
+
action: DELETE,
|
62
|
+
value: v
|
63
|
+
}
|
64
|
+
end
|
65
|
+
@updates.each do |k, v|
|
66
|
+
ret[k.to_s] = {
|
67
|
+
action: PUT,
|
68
|
+
value: v
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
ret
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def sanitize_attributes(attributes)
|
78
|
+
attributes.transform_values do |v|
|
79
|
+
v.is_a?(Hash) ? v.stringify_keys : v
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
ADD = 'ADD'
|
84
|
+
DELETE = 'DELETE'
|
85
|
+
PUT = 'PUT'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module AdapterPlugin
|
5
|
+
class AwsSdkV3
|
6
|
+
module Middleware
|
7
|
+
class Backoff
|
8
|
+
def initialize(next_chain)
|
9
|
+
@next_chain = next_chain
|
10
|
+
@backoff = Dynamoid.config.backoff ? Dynamoid.config.build_backoff : nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(request)
|
14
|
+
response = @next_chain.call(request)
|
15
|
+
@backoff.call if @backoff
|
16
|
+
|
17
|
+
return response
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module AdapterPlugin
|
5
|
+
class AwsSdkV3
|
6
|
+
module Middleware
|
7
|
+
class Limit
|
8
|
+
def initialize(next_chain, record_limit: nil, scan_limit: nil)
|
9
|
+
@next_chain = next_chain
|
10
|
+
|
11
|
+
@record_limit = record_limit
|
12
|
+
@scan_limit = scan_limit
|
13
|
+
|
14
|
+
@record_count = 0
|
15
|
+
@scan_count = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(request)
|
19
|
+
# Adjust the limit down if the remaining record and/or scan limit are
|
20
|
+
# lower to obey limits. We can assume the difference won't be
|
21
|
+
# negative due to break statements below but choose smaller limit
|
22
|
+
# which is why we have 2 separate if statements.
|
23
|
+
# NOTE: Adjusting based on record_limit can cause many HTTP requests
|
24
|
+
# being made. We may want to change this behavior, but it affects
|
25
|
+
# filtering on data with potentially large gaps.
|
26
|
+
# Example:
|
27
|
+
# User.where('created_at.gte' => 1.day.ago).record_limit(1000)
|
28
|
+
# Records 1-999 User's that fit criteria
|
29
|
+
# Records 1000-2000 Users's that do not fit criteria
|
30
|
+
# Record 2001 fits criteria
|
31
|
+
# The underlying implementation will have 1 page for records 1-999
|
32
|
+
# then will request with limit 1 for records 1000-2000 (making 1000
|
33
|
+
# requests of limit 1) until hit record 2001.
|
34
|
+
if request[:limit] && @record_limit && @record_limit - @record_count < request[:limit]
|
35
|
+
request[:limit] = @record_limit - @record_count
|
36
|
+
end
|
37
|
+
if request[:limit] && @scan_limit && @scan_limit - @scan_count < request[:limit]
|
38
|
+
request[:limit] = @scan_limit - @scan_count
|
39
|
+
end
|
40
|
+
|
41
|
+
response = @next_chain.call(request)
|
42
|
+
|
43
|
+
@record_count += response.count
|
44
|
+
throw :stop_pagination if @record_limit && @record_count >= @record_limit
|
45
|
+
|
46
|
+
@scan_count += response.scanned_count
|
47
|
+
throw :stop_pagination if @scan_limit && @scan_count >= @scan_limit
|
48
|
+
|
49
|
+
return response
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
module AdapterPlugin
|
5
|
+
class AwsSdkV3
|
6
|
+
module Middleware
|
7
|
+
class StartKey
|
8
|
+
def initialize(next_chain)
|
9
|
+
@next_chain = next_chain
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(request)
|
13
|
+
response = @next_chain.call(request)
|
14
|
+
|
15
|
+
if response.last_evaluated_key
|
16
|
+
request[:exclusive_start_key] = response.last_evaluated_key
|
17
|
+
else
|
18
|
+
throw :stop_pagination
|
19
|
+
end
|
20
|
+
|
21
|
+
return response
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,123 @@
|
|
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 Query
|
11
|
+
OPTIONS_KEYS = %i[
|
12
|
+
limit hash_key hash_value range_key consistent_read scan_index_forward
|
13
|
+
select index_name batch_size exclusive_start_key record_limit scan_limit
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
attr_reader :client, :table, :options, :conditions
|
17
|
+
|
18
|
+
def initialize(client, table, opts = {})
|
19
|
+
@client = client
|
20
|
+
@table = table
|
21
|
+
|
22
|
+
opts = opts.symbolize_keys
|
23
|
+
@options = opts.slice(*OPTIONS_KEYS)
|
24
|
+
@conditions = opts.except(*OPTIONS_KEYS)
|
25
|
+
end
|
26
|
+
|
27
|
+
def call
|
28
|
+
request = build_request
|
29
|
+
|
30
|
+
Enumerator.new do |yielder|
|
31
|
+
api_call = -> (request) do
|
32
|
+
client.query(request).tap do |response|
|
33
|
+
yielder << response
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
middlewares = Middleware::Backoff.new(
|
38
|
+
Middleware::StartKey.new(
|
39
|
+
Middleware::Limit.new(api_call, record_limit: record_limit, scan_limit: scan_limit)
|
40
|
+
)
|
41
|
+
)
|
42
|
+
|
43
|
+
catch :stop_pagination do
|
44
|
+
loop do
|
45
|
+
middlewares.call(request)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def build_request
|
54
|
+
request = options.slice(
|
55
|
+
:consistent_read,
|
56
|
+
:scan_index_forward,
|
57
|
+
:select,
|
58
|
+
:index_name,
|
59
|
+
:exclusive_start_key
|
60
|
+
).compact
|
61
|
+
|
62
|
+
# Deal with various limits and batching
|
63
|
+
batch_size = options[:batch_size]
|
64
|
+
limit = [record_limit, scan_limit, batch_size].compact.min
|
65
|
+
|
66
|
+
request[:limit] = limit if limit
|
67
|
+
request[:table_name] = table.name
|
68
|
+
request[:key_conditions] = key_conditions
|
69
|
+
request[:query_filter] = query_filter
|
70
|
+
|
71
|
+
request
|
72
|
+
end
|
73
|
+
|
74
|
+
def record_limit
|
75
|
+
options[:record_limit]
|
76
|
+
end
|
77
|
+
|
78
|
+
def scan_limit
|
79
|
+
options[:scan_limit]
|
80
|
+
end
|
81
|
+
|
82
|
+
def hash_key_name
|
83
|
+
(options[:hash_key] || table.hash_key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def range_key_name
|
87
|
+
(options[:range_key] || table.range_key)
|
88
|
+
end
|
89
|
+
|
90
|
+
def key_conditions
|
91
|
+
result = {
|
92
|
+
hash_key_name => {
|
93
|
+
comparison_operator: AwsSdkV3::EQ,
|
94
|
+
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::EQ, options[:hash_value].freeze)
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
conditions.slice(*AwsSdkV3::RANGE_MAP.keys).each do |k, _v|
|
99
|
+
op = AwsSdkV3::RANGE_MAP[k]
|
100
|
+
|
101
|
+
result[range_key_name] = {
|
102
|
+
comparison_operator: op,
|
103
|
+
attribute_value_list: AwsSdkV3.attribute_value_list(op, conditions[k].freeze)
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
def query_filter
|
111
|
+
conditions.except(*AwsSdkV3::RANGE_MAP.keys).reduce({}) do |result, (attr, cond)|
|
112
|
+
condition = {
|
113
|
+
comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
|
114
|
+
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
|
115
|
+
}
|
116
|
+
result[attr] = condition
|
117
|
+
result
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|