dynamini 2.2.0 → 2.2.1
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 +4 -4
- data/Gemfile.lock +1 -1
- data/dynamini.gemspec +1 -1
- data/lib/dynamini/querying.rb +14 -5
- data/lib/dynamini/test_client.rb +29 -10
- data/spec/dynamini/querying_spec.rb +2 -3
- data/spec/dynamini/test_client_spec.rb +70 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4966ab1242cf3fffce9c1282d02f71c87e9c143f
|
4
|
+
data.tar.gz: e9e93317d5d05ae0589ef6f1c01a67325bdc9b4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c971de76f66f13bb5df42b644ba170230d68c07da574d3bed1ecec0a2294984a7fc0eae73bf7d0ea1dead39586a37aaf584df5db3e539503541f7108258a461
|
7
|
+
data.tar.gz: a141f1ead3c262ac92274aade2eb0aadf135054bc302ace6a0b7cda74a6f0f229d90917d6148c6fa30447267ee0a1a1f6245be6ae92f87f40f5880b2c9c3f0d3
|
data/Gemfile.lock
CHANGED
data/dynamini.gemspec
CHANGED
data/lib/dynamini/querying.rb
CHANGED
@@ -51,10 +51,12 @@ module Dynamini
|
|
51
51
|
def dynamo_query(args)
|
52
52
|
expression_attribute_values = build_expression_attribute_values(args)
|
53
53
|
key_condition_expression = build_key_condition_expression(args)
|
54
|
+
expression_attribute_names = build_expression_attribute_names(args)
|
54
55
|
query = set_extra_parameters(
|
55
56
|
{
|
56
|
-
|
57
|
+
table_name: table_name,
|
57
58
|
key_condition_expression: key_condition_expression,
|
59
|
+
expression_attribute_names: expression_attribute_names,
|
58
60
|
expression_attribute_values: expression_attribute_values
|
59
61
|
},
|
60
62
|
args)
|
@@ -69,14 +71,21 @@ module Dynamini
|
|
69
71
|
expression_values
|
70
72
|
end
|
71
73
|
|
74
|
+
def build_expression_attribute_names(args)
|
75
|
+
expression_values = {}
|
76
|
+
expression_values['#H'] = current_index_hash_key(args)
|
77
|
+
expression_values['#R'] = current_index_range_key(args) if args[:end] || args[:start]
|
78
|
+
expression_values
|
79
|
+
end
|
80
|
+
|
72
81
|
def build_key_condition_expression(args)
|
73
|
-
expression = "#
|
82
|
+
expression = "#H = :h"
|
74
83
|
if args[:start] && args[:end]
|
75
|
-
expression += " AND #
|
84
|
+
expression += " AND #R BETWEEN :s AND :e"
|
76
85
|
elsif args[:start]
|
77
|
-
expression += " AND #
|
86
|
+
expression += " AND #R >= :s"
|
78
87
|
elsif args[:end]
|
79
|
-
expression += " AND #
|
88
|
+
expression += " AND #R <= :e"
|
80
89
|
end
|
81
90
|
expression
|
82
91
|
end
|
data/lib/dynamini/test_client.rb
CHANGED
@@ -106,15 +106,18 @@ module Dynamini
|
|
106
106
|
# "foo = val AND bar >= val2"
|
107
107
|
# "foo = val AND bar BETWEEN val2 AND val3"
|
108
108
|
|
109
|
-
args[:expression_attribute_values].
|
110
|
-
|
111
|
-
end
|
109
|
+
attr_placeholders = args[:expression_attribute_values].merge(args[:expression_attribute_names])
|
110
|
+
attr_placeholders.each { |symbol, value| args[:key_condition_expression].gsub!(symbol, value.to_s) }
|
112
111
|
|
113
112
|
tokens = args[:key_condition_expression].split(/\s+/)
|
113
|
+
|
114
|
+
hash_key_name, range_key_name = determine_hash_and_range(args)
|
115
|
+
|
116
|
+
inspect_for_correct_keys?(tokens, hash_key_name, range_key_name)
|
114
117
|
start_val, end_val = range_key_limits(tokens)
|
115
118
|
|
116
119
|
if args[:index_name]
|
117
|
-
secondary_index_query(args)
|
120
|
+
secondary_index_query(args, start_val, end_val)
|
118
121
|
else
|
119
122
|
hash_key = hash_key_value(args).is_a?(Integer) ? tokens[2].to_i : tokens[2]
|
120
123
|
|
@@ -126,6 +129,15 @@ module Dynamini
|
|
126
129
|
end
|
127
130
|
end
|
128
131
|
|
132
|
+
def determine_hash_and_range(args)
|
133
|
+
if args[:index_name]
|
134
|
+
index = secondary_index[args[:index_name].to_s]
|
135
|
+
[index[:hash_key_name].to_s, index[:range_key_name].to_s]
|
136
|
+
else
|
137
|
+
[@hash_key_attr.to_s, @range_key_attr.to_s]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
129
141
|
def range_key_limits(tokens)
|
130
142
|
case tokens[5]
|
131
143
|
when ">=" then [tokens[6], nil]
|
@@ -144,14 +156,10 @@ module Dynamini
|
|
144
156
|
records
|
145
157
|
end
|
146
158
|
|
147
|
-
|
148
|
-
|
149
|
-
index = secondary_index[args[:index_name]]
|
159
|
+
def secondary_index_query(args = {}, start_val = nil, end_val = nil)
|
160
|
+
index = secondary_index[args[:index_name].to_s]
|
150
161
|
table = get_table(args[:table_name])
|
151
162
|
|
152
|
-
tokens = args[:key_condition_expression].split(/\s+/)
|
153
|
-
start_val, end_val = range_key_limits(tokens)
|
154
|
-
|
155
163
|
records = @range_key_attr ? get_values(table) : table.values
|
156
164
|
selected = sort_records(records, index, args, start_val, end_val)
|
157
165
|
OpenStruct.new(items: selected)
|
@@ -214,5 +222,16 @@ module Dynamini
|
|
214
222
|
|
215
223
|
attribute_hash
|
216
224
|
end
|
225
|
+
|
226
|
+
def inspect_for_correct_keys?(tokens, hash_key_name, range_key_name)
|
227
|
+
missed_keys = []
|
228
|
+
missed_keys << hash_key_name unless tokens[0] == hash_key_name
|
229
|
+
missed_keys << range_key_name unless (tokens.length < 4 || tokens[4] == range_key_name)
|
230
|
+
raise missed_key_dynamodb_error(missed_keys) if missed_keys.length > 0
|
231
|
+
end
|
232
|
+
|
233
|
+
def missed_key_dynamodb_error(missed_keys)
|
234
|
+
Aws::DynamoDB::Errors::ValidationException.new(400,"Query condition missed key schema element: #{missed_keys.join(', ')}")
|
235
|
+
end
|
217
236
|
end
|
218
237
|
end
|
@@ -105,7 +105,7 @@ describe Dynamini::Querying do
|
|
105
105
|
|
106
106
|
context 'hash key does not exist' do
|
107
107
|
it 'should return an empty array' do
|
108
|
-
expect(TestClassWithRange.query(hash_key: 'non-existent
|
108
|
+
expect(TestClassWithRange.query(hash_key: 'non-existent-key')).to eq([])
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
@@ -178,9 +178,8 @@ describe Dynamini::Querying do
|
|
178
178
|
end
|
179
179
|
|
180
180
|
it 'should return no results if none are found with the secondary index' do
|
181
|
-
expect(TestClassWithRange.query(hash_key: 'non-existent
|
181
|
+
expect(TestClassWithRange.query(hash_key: 'non-existent-key', index_name: :secondary_index)).to eq([])
|
182
182
|
end
|
183
|
-
|
184
183
|
end
|
185
184
|
end
|
186
185
|
|
@@ -112,7 +112,8 @@ describe Dynamini::TestClient do
|
|
112
112
|
test_client.update_item(table_name: 'integer_table', key: {hash_key_field: 1, range_key_field: 1}, attribute_updates: {abc: {value: 'abc', action: 'PUT'}})
|
113
113
|
response = test_client.query(
|
114
114
|
table_name: 'integer_table',
|
115
|
-
key_condition_expression: "
|
115
|
+
key_condition_expression: "#H = :h",
|
116
|
+
expression_attribute_names: {'#H' => 'hash_key_field'},
|
116
117
|
expression_attribute_values: {
|
117
118
|
":h" => 1
|
118
119
|
}
|
@@ -123,11 +124,12 @@ describe Dynamini::TestClient do
|
|
123
124
|
end
|
124
125
|
end
|
125
126
|
|
126
|
-
context 'with LE operator' do
|
127
|
+
context 'with LE operator' do # broken
|
127
128
|
it 'should return all items with range key less than or equal to the provided value' do
|
128
129
|
response = test_client.query(
|
129
130
|
table_name: table_name,
|
130
|
-
key_condition_expression: "
|
131
|
+
key_condition_expression: "#H = :h AND #R <= :e",
|
132
|
+
expression_attribute_names: {'#H' => 'hash_key_field', '#R' => 'range_key_field'},
|
131
133
|
expression_attribute_values: {
|
132
134
|
":h" => 'foo',
|
133
135
|
":e" => 2
|
@@ -143,7 +145,8 @@ describe Dynamini::TestClient do
|
|
143
145
|
it 'should return all items with range key greater than or equal to the provided value' do
|
144
146
|
response = test_client.query(
|
145
147
|
table_name: table_name,
|
146
|
-
key_condition_expression: "
|
148
|
+
key_condition_expression: "#H = :h AND #R >= :s",
|
149
|
+
expression_attribute_names: {'#H' => 'hash_key_field', '#R' => 'range_key_field'},
|
147
150
|
expression_attribute_values: {
|
148
151
|
":h" => 'foo',
|
149
152
|
":s" => 2
|
@@ -159,7 +162,8 @@ describe Dynamini::TestClient do
|
|
159
162
|
it 'should return all items with range key between the provided values' do
|
160
163
|
response = test_client.query(
|
161
164
|
table_name: table_name,
|
162
|
-
key_condition_expression: "
|
165
|
+
key_condition_expression: "#H = :h AND #R BETWEEN :s AND :e",
|
166
|
+
expression_attribute_names: {'#H' => 'hash_key_field', '#R' => 'range_key_field'},
|
163
167
|
expression_attribute_values: {
|
164
168
|
":h" => 'foo',
|
165
169
|
":s" => 2,
|
@@ -176,7 +180,8 @@ describe Dynamini::TestClient do
|
|
176
180
|
it 'should return all items with range key between the provided values' do
|
177
181
|
response = test_client.query(
|
178
182
|
table_name: table_name,
|
179
|
-
key_condition_expression: "
|
183
|
+
key_condition_expression: "#H = :h",
|
184
|
+
expression_attribute_names: {'#H' => 'hash_key_field'},
|
180
185
|
expression_attribute_values: {
|
181
186
|
":h" => 'foo'
|
182
187
|
}
|
@@ -187,6 +192,31 @@ describe Dynamini::TestClient do
|
|
187
192
|
end
|
188
193
|
end
|
189
194
|
|
195
|
+
context 'with invalid expression_attribute_names' do
|
196
|
+
it 'should raise an error about an invalid hash_key' do
|
197
|
+
expect{ test_client.query(
|
198
|
+
table_name: table_name,
|
199
|
+
key_condition_expression: "#H = :h",
|
200
|
+
expression_attribute_names: {'#H' => 'not_hash_key_field'},
|
201
|
+
expression_attribute_values: {
|
202
|
+
':h' => 'foo'
|
203
|
+
}
|
204
|
+
) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: hash_key_field")
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should raise an error about an invalid range_key' do
|
208
|
+
expect{ test_client.query(
|
209
|
+
table_name: table_name,
|
210
|
+
key_condition_expression: "#H = :h AND #R >= :s",
|
211
|
+
expression_attribute_names: {'#H' => 'not_hash_key_field', '#R' => 'not_range_key_field'},
|
212
|
+
expression_attribute_values: {
|
213
|
+
':h' => 'foo',
|
214
|
+
':s' => 30
|
215
|
+
}
|
216
|
+
) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: hash_key_field, range_key_field")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
190
220
|
context 'with secondary index' do
|
191
221
|
before do
|
192
222
|
test_client.update_item(table_name: table_name,
|
@@ -199,7 +229,8 @@ describe Dynamini::TestClient do
|
|
199
229
|
it 'should return all items with secondary range key less than or equal to the provided value' do
|
200
230
|
response = test_client.query(
|
201
231
|
table_name: table_name,
|
202
|
-
key_condition_expression: "
|
232
|
+
key_condition_expression: "#H = :h AND #R <= :e",
|
233
|
+
expression_attribute_names: {'#H' => 'abc', '#R' => 'secondary_range_key'},
|
203
234
|
expression_attribute_values: {
|
204
235
|
":h" => 'abc',
|
205
236
|
":e" => 8
|
@@ -216,7 +247,8 @@ describe Dynamini::TestClient do
|
|
216
247
|
it 'should return all items with secondary range key greater than or equal to the provided value' do
|
217
248
|
response = test_client.query(
|
218
249
|
table_name: table_name,
|
219
|
-
key_condition_expression: "
|
250
|
+
key_condition_expression: "#H = :h AND #R >= :s",
|
251
|
+
expression_attribute_names: {'#H' => 'abc', '#R' => 'secondary_range_key'},
|
220
252
|
expression_attribute_values: {
|
221
253
|
":h" => 'abc',
|
222
254
|
":s" => 8
|
@@ -233,7 +265,8 @@ describe Dynamini::TestClient do
|
|
233
265
|
it 'should return all items with secondary range key between the provided values' do
|
234
266
|
response = test_client.query(
|
235
267
|
table_name: table_name,
|
236
|
-
key_condition_expression: "
|
268
|
+
key_condition_expression: "#H = :h AND #R BETWEEN :s AND :e",
|
269
|
+
expression_attribute_names: {'#H' => 'abc', '#R' => 'secondary_range_key'},
|
237
270
|
expression_attribute_values: {
|
238
271
|
":h" => 'abc',
|
239
272
|
":s" => 8,
|
@@ -251,7 +284,8 @@ describe Dynamini::TestClient do
|
|
251
284
|
it 'should return all items sorted by their secondary index' do
|
252
285
|
response = test_client.query(
|
253
286
|
table_name: table_name,
|
254
|
-
key_condition_expression: "
|
287
|
+
key_condition_expression: "#H = :h",
|
288
|
+
expression_attribute_names: {'#H' => 'abc'},
|
255
289
|
expression_attribute_values: {
|
256
290
|
":h" => 'abc'
|
257
291
|
},
|
@@ -264,6 +298,32 @@ describe Dynamini::TestClient do
|
|
264
298
|
end
|
265
299
|
end
|
266
300
|
|
301
|
+
context 'with invalid expression_attribute_names' do
|
302
|
+
it 'should raise an error about an invalid hash_key' do
|
303
|
+
expect{ test_client.query(
|
304
|
+
table_name: table_name,
|
305
|
+
key_condition_expression: "#H = :h",
|
306
|
+
expression_attribute_names: {'#H' => 'not_hash_key_field'},
|
307
|
+
expression_attribute_values: {
|
308
|
+
':h' => 'abc'
|
309
|
+
},
|
310
|
+
index_name: 'secondary_index'
|
311
|
+
) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: abc")
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'should raise an error about an invalid range_key' do
|
315
|
+
expect{ test_client.query(
|
316
|
+
table_name: table_name,
|
317
|
+
key_condition_expression: "#H = :h AND #R >= :s",
|
318
|
+
expression_attribute_names: {'#H' => 'not_hash_key_field', '#R' => 'not_range_key_field'},
|
319
|
+
expression_attribute_values: {
|
320
|
+
':h' => 'abc',
|
321
|
+
':s' => 3
|
322
|
+
},
|
323
|
+
index_name: 'secondary_index'
|
324
|
+
) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: abc, secondary_range_key")
|
325
|
+
end
|
326
|
+
end
|
267
327
|
end
|
268
328
|
|
269
329
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamini
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Ward
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2016-03-
|
18
|
+
date: 2016-03-09 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activemodel
|