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
@@ -1,144 +0,0 @@
|
|
1
|
-
module Dynamoid
|
2
|
-
module AdapterPlugin
|
3
|
-
class Query
|
4
|
-
OPTIONS_KEYS = [
|
5
|
-
:limit, :hash_key, :hash_value, :range_key, :consistent_read, :scan_index_forward,
|
6
|
-
:select, :index_name, :batch_size, :exclusive_start_key, :record_limit, :scan_limit
|
7
|
-
]
|
8
|
-
|
9
|
-
attr_reader :client, :table, :options, :conditions
|
10
|
-
|
11
|
-
def initialize(client, table, opts = {})
|
12
|
-
@client = client
|
13
|
-
@table = table
|
14
|
-
|
15
|
-
opts = opts.symbolize_keys
|
16
|
-
@options = opts.slice(*OPTIONS_KEYS)
|
17
|
-
@conditions = opts.except(*OPTIONS_KEYS)
|
18
|
-
end
|
19
|
-
|
20
|
-
def call
|
21
|
-
request = build_request
|
22
|
-
|
23
|
-
Enumerator.new do |yielder|
|
24
|
-
record_count = 0
|
25
|
-
scan_count = 0
|
26
|
-
|
27
|
-
backoff = Dynamoid.config.backoff ? Dynamoid.config.build_backoff : nil
|
28
|
-
|
29
|
-
loop do
|
30
|
-
# Adjust the limit down if the remaining record and/or scan limit are
|
31
|
-
# lower to obey limits. We can assume the difference won't be
|
32
|
-
# negative due to break statements below but choose smaller limit
|
33
|
-
# which is why we have 2 separate if statements.
|
34
|
-
# NOTE: Adjusting based on record_limit can cause many HTTP requests
|
35
|
-
# being made. We may want to change this behavior, but it affects
|
36
|
-
# filtering on data with potentially large gaps.
|
37
|
-
# Example:
|
38
|
-
# User.where('created_at.gte' => 1.day.ago).record_limit(1000)
|
39
|
-
# Records 1-999 User's that fit criteria
|
40
|
-
# Records 1000-2000 Users's that do not fit criteria
|
41
|
-
# Record 2001 fits criteria
|
42
|
-
# The underlying implementation will have 1 page for records 1-999
|
43
|
-
# then will request with limit 1 for records 1000-2000 (making 1000
|
44
|
-
# requests of limit 1) until hit record 2001.
|
45
|
-
if request[:limit] && record_limit && record_limit - record_count < request[:limit]
|
46
|
-
request[:limit] = record_limit - record_count
|
47
|
-
end
|
48
|
-
if request[:limit] && scan_limit && scan_limit - scan_count < request[:limit]
|
49
|
-
request[:limit] = scan_limit - scan_count
|
50
|
-
end
|
51
|
-
|
52
|
-
response = client.query(request)
|
53
|
-
|
54
|
-
yielder << response
|
55
|
-
|
56
|
-
record_count += response.count
|
57
|
-
break if record_limit && record_count >= record_limit
|
58
|
-
|
59
|
-
scan_count += response.scanned_count
|
60
|
-
break if scan_limit && scan_count >= scan_limit
|
61
|
-
|
62
|
-
if response.last_evaluated_key
|
63
|
-
request[:exclusive_start_key] = response.last_evaluated_key
|
64
|
-
else
|
65
|
-
break
|
66
|
-
end
|
67
|
-
|
68
|
-
backoff.call if backoff
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def build_request
|
76
|
-
request = options.slice(
|
77
|
-
:consistent_read,
|
78
|
-
:scan_index_forward,
|
79
|
-
:select,
|
80
|
-
:index_name,
|
81
|
-
:exclusive_start_key
|
82
|
-
).compact
|
83
|
-
|
84
|
-
# Deal with various limits and batching
|
85
|
-
batch_size = options[:batch_size]
|
86
|
-
limit = [record_limit, scan_limit, batch_size].compact.min
|
87
|
-
|
88
|
-
request[:limit] = limit if limit
|
89
|
-
request[:table_name] = table.name
|
90
|
-
request[:key_conditions] = key_conditions
|
91
|
-
request[:query_filter] = query_filter
|
92
|
-
|
93
|
-
request
|
94
|
-
end
|
95
|
-
|
96
|
-
def record_limit
|
97
|
-
options[:record_limit]
|
98
|
-
end
|
99
|
-
|
100
|
-
def scan_limit
|
101
|
-
options[:scan_limit]
|
102
|
-
end
|
103
|
-
|
104
|
-
def hash_key_name
|
105
|
-
(options[:hash_key] || table.hash_key)
|
106
|
-
end
|
107
|
-
|
108
|
-
def range_key_name
|
109
|
-
(options[:range_key] || table.range_key)
|
110
|
-
end
|
111
|
-
|
112
|
-
def key_conditions
|
113
|
-
result = {
|
114
|
-
hash_key_name => {
|
115
|
-
comparison_operator: AwsSdkV3::EQ,
|
116
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::EQ, options[:hash_value].freeze)
|
117
|
-
}
|
118
|
-
}
|
119
|
-
|
120
|
-
conditions.slice(*AwsSdkV3::RANGE_MAP.keys).each do |k, _v|
|
121
|
-
op = AwsSdkV3::RANGE_MAP[k]
|
122
|
-
|
123
|
-
result[range_key_name] = {
|
124
|
-
comparison_operator: op,
|
125
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(op, conditions[k].freeze)
|
126
|
-
}
|
127
|
-
end
|
128
|
-
|
129
|
-
result
|
130
|
-
end
|
131
|
-
|
132
|
-
def query_filter
|
133
|
-
conditions.except(*AwsSdkV3::RANGE_MAP.keys).reduce({}) do |result, (attr, cond)|
|
134
|
-
condition = {
|
135
|
-
comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
|
136
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
|
137
|
-
}
|
138
|
-
result[attr] = condition
|
139
|
-
result
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
@@ -1,107 +0,0 @@
|
|
1
|
-
module Dynamoid
|
2
|
-
module AdapterPlugin
|
3
|
-
class Scan
|
4
|
-
attr_reader :client, :table, :conditions, :options
|
5
|
-
|
6
|
-
def initialize(client, table, conditions = {}, options = {})
|
7
|
-
@client = client
|
8
|
-
@table = table
|
9
|
-
@conditions = conditions
|
10
|
-
@options = options
|
11
|
-
end
|
12
|
-
|
13
|
-
def call
|
14
|
-
request = build_request
|
15
|
-
|
16
|
-
Enumerator.new do |yielder|
|
17
|
-
record_count = 0
|
18
|
-
scan_count = 0
|
19
|
-
|
20
|
-
backoff = Dynamoid.config.backoff ? Dynamoid.config.build_backoff : nil
|
21
|
-
|
22
|
-
loop do
|
23
|
-
# Adjust the limit down if the remaining record and/or scan limit are
|
24
|
-
# lower to obey limits. We can assume the difference won't be
|
25
|
-
# negative due to break statements below but choose smaller limit
|
26
|
-
# which is why we have 2 separate if statements.
|
27
|
-
# NOTE: Adjusting based on record_limit can cause many HTTP requests
|
28
|
-
# being made. We may want to change this behavior, but it affects
|
29
|
-
# filtering on data with potentially large gaps.
|
30
|
-
# Example:
|
31
|
-
# User.where('created_at.gte' => 1.day.ago).record_limit(1000)
|
32
|
-
# Records 1-999 User's that fit criteria
|
33
|
-
# Records 1000-2000 Users's that do not fit criteria
|
34
|
-
# Record 2001 fits criteria
|
35
|
-
# The underlying implementation will have 1 page for records 1-999
|
36
|
-
# then will request with limit 1 for records 1000-2000 (making 1000
|
37
|
-
# requests of limit 1) until hit record 2001.
|
38
|
-
if request[:limit] && record_limit && record_limit - record_count < request[:limit]
|
39
|
-
request[:limit] = record_limit - record_count
|
40
|
-
end
|
41
|
-
if request[:limit] && scan_limit && scan_limit - scan_count < request[:limit]
|
42
|
-
request[:limit] = scan_limit - scan_count
|
43
|
-
end
|
44
|
-
|
45
|
-
response = client.scan(request)
|
46
|
-
|
47
|
-
yielder << response
|
48
|
-
|
49
|
-
record_count += response.count
|
50
|
-
break if record_limit && record_count >= record_limit
|
51
|
-
|
52
|
-
scan_count += response.scanned_count
|
53
|
-
break if scan_limit && scan_count >= scan_limit
|
54
|
-
|
55
|
-
# Keep pulling if we haven't finished paging in all data
|
56
|
-
if response.last_evaluated_key
|
57
|
-
request[:exclusive_start_key] = response.last_evaluated_key
|
58
|
-
else
|
59
|
-
break
|
60
|
-
end
|
61
|
-
|
62
|
-
backoff.call if backoff
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def build_request
|
70
|
-
request = options.slice(
|
71
|
-
:consistent_read,
|
72
|
-
:exclusive_start_key,
|
73
|
-
:select
|
74
|
-
).compact
|
75
|
-
|
76
|
-
# Deal with various limits and batching
|
77
|
-
batch_size = options[:batch_size]
|
78
|
-
limit = [record_limit, scan_limit, batch_size].compact.min
|
79
|
-
|
80
|
-
request[:limit] = limit if limit
|
81
|
-
request[:table_name] = table.name
|
82
|
-
request[:scan_filter] = scan_filter
|
83
|
-
|
84
|
-
request
|
85
|
-
end
|
86
|
-
|
87
|
-
def record_limit
|
88
|
-
options[:record_limit]
|
89
|
-
end
|
90
|
-
|
91
|
-
def scan_limit
|
92
|
-
options[:scan_limit]
|
93
|
-
end
|
94
|
-
|
95
|
-
def scan_filter
|
96
|
-
conditions.reduce({}) do |result, (attr, cond)|
|
97
|
-
condition = {
|
98
|
-
comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
|
99
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
|
100
|
-
}
|
101
|
-
result[attr] = condition
|
102
|
-
result
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|