dynamoid 3.9.0 → 3.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -6
- data/README.md +202 -25
- data/dynamoid.gemspec +5 -6
- data/lib/dynamoid/adapter.rb +19 -13
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +2 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +113 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +21 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +40 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +46 -61
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +34 -28
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/transact.rb +31 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +95 -66
- data/lib/dynamoid/associations/belongs_to.rb +6 -6
- data/lib/dynamoid/associations.rb +1 -1
- data/lib/dynamoid/components.rb +1 -0
- data/lib/dynamoid/config/options.rb +12 -12
- data/lib/dynamoid/config.rb +3 -0
- data/lib/dynamoid/criteria/chain.rb +149 -142
- data/lib/dynamoid/criteria/key_fields_detector.rb +6 -7
- data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
- data/lib/dynamoid/criteria/where_conditions.rb +36 -0
- data/lib/dynamoid/dirty.rb +87 -12
- data/lib/dynamoid/document.rb +1 -1
- data/lib/dynamoid/dumping.rb +38 -16
- data/lib/dynamoid/errors.rb +14 -2
- data/lib/dynamoid/fields/declare.rb +6 -6
- data/lib/dynamoid/fields.rb +6 -8
- data/lib/dynamoid/finders.rb +23 -32
- data/lib/dynamoid/indexes.rb +6 -7
- data/lib/dynamoid/loadable.rb +3 -2
- data/lib/dynamoid/persistence/inc.rb +6 -7
- data/lib/dynamoid/persistence/item_updater_with_casting_and_dumping.rb +36 -0
- data/lib/dynamoid/persistence/item_updater_with_dumping.rb +33 -0
- data/lib/dynamoid/persistence/save.rb +17 -18
- data/lib/dynamoid/persistence/update_fields.rb +7 -5
- data/lib/dynamoid/persistence/update_validations.rb +1 -1
- data/lib/dynamoid/persistence/upsert.rb +5 -4
- data/lib/dynamoid/persistence.rb +77 -21
- data/lib/dynamoid/transaction_write/base.rb +47 -0
- data/lib/dynamoid/transaction_write/create.rb +49 -0
- data/lib/dynamoid/transaction_write/delete_with_instance.rb +60 -0
- data/lib/dynamoid/transaction_write/delete_with_primary_key.rb +59 -0
- data/lib/dynamoid/transaction_write/destroy.rb +79 -0
- data/lib/dynamoid/transaction_write/save.rb +164 -0
- data/lib/dynamoid/transaction_write/update_attributes.rb +46 -0
- data/lib/dynamoid/transaction_write/update_fields.rb +102 -0
- data/lib/dynamoid/transaction_write/upsert.rb +96 -0
- data/lib/dynamoid/transaction_write.rb +464 -0
- data/lib/dynamoid/type_casting.rb +18 -15
- data/lib/dynamoid/undumping.rb +14 -3
- data/lib/dynamoid/validations.rb +1 -1
- data/lib/dynamoid/version.rb +1 -1
- data/lib/dynamoid.rb +7 -0
- metadata +30 -16
- data/lib/dynamoid/criteria/ignored_conditions_detector.rb +0 -41
- data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -40
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
# @private
|
5
|
+
module AdapterPlugin
|
6
|
+
class AwsSdkV3
|
7
|
+
class ProjectionExpressionConvertor
|
8
|
+
attr_reader :expression, :name_placeholders
|
9
|
+
|
10
|
+
def initialize(names, name_placeholders, name_placeholder_sequence)
|
11
|
+
@names = names
|
12
|
+
@name_placeholders = name_placeholders.dup
|
13
|
+
@name_placeholder_sequence = name_placeholder_sequence
|
14
|
+
|
15
|
+
build
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build
|
21
|
+
return if @names.nil? || @names.empty?
|
22
|
+
|
23
|
+
clauses = @names.map do |name|
|
24
|
+
# replace attribute names with placeholders unconditionally to support
|
25
|
+
# - special characters (e.g. '.', ':', and '#') and
|
26
|
+
# - leading '_'
|
27
|
+
# See
|
28
|
+
# - https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.NamingRules
|
29
|
+
# - https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html#Expressions.ExpressionAttributeNames.AttributeNamesContainingSpecialCharacters
|
30
|
+
placeholder = @name_placeholder_sequence.call
|
31
|
+
@name_placeholders[placeholder] = name
|
32
|
+
placeholder
|
33
|
+
end
|
34
|
+
|
35
|
+
@expression = clauses.join(' , ')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative 'middleware/backoff'
|
4
4
|
require_relative 'middleware/limit'
|
5
5
|
require_relative 'middleware/start_key'
|
6
|
+
require_relative 'filter_expression_convertor'
|
7
|
+
require_relative 'projection_expression_convertor'
|
6
8
|
|
7
9
|
module Dynamoid
|
8
10
|
# @private
|
@@ -10,20 +12,19 @@ module Dynamoid
|
|
10
12
|
class AwsSdkV3
|
11
13
|
class Query
|
12
14
|
OPTIONS_KEYS = %i[
|
13
|
-
|
14
|
-
|
15
|
-
project
|
15
|
+
consistent_read scan_index_forward select index_name batch_size
|
16
|
+
exclusive_start_key record_limit scan_limit project
|
16
17
|
].freeze
|
17
18
|
|
18
19
|
attr_reader :client, :table, :options, :conditions
|
19
20
|
|
20
|
-
def initialize(client, table,
|
21
|
+
def initialize(client, table, key_conditions, non_key_conditions, options)
|
21
22
|
@client = client
|
22
23
|
@table = table
|
23
24
|
|
24
|
-
|
25
|
-
@
|
26
|
-
@
|
25
|
+
@key_conditions = key_conditions
|
26
|
+
@non_key_conditions = non_key_conditions
|
27
|
+
@options = options.slice(*OPTIONS_KEYS)
|
27
28
|
end
|
28
29
|
|
29
30
|
def call
|
@@ -53,6 +54,37 @@ module Dynamoid
|
|
53
54
|
private
|
54
55
|
|
55
56
|
def build_request
|
57
|
+
# expressions
|
58
|
+
name_placeholder = +'#_a0'
|
59
|
+
value_placeholder = +':_a0'
|
60
|
+
|
61
|
+
name_placeholder_sequence = -> { name_placeholder.next!.dup }
|
62
|
+
value_placeholder_sequence = -> { value_placeholder.next!.dup }
|
63
|
+
|
64
|
+
name_placeholders = {}
|
65
|
+
value_placeholders = {}
|
66
|
+
|
67
|
+
# Deal with various limits and batching
|
68
|
+
batch_size = options[:batch_size]
|
69
|
+
limit = [record_limit, scan_limit, batch_size].compact.min
|
70
|
+
|
71
|
+
# key condition expression
|
72
|
+
convertor = FilterExpressionConvertor.new([@key_conditions], name_placeholders, value_placeholders, name_placeholder_sequence, value_placeholder_sequence)
|
73
|
+
key_condition_expression = convertor.expression
|
74
|
+
value_placeholders = convertor.value_placeholders
|
75
|
+
name_placeholders = convertor.name_placeholders
|
76
|
+
|
77
|
+
# filter expression
|
78
|
+
convertor = FilterExpressionConvertor.new(@non_key_conditions, name_placeholders, value_placeholders, name_placeholder_sequence, value_placeholder_sequence)
|
79
|
+
filter_expression = convertor.expression
|
80
|
+
value_placeholders = convertor.value_placeholders
|
81
|
+
name_placeholders = convertor.name_placeholders
|
82
|
+
|
83
|
+
# projection expression
|
84
|
+
convertor = ProjectionExpressionConvertor.new(options[:project], name_placeholders, name_placeholder_sequence)
|
85
|
+
projection_expression = convertor.expression
|
86
|
+
name_placeholders = convertor.name_placeholders
|
87
|
+
|
56
88
|
request = options.slice(
|
57
89
|
:consistent_read,
|
58
90
|
:scan_index_forward,
|
@@ -61,15 +93,13 @@ module Dynamoid
|
|
61
93
|
:exclusive_start_key
|
62
94
|
).compact
|
63
95
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
request[:
|
69
|
-
request[:
|
70
|
-
request[:
|
71
|
-
request[:query_filter] = query_filter
|
72
|
-
request[:attributes_to_get] = attributes_to_get
|
96
|
+
request[:table_name] = table.name
|
97
|
+
request[:limit] = limit if limit
|
98
|
+
request[:key_condition_expression] = key_condition_expression if key_condition_expression.present?
|
99
|
+
request[:filter_expression] = filter_expression if filter_expression.present?
|
100
|
+
request[:expression_attribute_values] = value_placeholders if value_placeholders.present?
|
101
|
+
request[:expression_attribute_names] = name_placeholders if name_placeholders.present?
|
102
|
+
request[:projection_expression] = projection_expression if projection_expression.present?
|
73
103
|
|
74
104
|
request
|
75
105
|
end
|
@@ -81,51 +111,6 @@ module Dynamoid
|
|
81
111
|
def scan_limit
|
82
112
|
options[:scan_limit]
|
83
113
|
end
|
84
|
-
|
85
|
-
def hash_key_name
|
86
|
-
(options[:hash_key] || table.hash_key)
|
87
|
-
end
|
88
|
-
|
89
|
-
def range_key_name
|
90
|
-
(options[:range_key] || table.range_key)
|
91
|
-
end
|
92
|
-
|
93
|
-
def key_conditions
|
94
|
-
result = {
|
95
|
-
hash_key_name => {
|
96
|
-
comparison_operator: AwsSdkV3::EQ,
|
97
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::EQ, options[:hash_value].freeze)
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
conditions.slice(*AwsSdkV3::RANGE_MAP.keys).each do |k, _v|
|
102
|
-
op = AwsSdkV3::RANGE_MAP[k]
|
103
|
-
|
104
|
-
result[range_key_name] = {
|
105
|
-
comparison_operator: op,
|
106
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(op, conditions[k].freeze)
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
|
-
result
|
111
|
-
end
|
112
|
-
|
113
|
-
def query_filter
|
114
|
-
conditions.except(*AwsSdkV3::RANGE_MAP.keys).reduce({}) do |result, (attr, cond)|
|
115
|
-
condition = {
|
116
|
-
comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
|
117
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
|
118
|
-
}
|
119
|
-
result[attr] = condition
|
120
|
-
result
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def attributes_to_get
|
125
|
-
return if options[:project].nil?
|
126
|
-
|
127
|
-
options[:project].map(&:to_s)
|
128
|
-
end
|
129
114
|
end
|
130
115
|
end
|
131
116
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative 'middleware/backoff'
|
4
4
|
require_relative 'middleware/limit'
|
5
5
|
require_relative 'middleware/start_key'
|
6
|
+
require_relative 'filter_expression_convertor'
|
7
|
+
require_relative 'projection_expression_convertor'
|
6
8
|
|
7
9
|
module Dynamoid
|
8
10
|
# @private
|
@@ -11,7 +13,7 @@ module Dynamoid
|
|
11
13
|
class Scan
|
12
14
|
attr_reader :client, :table, :conditions, :options
|
13
15
|
|
14
|
-
def initialize(client, table, conditions =
|
16
|
+
def initialize(client, table, conditions = [], options = {})
|
15
17
|
@client = client
|
16
18
|
@table = table
|
17
19
|
@conditions = conditions
|
@@ -45,6 +47,31 @@ module Dynamoid
|
|
45
47
|
private
|
46
48
|
|
47
49
|
def build_request
|
50
|
+
# expressions
|
51
|
+
name_placeholder = +'#_a0'
|
52
|
+
value_placeholder = +':_a0'
|
53
|
+
|
54
|
+
name_placeholder_sequence = -> { name_placeholder.next!.dup }
|
55
|
+
value_placeholder_sequence = -> { value_placeholder.next!.dup }
|
56
|
+
|
57
|
+
name_placeholders = {}
|
58
|
+
value_placeholders = {}
|
59
|
+
|
60
|
+
# Deal with various limits and batching
|
61
|
+
batch_size = options[:batch_size]
|
62
|
+
limit = [record_limit, scan_limit, batch_size].compact.min
|
63
|
+
|
64
|
+
# filter expression
|
65
|
+
convertor = FilterExpressionConvertor.new(conditions, name_placeholders, value_placeholders, name_placeholder_sequence, value_placeholder_sequence)
|
66
|
+
filter_expression = convertor.expression
|
67
|
+
value_placeholders = convertor.value_placeholders
|
68
|
+
name_placeholders = convertor.name_placeholders
|
69
|
+
|
70
|
+
# projection expression
|
71
|
+
convertor = ProjectionExpressionConvertor.new(options[:project], name_placeholders, name_placeholder_sequence)
|
72
|
+
projection_expression = convertor.expression
|
73
|
+
name_placeholders = convertor.name_placeholders
|
74
|
+
|
48
75
|
request = options.slice(
|
49
76
|
:consistent_read,
|
50
77
|
:exclusive_start_key,
|
@@ -52,14 +79,12 @@ module Dynamoid
|
|
52
79
|
:index_name
|
53
80
|
).compact
|
54
81
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
request[:
|
60
|
-
request[:
|
61
|
-
request[:scan_filter] = scan_filter
|
62
|
-
request[:attributes_to_get] = attributes_to_get
|
82
|
+
request[:table_name] = table.name
|
83
|
+
request[:limit] = limit if limit
|
84
|
+
request[:filter_expression] = filter_expression if filter_expression.present?
|
85
|
+
request[:expression_attribute_values] = value_placeholders if value_placeholders.present?
|
86
|
+
request[:expression_attribute_names] = name_placeholders if name_placeholders.present?
|
87
|
+
request[:projection_expression] = projection_expression if projection_expression.present?
|
63
88
|
|
64
89
|
request
|
65
90
|
end
|
@@ -71,25 +96,6 @@ module Dynamoid
|
|
71
96
|
def scan_limit
|
72
97
|
options[:scan_limit]
|
73
98
|
end
|
74
|
-
|
75
|
-
def scan_filter
|
76
|
-
conditions.reduce({}) do |result, (attr, cond)|
|
77
|
-
condition = {
|
78
|
-
comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
|
79
|
-
attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
|
80
|
-
}
|
81
|
-
# nil means operator doesn't require attribute value list
|
82
|
-
conditions.delete(:attribute_value_list) if conditions[:attribute_value_list].nil?
|
83
|
-
result[attr] = condition
|
84
|
-
result
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def attributes_to_get
|
89
|
-
return if options[:project].nil?
|
90
|
-
|
91
|
-
options[:project].map(&:to_s)
|
92
|
-
end
|
93
99
|
end
|
94
100
|
end
|
95
101
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Prepare all the actions of the transaction for sending to the AWS SDK.
|
4
|
+
module Dynamoid
|
5
|
+
module AdapterPlugin
|
6
|
+
class AwsSdkV3
|
7
|
+
class Transact
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Perform all of the item actions in a single transaction.
|
15
|
+
#
|
16
|
+
# @param [Array] items of type Dynamoid::Transaction::Action or
|
17
|
+
# any other object whose to_h is a transact_item hash
|
18
|
+
#
|
19
|
+
def transact_write_items(items)
|
20
|
+
transact_items = items.map(&:to_h)
|
21
|
+
params = {
|
22
|
+
transact_items: transact_items,
|
23
|
+
return_consumed_capacity: 'TOTAL',
|
24
|
+
return_item_collection_metrics: 'SIZE'
|
25
|
+
}
|
26
|
+
client.transact_write_items(params) # returns this
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -8,13 +8,14 @@ require_relative 'aws_sdk_v3/batch_get_item'
|
|
8
8
|
require_relative 'aws_sdk_v3/item_updater'
|
9
9
|
require_relative 'aws_sdk_v3/table'
|
10
10
|
require_relative 'aws_sdk_v3/until_past_table_status'
|
11
|
+
require_relative 'aws_sdk_v3/transact'
|
11
12
|
|
12
13
|
module Dynamoid
|
13
14
|
# @private
|
14
15
|
module AdapterPlugin
|
15
16
|
# The AwsSdkV3 adapter provides support for the aws-sdk version 2 for ruby.
|
16
17
|
|
17
|
-
#
|
18
|
+
# NOTE: Don't use keyword arguments in public methods as far as method
|
18
19
|
# calls on adapter are delegated to the plugin.
|
19
20
|
#
|
20
21
|
# There are breaking changes in Ruby related to delegating keyword
|
@@ -24,31 +25,6 @@ module Dynamoid
|
|
24
25
|
|
25
26
|
class AwsSdkV3
|
26
27
|
EQ = 'EQ'
|
27
|
-
RANGE_MAP = {
|
28
|
-
range_greater_than: 'GT',
|
29
|
-
range_less_than: 'LT',
|
30
|
-
range_gte: 'GE',
|
31
|
-
range_lte: 'LE',
|
32
|
-
range_begins_with: 'BEGINS_WITH',
|
33
|
-
range_between: 'BETWEEN',
|
34
|
-
range_eq: 'EQ'
|
35
|
-
}.freeze
|
36
|
-
|
37
|
-
FIELD_MAP = {
|
38
|
-
eq: 'EQ',
|
39
|
-
ne: 'NE',
|
40
|
-
gt: 'GT',
|
41
|
-
lt: 'LT',
|
42
|
-
gte: 'GE',
|
43
|
-
lte: 'LE',
|
44
|
-
begins_with: 'BEGINS_WITH',
|
45
|
-
between: 'BETWEEN',
|
46
|
-
in: 'IN',
|
47
|
-
contains: 'CONTAINS',
|
48
|
-
not_contains: 'NOT_CONTAINS',
|
49
|
-
null: 'NULL',
|
50
|
-
not_null: 'NOT_NULL',
|
51
|
-
}.freeze
|
52
28
|
HASH_KEY = 'HASH'
|
53
29
|
RANGE_KEY = 'RANGE'
|
54
30
|
STRING_TYPE = 'S'
|
@@ -70,26 +46,72 @@ module Dynamoid
|
|
70
46
|
|
71
47
|
CONNECTION_CONFIG_OPTIONS = %i[endpoint region http_continue_timeout http_idle_timeout http_open_timeout http_read_timeout].freeze
|
72
48
|
|
73
|
-
|
49
|
+
# See https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
|
50
|
+
# rubocop:disable Metrics/CollectionLiteralLength
|
51
|
+
RESERVED_WORDS = Set.new(
|
52
|
+
%i[
|
53
|
+
ABORT ABSOLUTE ACTION ADD AFTER AGENT AGGREGATE ALL ALLOCATE ALTER ANALYZE
|
54
|
+
AND ANY ARCHIVE ARE ARRAY AS ASC ASCII ASENSITIVE ASSERTION ASYMMETRIC AT
|
55
|
+
ATOMIC ATTACH ATTRIBUTE AUTH AUTHORIZATION AUTHORIZE AUTO AVG BACK BACKUP
|
56
|
+
BASE BATCH BEFORE BEGIN BETWEEN BIGINT BINARY BIT BLOB BLOCK BOOLEAN BOTH
|
57
|
+
BREADTH BUCKET BULK BY BYTE CALL CALLED CALLING CAPACITY CASCADE CASCADED
|
58
|
+
CASE CAST CATALOG CHAR CHARACTER CHECK CLASS CLOB CLOSE CLUSTER CLUSTERED
|
59
|
+
CLUSTERING CLUSTERS COALESCE COLLATE COLLATION COLLECTION COLUMN COLUMNS
|
60
|
+
COMBINE COMMENT COMMIT COMPACT COMPILE COMPRESS CONDITION CONFLICT CONNECT
|
61
|
+
CONNECTION CONSISTENCY CONSISTENT CONSTRAINT CONSTRAINTS CONSTRUCTOR
|
62
|
+
CONSUMED CONTINUE CONVERT COPY CORRESPONDING COUNT COUNTER CREATE CROSS
|
63
|
+
CUBE CURRENT CURSOR CYCLE DATA DATABASE DATE DATETIME DAY DEALLOCATE DEC
|
64
|
+
DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DEFINE DEFINED DEFINITION
|
65
|
+
DELETE DELIMITED DEPTH DEREF DESC DESCRIBE DESCRIPTOR DETACH DETERMINISTIC
|
66
|
+
DIAGNOSTICS DIRECTORIES DISABLE DISCONNECT DISTINCT DISTRIBUTE DO DOMAIN
|
67
|
+
DOUBLE DROP DUMP DURATION DYNAMIC EACH ELEMENT ELSE ELSEIF EMPTY ENABLE
|
68
|
+
END EQUAL EQUALS ERROR ESCAPE ESCAPED EVAL EVALUATE EXCEEDED EXCEPT
|
69
|
+
EXCEPTION EXCEPTIONS EXCLUSIVE EXEC EXECUTE EXISTS EXIT EXPLAIN EXPLODE
|
70
|
+
EXPORT EXPRESSION EXTENDED EXTERNAL EXTRACT FAIL FALSE FAMILY FETCH FIELDS
|
71
|
+
FILE FILTER FILTERING FINAL FINISH FIRST FIXED FLATTERN FLOAT FOR FORCE
|
72
|
+
FOREIGN FORMAT FORWARD FOUND FREE FROM FULL FUNCTION FUNCTIONS GENERAL
|
73
|
+
GENERATE GET GLOB GLOBAL GO GOTO GRANT GREATER GROUP GROUPING HANDLER HASH
|
74
|
+
HAVE HAVING HEAP HIDDEN HOLD HOUR IDENTIFIED IDENTITY IF IGNORE IMMEDIATE
|
75
|
+
IMPORT IN INCLUDING INCLUSIVE INCREMENT INCREMENTAL INDEX INDEXED INDEXES
|
76
|
+
INDICATOR INFINITE INITIALLY INLINE INNER INNTER INOUT INPUT INSENSITIVE
|
77
|
+
INSERT INSTEAD INT INTEGER INTERSECT INTERVAL INTO INVALIDATE IS ISOLATION
|
78
|
+
ITEM ITEMS ITERATE JOIN KEY KEYS LAG LANGUAGE LARGE LAST LATERAL LEAD
|
79
|
+
LEADING LEAVE LEFT LENGTH LESS LEVEL LIKE LIMIT LIMITED LINES LIST LOAD
|
80
|
+
LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCATOR LOCK LOCKS LOG LOGED LONG
|
81
|
+
LOOP LOWER MAP MATCH MATERIALIZED MAX MAXLEN MEMBER MERGE METHOD METRICS
|
82
|
+
MIN MINUS MINUTE MISSING MOD MODE MODIFIES MODIFY MODULE MONTH MULTI
|
83
|
+
MULTISET NAME NAMES NATIONAL NATURAL NCHAR NCLOB NEW NEXT NO NONE NOT NULL
|
84
|
+
NULLIF NUMBER NUMERIC OBJECT OF OFFLINE OFFSET OLD ON ONLINE ONLY OPAQUE
|
85
|
+
OPEN OPERATOR OPTION OR ORDER ORDINALITY OTHER OTHERS OUT OUTER OUTPUT
|
86
|
+
OVER OVERLAPS OVERRIDE OWNER PAD PARALLEL PARAMETER PARAMETERS PARTIAL
|
87
|
+
PARTITION PARTITIONED PARTITIONS PATH PERCENT PERCENTILE PERMISSION
|
88
|
+
PERMISSIONS PIPE PIPELINED PLAN POOL POSITION PRECISION PREPARE PRESERVE
|
89
|
+
PRIMARY PRIOR PRIVATE PRIVILEGES PROCEDURE PROCESSED PROJECT PROJECTION
|
90
|
+
PROPERTY PROVISIONING PUBLIC PUT QUERY QUIT QUORUM RAISE RANDOM RANGE RANK
|
91
|
+
RAW READ READS REAL REBUILD RECORD RECURSIVE REDUCE REF REFERENCE
|
92
|
+
REFERENCES REFERENCING REGEXP REGION REINDEX RELATIVE RELEASE REMAINDER
|
93
|
+
RENAME REPEAT REPLACE REQUEST RESET RESIGNAL RESOURCE RESPONSE RESTORE
|
94
|
+
RESTRICT RESULT RETURN RETURNING RETURNS REVERSE REVOKE RIGHT ROLE ROLES
|
95
|
+
ROLLBACK ROLLUP ROUTINE ROW ROWS RULE RULES SAMPLE SATISFIES SAVE SAVEPOINT
|
96
|
+
SCAN SCHEMA SCOPE SCROLL SEARCH SECOND SECTION SEGMENT SEGMENTS SELECT SELF
|
97
|
+
SEMI SENSITIVE SEPARATE SEQUENCE SERIALIZABLE SESSION SET SETS SHARD SHARE
|
98
|
+
SHARED SHORT SHOW SIGNAL SIMILAR SIZE SKEWED SMALLINT SNAPSHOT SOME SOURCE
|
99
|
+
SPACE SPACES SPARSE SPECIFIC SPECIFICTYPE SPLIT SQL SQLCODE SQLERROR
|
100
|
+
SQLEXCEPTION SQLSTATE SQLWARNING START STATE STATIC STATUS STORAGE STORE
|
101
|
+
STORED STREAM STRING STRUCT STYLE SUB SUBMULTISET SUBPARTITION SUBSTRING
|
102
|
+
SUBTYPE SUM SUPER SYMMETRIC SYNONYM SYSTEM TABLE TABLESAMPLE TEMP TEMPORARY
|
103
|
+
TERMINATED TEXT THAN THEN THROUGHPUT TIME TIMESTAMP TIMEZONE TINYINT TO
|
104
|
+
TOKEN TOTAL TOUCH TRAILING TRANSACTION TRANSFORM TRANSLATE TRANSLATION
|
105
|
+
TREAT TRIGGER TRIM TRUE TRUNCATE TTL TUPLE TYPE UNDER UNDO UNION UNIQUE UNIT
|
106
|
+
UNKNOWN UNLOGGED UNNEST UNPROCESSED UNSIGNED UNTIL UPDATE UPPER URL USAGE
|
107
|
+
USE USER USERS USING UUID VACUUM VALUE VALUED VALUES VARCHAR VARIABLE
|
108
|
+
VARIANCE VARINT VARYING VIEW VIEWS VIRTUAL VOID WAIT WHEN WHENEVER WHERE
|
109
|
+
WHILE WINDOW WITH WITHIN WITHOUT WORK WRAPPED WRITE YEAR ZONE
|
110
|
+
]
|
111
|
+
).freeze
|
112
|
+
# rubocop:enable Metrics/CollectionLiteralLength
|
74
113
|
|
75
|
-
|
76
|
-
# Is used in ScanFilter and QueryFilter
|
77
|
-
# https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html
|
78
|
-
# @param [String] operator value of RANGE_MAP or FIELD_MAP hash, e.g. "EQ", "LT" etc
|
79
|
-
# @param [Object] value scalar value or array/set
|
80
|
-
def self.attribute_value_list(operator, value)
|
81
|
-
# For BETWEEN and IN operators we should keep value as is (it should be already an array)
|
82
|
-
# NULL and NOT_NULL require absence of attribute list
|
83
|
-
# For all the other operators we wrap the value with array
|
84
|
-
# https://docs.aws.amazon.com/en_us/amazondynamodb/latest/developerguide/LegacyConditionalParameters.Conditions.html
|
85
|
-
if %w[BETWEEN IN].include?(operator)
|
86
|
-
[value].flatten
|
87
|
-
elsif %w[NULL NOT_NULL].include?(operator)
|
88
|
-
nil
|
89
|
-
else
|
90
|
-
[value]
|
91
|
-
end
|
92
|
-
end
|
114
|
+
attr_reader :table_cache
|
93
115
|
|
94
116
|
# Establish the connection to DynamoDB.
|
95
117
|
#
|
@@ -268,6 +290,10 @@ module Dynamoid
|
|
268
290
|
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
269
291
|
end
|
270
292
|
|
293
|
+
def transact_write_items(items)
|
294
|
+
Transact.new(client).transact_write_items(items)
|
295
|
+
end
|
296
|
+
|
271
297
|
# Create a table on DynamoDB. This usually takes a long time to complete.
|
272
298
|
#
|
273
299
|
# @param [String] table_name the name of the table to create
|
@@ -470,25 +496,32 @@ module Dynamoid
|
|
470
496
|
# only really useful for range queries, since it can only find by one hash key at once. Only provide
|
471
497
|
# one range key to the hash.
|
472
498
|
#
|
499
|
+
# Dynamoid.adapter.query('users', { id: [[:eq, '1']], age: [[:between, [10, 30]]] }, { batch_size: 1000 })
|
500
|
+
#
|
473
501
|
# @param [String] table_name the name of the table
|
474
|
-
# @param [
|
475
|
-
# @
|
476
|
-
# @
|
477
|
-
# @option options [
|
478
|
-
# @option options [
|
479
|
-
# @option options [
|
480
|
-
# @option options [
|
502
|
+
# @param [Array[Array]] key_conditions conditions for the primary key attributes
|
503
|
+
# @param [Array[Array]] non_key_conditions (optional) conditions for non-primary key attributes
|
504
|
+
# @param [Hash] options (optional) the options to query the table with
|
505
|
+
# @option options [Boolean] :consistent_read You can set the ConsistentRead parameter to true and obtain a strongly consistent result
|
506
|
+
# @option options [Boolean] :scan_index_forward Specifies the order for index traversal: If true (default), the traversal is performed in ascending order; if false, the traversal is performed in descending order.
|
507
|
+
# @option options [Symbop] :select The attributes to be returned in the result (one of ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, ...)
|
508
|
+
# @option options [Symbol] :index_name The name of an index to query. This index can be any local secondary index or global secondary index on the table.
|
509
|
+
# @option options [Hash] :exclusive_start_key The primary key of the first item that this operation will evaluate.
|
510
|
+
# @option options [Integer] :batch_size The number of items to lazily load one by one
|
511
|
+
# @option options [Integer] :record_limit The maximum number of items to return (not necessarily the number of evaluated items)
|
512
|
+
# @option options [Integer] :scan_limit The maximum number of items to evaluate (not necessarily the number of matching items)
|
513
|
+
# @option options [Array[Symbol]] :project The attributes to retrieve from the table
|
481
514
|
#
|
482
515
|
# @return [Enumerable] matching items
|
483
516
|
#
|
484
517
|
# @since 1.0.0
|
485
518
|
#
|
486
519
|
# @todo Provide support for various other options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#query-instance_method
|
487
|
-
def query(table_name, options = {})
|
520
|
+
def query(table_name, key_conditions, non_key_conditions = [], options = {})
|
488
521
|
Enumerator.new do |yielder|
|
489
522
|
table = describe_table(table_name)
|
490
523
|
|
491
|
-
Query.new(client, table, options).call.each do |page|
|
524
|
+
Query.new(client, table, key_conditions, non_key_conditions, options).call.each do |page|
|
492
525
|
yielder.yield(
|
493
526
|
page.items.map { |item| item_to_hash(item) },
|
494
527
|
last_evaluated_key: page.last_evaluated_key
|
@@ -497,11 +530,11 @@ module Dynamoid
|
|
497
530
|
end
|
498
531
|
end
|
499
532
|
|
500
|
-
def query_count(table_name,
|
533
|
+
def query_count(table_name, key_conditions, non_key_conditions, options)
|
501
534
|
table = describe_table(table_name)
|
502
535
|
options[:select] = 'COUNT'
|
503
536
|
|
504
|
-
Query.new(client, table, options).call
|
537
|
+
Query.new(client, table, key_conditions, non_key_conditions, options).call
|
505
538
|
.map(&:count)
|
506
539
|
.reduce(:+)
|
507
540
|
end
|
@@ -517,7 +550,7 @@ module Dynamoid
|
|
517
550
|
# @since 1.0.0
|
518
551
|
#
|
519
552
|
# @todo: Provide support for various options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#scan-instance_method
|
520
|
-
def scan(table_name, conditions =
|
553
|
+
def scan(table_name, conditions = [], options = {})
|
521
554
|
Enumerator.new do |yielder|
|
522
555
|
table = describe_table(table_name)
|
523
556
|
|
@@ -530,7 +563,7 @@ module Dynamoid
|
|
530
563
|
end
|
531
564
|
end
|
532
565
|
|
533
|
-
def scan_count(table_name, conditions =
|
566
|
+
def scan_count(table_name, conditions = [], options = {})
|
534
567
|
table = describe_table(table_name)
|
535
568
|
options[:select] = 'COUNT'
|
536
569
|
|
@@ -558,7 +591,7 @@ module Dynamoid
|
|
558
591
|
end
|
559
592
|
|
560
593
|
def count(table_name)
|
561
|
-
describe_table(table_name, true).item_count
|
594
|
+
describe_table(table_name, reload: true).item_count
|
562
595
|
end
|
563
596
|
|
564
597
|
# Run PartiQL query.
|
@@ -605,10 +638,7 @@ module Dynamoid
|
|
605
638
|
conditions.delete(:unless_exists).try(:each) do |col|
|
606
639
|
expected[col.to_s][:exists] = false
|
607
640
|
end
|
608
|
-
|
609
|
-
expected[col.to_s][:exists] = true
|
610
|
-
expected[col.to_s][:value] = val
|
611
|
-
end
|
641
|
+
|
612
642
|
conditions.delete(:if).try(:each) do |col, val|
|
613
643
|
expected[col.to_s][:value] = val
|
614
644
|
end
|
@@ -619,7 +649,7 @@ module Dynamoid
|
|
619
649
|
#
|
620
650
|
# New, semi-arbitrary API to get data on the table
|
621
651
|
#
|
622
|
-
def describe_table(table_name, reload
|
652
|
+
def describe_table(table_name, reload: false)
|
623
653
|
(!reload && table_cache[table_name]) || begin
|
624
654
|
table_cache[table_name] = Table.new(client.describe_table(table_name: table_name).data)
|
625
655
|
end
|
@@ -637,8 +667,7 @@ module Dynamoid
|
|
637
667
|
store_attribute_with_nil_value = config_value.nil? ? false : !!config_value
|
638
668
|
|
639
669
|
attributes.reject do |_, v|
|
640
|
-
|
641
|
-
(!store_attribute_with_nil_value && v.nil?)
|
670
|
+
!store_attribute_with_nil_value && v.nil?
|
642
671
|
end.transform_values do |v|
|
643
672
|
v.is_a?(Hash) ? v.stringify_keys : v
|
644
673
|
end
|
@@ -39,14 +39,14 @@ module Dynamoid
|
|
39
39
|
#
|
40
40
|
# @since 0.2.0
|
41
41
|
def target_association
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
return has_many_key_name if target_class.associations[has_many_key_name][:type] == :has_many
|
42
|
+
name = options[:inverse_of] || source.class.to_s.underscore.pluralize.to_sym
|
43
|
+
if target_class.associations.dig(name, :type) == :has_many
|
44
|
+
return name
|
46
45
|
end
|
47
46
|
|
48
|
-
|
49
|
-
|
47
|
+
name = options[:inverse_of] || source.class.to_s.underscore.to_sym
|
48
|
+
if target_class.associations.dig(name, :type) == :has_one
|
49
|
+
return name # rubocop:disable Style/RedundantReturn
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -265,7 +265,7 @@ module Dynamoid
|
|
265
265
|
@associations[:"#{name}_ids"] ||= Dynamoid::Associations.const_get(type.to_s.camelcase).new(self, name, options)
|
266
266
|
end
|
267
267
|
|
268
|
-
define_method("#{name}="
|
268
|
+
define_method(:"#{name}=") do |objects|
|
269
269
|
@associations[:"#{name}_ids"] ||= Dynamoid::Associations.const_get(type.to_s.camelcase).new(self, name, options)
|
270
270
|
@associations[:"#{name}_ids"].setter(objects)
|
271
271
|
end
|
data/lib/dynamoid/components.rb
CHANGED
@@ -13,6 +13,7 @@ module Dynamoid
|
|
13
13
|
|
14
14
|
define_model_callbacks :create, :save, :destroy, :update
|
15
15
|
define_model_callbacks :initialize, :find, :touch, only: :after
|
16
|
+
define_model_callbacks :commit, :rollback, only: :after
|
16
17
|
|
17
18
|
before_save :set_expires_field
|
18
19
|
after_initialize :set_inheritance_field
|
@@ -33,21 +33,21 @@ module Dynamoid
|
|
33
33
|
defaults[name] = settings[name] = options[:default]
|
34
34
|
|
35
35
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
36
|
-
def #{name}
|
37
|
-
settings[#{name.inspect}]
|
38
|
-
end
|
36
|
+
def #{name} # def endpoint
|
37
|
+
settings[#{name.inspect}] # settings["endpoint"]
|
38
|
+
end # end
|
39
39
|
|
40
|
-
def #{name}=(value)
|
41
|
-
settings[#{name.inspect}] = value
|
42
|
-
end
|
40
|
+
def #{name}=(value) # def endpoint=(value)
|
41
|
+
settings[#{name.inspect}] = value # settings["endpoint"] = value
|
42
|
+
end # end
|
43
43
|
|
44
|
-
def #{name}?
|
45
|
-
#{name}
|
46
|
-
end
|
44
|
+
def #{name}? # def endpoint?
|
45
|
+
#{name} # endpoint
|
46
|
+
end # end
|
47
47
|
|
48
|
-
def reset_#{name}
|
49
|
-
settings[#{name.inspect}] = defaults[#{name.inspect}]
|
50
|
-
end
|
48
|
+
def reset_#{name} # def reset_endpoint
|
49
|
+
settings[#{name.inspect}] = defaults[#{name.inspect}] # settings["endpoint"] = defaults["endpoint"]
|
50
|
+
end # end
|
51
51
|
RUBY
|
52
52
|
end
|
53
53
|
|