dynamoid 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +18 -0
  3. data/.travis.yml +5 -3
  4. data/CHANGELOG.md +15 -0
  5. data/README.md +113 -63
  6. data/Vagrantfile +2 -2
  7. data/docker-compose.yml +1 -1
  8. data/gemfiles/rails_4_2.gemfile +1 -1
  9. data/gemfiles/rails_5_0.gemfile +1 -1
  10. data/gemfiles/rails_5_1.gemfile +1 -1
  11. data/gemfiles/rails_5_2.gemfile +1 -1
  12. data/lib/dynamoid/adapter.rb +1 -0
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +26 -395
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +234 -0
  15. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +89 -0
  16. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +24 -0
  17. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +57 -0
  18. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +28 -0
  19. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +123 -0
  20. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +85 -0
  21. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +52 -0
  22. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +60 -0
  23. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +1 -0
  24. data/lib/dynamoid/associations/has_many.rb +1 -0
  25. data/lib/dynamoid/associations/has_one.rb +1 -0
  26. data/lib/dynamoid/associations/single_association.rb +1 -0
  27. data/lib/dynamoid/criteria.rb +4 -4
  28. data/lib/dynamoid/criteria/chain.rb +86 -79
  29. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +41 -0
  30. data/lib/dynamoid/criteria/key_fields_detector.rb +61 -0
  31. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +41 -0
  32. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +40 -0
  33. data/lib/dynamoid/document.rb +18 -13
  34. data/lib/dynamoid/dumping.rb +52 -40
  35. data/lib/dynamoid/fields.rb +4 -3
  36. data/lib/dynamoid/finders.rb +3 -3
  37. data/lib/dynamoid/persistence.rb +5 -6
  38. data/lib/dynamoid/primary_key_type_mapping.rb +1 -1
  39. data/lib/dynamoid/tasks.rb +1 -0
  40. data/lib/dynamoid/tasks/database.rake +2 -2
  41. data/lib/dynamoid/type_casting.rb +37 -19
  42. data/lib/dynamoid/undumping.rb +53 -42
  43. data/lib/dynamoid/validations.rb +2 -0
  44. data/lib/dynamoid/version.rb +1 -1
  45. metadata +17 -5
  46. data/lib/dynamoid/adapter_plugin/query.rb +0 -144
  47. 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