aws-record 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/aws-record.rb +1 -0
- data/lib/aws-record/record/buildable_search.rb +232 -0
- data/lib/aws-record/record/query.rb +48 -0
- data/lib/aws-record/record/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c0d7d527489fd1a7848279570aefbe37aeee9297
|
4
|
+
data.tar.gz: f3234598e3f895c93184a7b283a7064853ad3707
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afd2c0b89fac562aeaee2fb0f67f25cee31700b29639e52a8281babd769a45b8da8bf2cedf86d396904e1103b4f9e2722445a13bada7cab446c443b460b8e42b
|
7
|
+
data.tar.gz: f0dfc0620149bbf63d2b5334dc441f2370e220278a3893f5b84b1e68a6a74acfcfec9f36e67cb4d31696add5e4795a567a4444d4b05f685dcbfbe8dbfbe0df98
|
data/lib/aws-record.rb
CHANGED
@@ -28,6 +28,7 @@ require_relative 'aws-record/record/table_config'
|
|
28
28
|
require_relative 'aws-record/record/table_migration'
|
29
29
|
require_relative 'aws-record/record/version'
|
30
30
|
require_relative 'aws-record/record/transactions'
|
31
|
+
require_relative 'aws-record/record/buildable_search'
|
31
32
|
require_relative 'aws-record/record/marshalers/string_marshaler'
|
32
33
|
require_relative 'aws-record/record/marshalers/boolean_marshaler'
|
33
34
|
require_relative 'aws-record/record/marshalers/integer_marshaler'
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Aws
|
2
|
+
module Record
|
3
|
+
class BuildableSearch
|
4
|
+
SUPPORTED_OPERATIONS = [:query, :scan]
|
5
|
+
|
6
|
+
# This should never be called directly, rather it is called by the
|
7
|
+
# #build_query or #build_scan methods of your aws-record model class.
|
8
|
+
def initialize(opts)
|
9
|
+
operation = opts[:operation]
|
10
|
+
model = opts[:model]
|
11
|
+
if SUPPORTED_OPERATIONS.include?(operation)
|
12
|
+
@operation = operation
|
13
|
+
else
|
14
|
+
raise ArgumentError.new("Unsupported operation: #{operation}")
|
15
|
+
end
|
16
|
+
@model = model
|
17
|
+
@params = {}
|
18
|
+
@next_name = "BUILDERA"
|
19
|
+
@next_value = "buildera"
|
20
|
+
end
|
21
|
+
|
22
|
+
# If you are querying or scanning on an index, you can specify it with
|
23
|
+
# this builder method. Provide the symbol of your index as defined on your
|
24
|
+
# model class.
|
25
|
+
def on_index(index)
|
26
|
+
@params[:index_name] = index
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# If true, will perform your query or scan as a consistent read. If false,
|
31
|
+
# the query or scan is eventually consistent.
|
32
|
+
def consistent_read(b)
|
33
|
+
@params[:consistent_read] = b
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# For the scan operation, you can split your scan into multiple segments
|
38
|
+
# to be scanned in parallel. If you wish to do this, you can use this
|
39
|
+
# builder method to provide the :total_segments of your parallel scan and
|
40
|
+
# the :segment number of this scan.
|
41
|
+
def parallel_scan(opts)
|
42
|
+
unless @operation == :scan
|
43
|
+
raise ArgumentError.new("parallel_scan is only supported for scans")
|
44
|
+
end
|
45
|
+
unless opts[:total_segments] && opts[:segment]
|
46
|
+
raise ArgumentError.new("Must specify :total_segments and :segment in a parallel scan.")
|
47
|
+
end
|
48
|
+
@params[:total_segments] = opts[:total_segments]
|
49
|
+
@params[:segment] = opts[:segment]
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# For a query operation, you can use this to set if you query is in
|
54
|
+
# ascending or descending order on your range key. By default, a query is
|
55
|
+
# run in ascending order.
|
56
|
+
def scan_ascending(b)
|
57
|
+
unless @operation == :query
|
58
|
+
raise ArgumentError.new("scan_ascending is only supported for queries.")
|
59
|
+
end
|
60
|
+
@params[:scan_index_forward] = b
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# If you have an exclusive start key for your query or scan, you can
|
65
|
+
# provide it with this builder method. You should not use this if you are
|
66
|
+
# querying or scanning without a set starting point, as the
|
67
|
+
# {Aws::Record::ItemCollection} class handles pagination automatically
|
68
|
+
# for you.
|
69
|
+
def exclusive_start_key(key)
|
70
|
+
@params[:exclusive_start_key] = key
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Provide a key condition expression for your query using a substitution
|
75
|
+
# expression.
|
76
|
+
#
|
77
|
+
# @example Building a simple query with a key expression:
|
78
|
+
# # Example model class
|
79
|
+
# class ExampleTable
|
80
|
+
# include Aws::Record
|
81
|
+
# string_attr :uuid, hash_key: true
|
82
|
+
# integer_attr :id, range_key: true
|
83
|
+
# string_attr :body
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# q = ExampleTable.build_query.key_expr(
|
87
|
+
# ":uuid = ? AND :id > ?", "smpl-uuid", 100
|
88
|
+
# ).complete!
|
89
|
+
# q.to_a # You can use this like any other query result in aws-record
|
90
|
+
def key_expr(statement_str, *subs)
|
91
|
+
unless @operation == :query
|
92
|
+
raise ArgumentError.new("key_expr is only supported for queries.")
|
93
|
+
end
|
94
|
+
names = @params[:expression_attribute_names]
|
95
|
+
if names.nil?
|
96
|
+
@params[:expression_attribute_names] = {}
|
97
|
+
names = @params[:expression_attribute_names]
|
98
|
+
end
|
99
|
+
values = @params[:expression_attribute_values]
|
100
|
+
if values.nil?
|
101
|
+
@params[:expression_attribute_values] = {}
|
102
|
+
values = @params[:expression_attribute_values]
|
103
|
+
end
|
104
|
+
_key_pass(statement_str, names)
|
105
|
+
_apply_values(statement_str, subs, values)
|
106
|
+
@params[:key_condition_expression] = statement_str
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
# Provide a filter expression for your query or scan using a substitution
|
111
|
+
# expression.
|
112
|
+
#
|
113
|
+
# @example Building a simple scan:
|
114
|
+
# # Example model class
|
115
|
+
# class ExampleTable
|
116
|
+
# include Aws::Record
|
117
|
+
# string_attr :uuid, hash_key: true
|
118
|
+
# integer_attr :id, range_key: true
|
119
|
+
# string_attr :body
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# scan = ExampleTable.build_scan.filter_expr(
|
123
|
+
# "contains(:body, ?)",
|
124
|
+
# "bacon"
|
125
|
+
# ).complete!
|
126
|
+
#
|
127
|
+
def filter_expr(statement_str, *subs)
|
128
|
+
names = @params[:expression_attribute_names]
|
129
|
+
if names.nil?
|
130
|
+
@params[:expression_attribute_names] = {}
|
131
|
+
names = @params[:expression_attribute_names]
|
132
|
+
end
|
133
|
+
values = @params[:expression_attribute_values]
|
134
|
+
if values.nil?
|
135
|
+
@params[:expression_attribute_values] = {}
|
136
|
+
values = @params[:expression_attribute_values]
|
137
|
+
end
|
138
|
+
_key_pass(statement_str, names)
|
139
|
+
_apply_values(statement_str, subs, values)
|
140
|
+
@params[:filter_expression] = statement_str
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
# Allows you to define a projection expression for the values returned by
|
145
|
+
# a query or scan. See
|
146
|
+
# {https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html the Amazon DynamoDB Developer Guide}
|
147
|
+
# for more details on projection expressions. You can use the symbols from
|
148
|
+
# your aws-record model class in a projection expression. Keys are always
|
149
|
+
# retrieved.
|
150
|
+
#
|
151
|
+
# @example Scan with a projection expression:
|
152
|
+
# # Example model class
|
153
|
+
# class ExampleTable
|
154
|
+
# include Aws::Record
|
155
|
+
# string_attr :uuid, hash_key: true
|
156
|
+
# integer_attr :id, range_key: true
|
157
|
+
# string_attr :body
|
158
|
+
# map_attr :metadata
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# scan = ExampleTable.build_scan.projection_expr(
|
162
|
+
# ":body"
|
163
|
+
# ).complete!
|
164
|
+
def projection_expr(statement_str)
|
165
|
+
names = @params[:expression_attribute_names]
|
166
|
+
if names.nil?
|
167
|
+
@params[:expression_attribute_names] = {}
|
168
|
+
names = @params[:expression_attribute_names]
|
169
|
+
end
|
170
|
+
_key_pass(statement_str, names)
|
171
|
+
@params[:projection_expression] = statement_str
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
# Allows you to set a page size limit on each query or scan request.
|
176
|
+
def limit(size)
|
177
|
+
@params[:limit] = size
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
# You must call this method at the end of any query or scan you build.
|
182
|
+
#
|
183
|
+
# @return [Aws::Record::ItemCollection] The item collection lazy
|
184
|
+
# enumerable.
|
185
|
+
def complete!
|
186
|
+
@model.send(@operation, @params)
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
def _key_pass(statement, names)
|
191
|
+
statement.gsub!(/:(\w+)/) do |match|
|
192
|
+
key = match.gsub!(':','').to_sym
|
193
|
+
key_name = @model.attributes.storage_name_for(key)
|
194
|
+
if key_name
|
195
|
+
sub_name = _next_name
|
196
|
+
raise "Substitution collision!" if names[sub_name]
|
197
|
+
names[sub_name] = key_name
|
198
|
+
sub_name
|
199
|
+
else
|
200
|
+
raise "No such key #{key}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def _apply_values(statement, subs, values)
|
206
|
+
count = 0
|
207
|
+
statement.gsub!(/[?]/) do |match|
|
208
|
+
sub_value = _next_value
|
209
|
+
raise "Substitution collision!" if values[sub_value]
|
210
|
+
values[sub_value] = subs[count]
|
211
|
+
count += 1
|
212
|
+
sub_value
|
213
|
+
end
|
214
|
+
unless count == subs.size
|
215
|
+
raise "Expected #{count} values in the substitution set, but found #{subs.size}"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def _next_name
|
220
|
+
ret = "#" + @next_name
|
221
|
+
@next_name.next!
|
222
|
+
ret
|
223
|
+
end
|
224
|
+
|
225
|
+
def _next_value
|
226
|
+
ret = ":" + @next_value
|
227
|
+
@next_value.next!
|
228
|
+
ret
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -102,6 +102,54 @@ module Aws
|
|
102
102
|
scan_opts = opts.merge(table_name: table_name)
|
103
103
|
ItemCollection.new(:scan, scan_opts, self, dynamodb_client)
|
104
104
|
end
|
105
|
+
|
106
|
+
# This method allows you to build a query using the {Aws::Record::BuildableSearch} DSL.
|
107
|
+
#
|
108
|
+
# @example Building a simple query:
|
109
|
+
# # Example model class
|
110
|
+
# class ExampleTable
|
111
|
+
# include Aws::Record
|
112
|
+
# string_attr :uuid, hash_key: true
|
113
|
+
# integer_attr :id, range_key: true
|
114
|
+
# string_attr :body
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# q = ExampleTable.build_query.key_expr(
|
118
|
+
# ":uuid = ? AND :id > ?", "smpl-uuid", 100
|
119
|
+
# ).scan_ascending(false).complete!
|
120
|
+
# q.to_a # You can use this like any other query result in aws-record
|
121
|
+
def build_query
|
122
|
+
BuildableSearch.new(
|
123
|
+
operation: :query,
|
124
|
+
model: self
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
# This method allows you to build a scan using the {Aws::Record::BuildableSearch} DSL.
|
129
|
+
#
|
130
|
+
# @example Building a simple scan:
|
131
|
+
# # Example model class
|
132
|
+
# class ExampleTable
|
133
|
+
# include Aws::Record
|
134
|
+
# string_attr :uuid, hash_key: true
|
135
|
+
# integer_attr :id, range_key: true
|
136
|
+
# string_attr :body
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# segment_2_scan = ExampleTable.build_scan.filter_expr(
|
140
|
+
# "contains(:body, ?)",
|
141
|
+
# "bacon"
|
142
|
+
# ).scan_ascending(false).parallel_scan(
|
143
|
+
# total_segments: 5,
|
144
|
+
# segment: 2
|
145
|
+
# ).complete!
|
146
|
+
# segment_2_scan.to_a # You can use this like any other query result in aws-record
|
147
|
+
def build_scan
|
148
|
+
BuildableSearch.new(
|
149
|
+
operation: :scan,
|
150
|
+
model: self
|
151
|
+
)
|
152
|
+
end
|
105
153
|
end
|
106
154
|
|
107
155
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amazon Web Services
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- lib/aws-record/record.rb
|
36
36
|
- lib/aws-record/record/attribute.rb
|
37
37
|
- lib/aws-record/record/attributes.rb
|
38
|
+
- lib/aws-record/record/buildable_search.rb
|
38
39
|
- lib/aws-record/record/dirty_tracking.rb
|
39
40
|
- lib/aws-record/record/errors.rb
|
40
41
|
- lib/aws-record/record/item_collection.rb
|
@@ -80,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
81
|
version: '0'
|
81
82
|
requirements: []
|
82
83
|
rubyforge_project:
|
83
|
-
rubygems_version: 2.
|
84
|
+
rubygems_version: 2.5.2
|
84
85
|
signing_key:
|
85
86
|
specification_version: 4
|
86
87
|
summary: AWS Record library for Amazon DynamoDB
|