dynamoid-advanced-where 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +97 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +19 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +5 -0
  8. data/Appraisals +8 -0
  9. data/Gemfile +9 -0
  10. data/Gemfile.lock +121 -0
  11. data/README.md +375 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/dynamoid_advanced_where.gemspec +41 -0
  16. data/gemfiles/.bundle/config +2 -0
  17. data/gemfiles/dynamoid_3.4.gemfile +8 -0
  18. data/gemfiles/dynamoid_3.4.gemfile.lock +118 -0
  19. data/gemfiles/dynamoid_latest.gemfile +8 -0
  20. data/gemfiles/dynamoid_latest.gemfile.lock +118 -0
  21. data/lib/dynamoid_advanced_where.rb +8 -0
  22. data/lib/dynamoid_advanced_where/batched_updater.rb +229 -0
  23. data/lib/dynamoid_advanced_where/filter_builder.rb +136 -0
  24. data/lib/dynamoid_advanced_where/integrations/model.rb +34 -0
  25. data/lib/dynamoid_advanced_where/nodes.rb +15 -0
  26. data/lib/dynamoid_advanced_where/nodes/and_node.rb +43 -0
  27. data/lib/dynamoid_advanced_where/nodes/base_node.rb +18 -0
  28. data/lib/dynamoid_advanced_where/nodes/equality_node.rb +37 -0
  29. data/lib/dynamoid_advanced_where/nodes/exists_node.rb +44 -0
  30. data/lib/dynamoid_advanced_where/nodes/field_node.rb +186 -0
  31. data/lib/dynamoid_advanced_where/nodes/greater_than_node.rb +25 -0
  32. data/lib/dynamoid_advanced_where/nodes/includes.rb +29 -0
  33. data/lib/dynamoid_advanced_where/nodes/less_than_node.rb +27 -0
  34. data/lib/dynamoid_advanced_where/nodes/literal_node.rb +28 -0
  35. data/lib/dynamoid_advanced_where/nodes/not.rb +35 -0
  36. data/lib/dynamoid_advanced_where/nodes/null_node.rb +25 -0
  37. data/lib/dynamoid_advanced_where/nodes/operation_node.rb +44 -0
  38. data/lib/dynamoid_advanced_where/nodes/or_node.rb +41 -0
  39. data/lib/dynamoid_advanced_where/nodes/root_node.rb +47 -0
  40. data/lib/dynamoid_advanced_where/nodes/subfield.rb +17 -0
  41. data/lib/dynamoid_advanced_where/query_builder.rb +47 -0
  42. data/lib/dynamoid_advanced_where/query_materializer.rb +73 -0
  43. data/lib/dynamoid_advanced_where/version.rb +3 -0
  44. metadata +216 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4bd2564a5833e221f313f51b3b70f196f1ee5036bb5a82886316d5badb5b2895
4
+ data.tar.gz: dd698e558c05824e9f666a2519db9be0fe817a22fc074ea1a4bee0823a838b72
5
+ SHA512:
6
+ metadata.gz: 998bfdc6302c004de857c5878b388c7387074d38e4b836e656cf879c36c8b26f9e82a74fe5b8597f8604d022b997ce538f1e7153aca6714c34d96922e8bd5487
7
+ data.tar.gz: b7e4353c5f0845079b23514256e487da117c0a0ff9c28cc31e391df7956f92136cb404a4ec0905c9de643c4813927e5f61da372d3ff0f308d16ef12aaa555b29
@@ -0,0 +1,97 @@
1
+ version: 2.0
2
+
3
+ workflows:
4
+ version: 2
5
+ build:
6
+ jobs:
7
+ - "ruby-2.5"
8
+ - "ruby-2.6"
9
+ - "ruby-2.7"
10
+
11
+ jobs:
12
+ "ruby-2.5":
13
+ docker:
14
+ - image: ruby:2.5
15
+ - image: amazon/dynamodb-local
16
+ environment:
17
+ MAX_HEAP_SIZE: 1024m
18
+ HEAP_NEWSIZE: 512m
19
+
20
+ steps:
21
+ - checkout
22
+ - restore_cache:
23
+ keys:
24
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
25
+ # fallback to using the latest cache if no exact match is found
26
+ - v1-dependencies-
27
+
28
+ - run:
29
+ name: install dependencies
30
+ command: |
31
+ bundle install
32
+ bundle exec appraisal install
33
+ - save_cache:
34
+ paths:
35
+ - ./vendor/bundle
36
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
37
+ - run: bundle exec appraisal rspec --format progress
38
+
39
+ "ruby-2.6":
40
+ docker:
41
+ - image: ruby:2.6
42
+ - image: amazon/dynamodb-local
43
+ environment:
44
+ MAX_HEAP_SIZE: 1024m
45
+ HEAP_NEWSIZE: 512m
46
+
47
+ steps:
48
+ - checkout
49
+ - restore_cache:
50
+ keys:
51
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
52
+ # fallback to using the latest cache if no exact match is found
53
+ - v1-dependencies-
54
+
55
+ - run:
56
+ name: install dependencies
57
+ command: |
58
+ bundle install
59
+ bundle exec appraisal install
60
+ - save_cache:
61
+ paths:
62
+ - ./vendor/bundle
63
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
64
+ - run: bundle exec appraisal rspec --format progress
65
+
66
+ "ruby-2.7":
67
+ docker:
68
+ - image: ruby:2.7
69
+ - image: amazon/dynamodb-local
70
+ environment:
71
+ MAX_HEAP_SIZE: 1024m
72
+ HEAP_NEWSIZE: 512m
73
+
74
+ steps:
75
+ - checkout
76
+ - restore_cache:
77
+ keys:
78
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
79
+ # fallback to using the latest cache if no exact match is found
80
+ - v1-dependencies-
81
+
82
+ - run:
83
+ name: install dependencies
84
+ command: |
85
+ bundle install
86
+ bundle exec appraisal install
87
+ - save_cache:
88
+ paths:
89
+ - ./vendor/bundle
90
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
91
+ - run: bundle exec appraisal rspec --format progress
92
+
93
+
94
+
95
+
96
+
97
+
@@ -0,0 +1,12 @@
1
+ tags
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,19 @@
1
+ AllCops:
2
+ Exclude:
3
+ - bin/*
4
+ - spec/**/*.rb
5
+ - vendor/**/*
6
+
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ Style/TrailingCommaInArguments:
11
+ Enabled: false
12
+
13
+ Metrics/LineLength:
14
+ Exclude:
15
+ - 'lib/protobufs/**/*.rb'
16
+
17
+ Metrics/BlockLength:
18
+ Exclude:
19
+ - 'spec/**/*_spec.rb'
@@ -0,0 +1 @@
1
+ 2.6.3
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
@@ -0,0 +1,8 @@
1
+
2
+ appraise "dynamoid-3.4" do
3
+ gem "dynamoid", "~> 3.4.0"
4
+ end
5
+
6
+ appraise "dynamoid-latest" do
7
+ gem "dynamoid", "~> 3.0"
8
+ end
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in dynamoid-advanced-where.gemspec
6
+ gemspec
7
+
8
+ gem 'pry'
9
+
@@ -0,0 +1,121 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dynamoid-advanced-where (1.0.0)
5
+ dynamoid (>= 3.2, < 4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (6.0.2.1)
11
+ activesupport (= 6.0.2.1)
12
+ activesupport (6.0.2.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ zeitwerk (~> 2.2)
18
+ appraisal (2.2.0)
19
+ bundler
20
+ rake
21
+ thor (>= 0.14.0)
22
+ ast (2.4.0)
23
+ aws-eventstream (1.0.3)
24
+ aws-partitions (1.270.0)
25
+ aws-sdk-core (3.89.1)
26
+ aws-eventstream (~> 1.0, >= 1.0.2)
27
+ aws-partitions (~> 1, >= 1.239.0)
28
+ aws-sigv4 (~> 1.1)
29
+ jmespath (~> 1.0)
30
+ aws-sdk-dynamodb (1.41.0)
31
+ aws-sdk-core (~> 3, >= 3.71.0)
32
+ aws-sigv4 (~> 1.1)
33
+ aws-sigv4 (1.1.0)
34
+ aws-eventstream (~> 1.0, >= 1.0.2)
35
+ bundle-audit (0.1.0)
36
+ bundler-audit
37
+ bundler-audit (0.6.1)
38
+ bundler (>= 1.2.0, < 3)
39
+ thor (~> 0.18)
40
+ childprocess (3.0.0)
41
+ coderay (1.1.2)
42
+ colorize (0.8.1)
43
+ concurrent-ruby (1.1.5)
44
+ diff-lcs (1.3)
45
+ dynamoid (3.4.1)
46
+ activemodel (>= 4)
47
+ aws-sdk-dynamodb (~> 1)
48
+ concurrent-ruby (>= 1.0)
49
+ null-logger
50
+ fasterer (0.8.2)
51
+ colorize (~> 0.7)
52
+ ruby_parser (>= 3.14.1)
53
+ i18n (1.8.2)
54
+ concurrent-ruby (~> 1.0)
55
+ iniparse (1.4.4)
56
+ jaro_winkler (1.5.4)
57
+ jmespath (1.4.0)
58
+ method_source (0.9.2)
59
+ minitest (5.14.0)
60
+ null-logger (0.1.5)
61
+ overcommit (0.52.1)
62
+ childprocess (>= 0.6.3, < 4)
63
+ iniparse (~> 1.4)
64
+ parallel (1.19.1)
65
+ parser (2.7.0.2)
66
+ ast (~> 2.4.0)
67
+ pry (0.12.2)
68
+ coderay (~> 1.1.0)
69
+ method_source (~> 0.9.0)
70
+ rainbow (3.0.0)
71
+ rake (10.5.0)
72
+ rspec (3.9.0)
73
+ rspec-core (~> 3.9.0)
74
+ rspec-expectations (~> 3.9.0)
75
+ rspec-mocks (~> 3.9.0)
76
+ rspec-core (3.9.1)
77
+ rspec-support (~> 3.9.1)
78
+ rspec-expectations (3.9.0)
79
+ diff-lcs (>= 1.2.0, < 2.0)
80
+ rspec-support (~> 3.9.0)
81
+ rspec-mocks (3.9.1)
82
+ diff-lcs (>= 1.2.0, < 2.0)
83
+ rspec-support (~> 3.9.0)
84
+ rspec-support (3.9.2)
85
+ rubocop (0.79.0)
86
+ jaro_winkler (~> 1.5.1)
87
+ parallel (~> 1.10)
88
+ parser (>= 2.7.0.1)
89
+ rainbow (>= 2.2.2, < 4.0)
90
+ ruby-progressbar (~> 1.7)
91
+ unicode-display_width (>= 1.4.0, < 1.7)
92
+ ruby-progressbar (1.10.1)
93
+ ruby_parser (3.14.1)
94
+ sexp_processor (~> 4.9)
95
+ sexp_processor (4.13.0)
96
+ thor (0.20.3)
97
+ thread_safe (0.3.6)
98
+ tzinfo (1.2.6)
99
+ thread_safe (~> 0.1)
100
+ unicode-display_width (1.6.1)
101
+ zeitwerk (2.2.2)
102
+
103
+ PLATFORMS
104
+ ruby
105
+ x86_64-darwin-17
106
+ x86_64-darwin-18
107
+
108
+ DEPENDENCIES
109
+ appraisal
110
+ bundle-audit
111
+ bundler (>= 1.16)
112
+ dynamoid-advanced-where!
113
+ fasterer
114
+ overcommit
115
+ pry
116
+ rake (~> 10.0)
117
+ rspec (~> 3.0)
118
+ rubocop
119
+
120
+ BUNDLED WITH
121
+ 2.1.4
@@ -0,0 +1,375 @@
1
+ # Dynamoid Advanced Where (DAW)
2
+
3
+ Dynamoid Advanced where provides a more advanced query structure for selecting,
4
+ and updating records. This is very much a work in progress and functionality is
5
+ being added as it is needed.
6
+
7
+ This gem is tested against:
8
+ * MRI 2.5, 2.6, and 2.7
9
+ * Dynamoid 3.4
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'dynamoid_advanced_where'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ ## Upgrading
24
+ From pre 1.0
25
+
26
+ New where block format:
27
+ ```
28
+ # Previously you had to do this to get access to certain scoped variables
29
+ local = getValue(123)
30
+ Model.where do
31
+ field == local
32
+ end
33
+
34
+ # This is annoying, the new search block has deprecated the argument-less block, and now should be called
35
+ # with a single argument
36
+
37
+ Model.where do |r|
38
+ r.field == getValue(123)
39
+ end
40
+ ```
41
+
42
+ Existence checks have been changed:
43
+ ```ruby
44
+ # Old
45
+ Model.where{|r| r.field }
46
+
47
+ # New
48
+ Model.where{|r| r.field.exists? }
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ The HellowWorld usage for this app is basic search and retrieval. You can
54
+ invoke DAW by calling `where` on a Dynamoid::Document (No relations yet) using
55
+ a new block form.
56
+
57
+ ```ruby
58
+ class Foo
59
+ include Dynamoid::Document
60
+
61
+ field :bar
62
+ field :baz
63
+ end
64
+
65
+ # Returns all records with `bar` equal to 'hello'
66
+ Foo.where{|r| r.bar == 'hello' }.all
67
+
68
+ # Advanced boolean logic is also supported
69
+
70
+ # Returns all records with `bar` equal to 'hello' and `baz` equal to 'dude'
71
+ x = Foo.where{|r| (r.baz == 'dude') & (r.bar == 'hello') }.all
72
+ ```
73
+
74
+ **Note:** Those `()` are required, you do remember your [operator precedence](https://ruby-doc.org/core-2.2.0/doc/syntax/precedence_rdoc.html)
75
+ right?
76
+
77
+ ## Filtering
78
+ Filter can be applied to Queries (Searches by hash key), Scans, and update
79
+ actions provided by this gem. Not all persistence actions make sense at the end
80
+ of a filtering query, such as `create`.
81
+
82
+ ### Field Existence
83
+ Checks to see if a field is defined. See [attribute_exists](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)
84
+
85
+ Valid on field types: `any`
86
+
87
+ #### Example
88
+ `where{|r| r.foo }` or `where{|r| r.foo.exists! }`
89
+
90
+ ### Value Equality
91
+ The equality of a field can be tested using `==` and not equals tested using `!=`
92
+
93
+ Valid on field types: `string`
94
+
95
+ #### Example
96
+ `where{|r| r.foo == 'bar' }` and `where{|r| r.foo != 'bar' }`
97
+
98
+ ### Less than
99
+ The less than for a field can be tested using `<`
100
+
101
+ Valid on field types: `numeric`, and `datetime` (only when stored as a number)
102
+
103
+ #### Example
104
+ `where{|r| r.foo < 123 }` and `where{|r| r.foo < Date.today }`
105
+
106
+ ### Includes
107
+ This operator may be used to check if:
108
+
109
+ * A string contains another substring
110
+ * A set of String or Integers contain a given value
111
+
112
+ Valid on field types: `string`, or `set` of `String` / `Integer`
113
+
114
+ #### Example
115
+ `where{|r| r.foo.includes?(123) }` and `where{|r| r.foo.includes?('foo') }`
116
+
117
+ ### Working with Map and Raw types
118
+ When it comes to map and raw attribute types, DAW takes the approach of
119
+ trusting you, since the exact format is not explicitly defined or enforced.
120
+ You may specify the path to the value, as well as the value type and it will
121
+ behave like any other top level attribute.
122
+
123
+ ```
124
+ where do |r|
125
+ (r.ratings.dig(:verified_reviews, :review_count, type: :number) > 100) &
126
+ (r.ratings.dig(:verified_reviews, :average_review, type: :number) > 4) &
127
+ (r.metadata.dig(:keywords, type: :set, of: :string).includes?('foo'))
128
+ end
129
+ ```
130
+
131
+ If you have a nested array, you may access the elements by index by passing an integer into the `dig` command.
132
+
133
+ #### Custom Classes
134
+ The subfield dig works with CustomClasses if the classes store their data as a hash.
135
+
136
+ **Example**
137
+
138
+ ```ruby
139
+ CustomAttribute = Struct.new(:sub_field_a) do
140
+ def self.dynamoid_dump(item)
141
+ item.to_h
142
+ end
143
+
144
+ def self.dynamoid_load(data)
145
+ new(**data.transform_keys(&:to_sym))
146
+ end
147
+ end
148
+
149
+ class Foo
150
+ include Dynamoid::Document
151
+ field :bar, CustomAttribute
152
+ end
153
+
154
+ x = Foo.create(bar: CustomAttribute.new('b'))
155
+ Foo.where{|r| r.bar.dig(:sub_field_a, type: string).inclues?('b') }.all
156
+ # => [x]
157
+ ```
158
+
159
+ ### Boolean Operators
160
+
161
+ | Logical Operator | Behavior | Example
162
+ | ------------- | ------------- | --------
163
+ | `&` | and | <code>where{&#124;r&#124; (r.foo == 'bar') & (r.baz == 'nitch') }</code>
164
+ | <code>&#124;</code> | or | <code> where{&#124;r&#124; (r.foo == 'bar') &#124; (r.baz == 'nitch') } </code>
165
+ | `!` | negation | <code>where{&#124;r&#124; !( (r.foo == 'bar') & (r.baz == 'nitch')) }</code>
166
+
167
+ ## Retrieving Records
168
+ Retrieving a pre-filtered set of records is a fairly obvious use case for the
169
+ filtering abilities provided by DAW. Only a subset of what you may expect is
170
+ provided, but enumerable is mixed in, and each provides an Enumerator.
171
+
172
+ Provided methods
173
+ * `all`
174
+ * `first`
175
+ * `each` (and related enumerable methods)
176
+
177
+ ### Scan vs Query
178
+ DAW will automatically preform a query when it determines it is possible,
179
+ however if a query is determined to not be appropriate, a scan will be conduced
180
+ instead. When ever possible, query do not scan. See the DynamoDB docs for why.
181
+
182
+ DAW will also extract filters on the range key whenever possible. In order to
183
+ filter on a range key to be used for a query, it must be one of the allowed
184
+ range key filters and at the top level of filters.
185
+
186
+
187
+ **NOTE:** Global Secondary Indices are not yet supported
188
+
189
+ #### How a query-able filter is identified
190
+ A scan will be performed when the search is not done via the hash key, with
191
+ exact equality. DAW will examine the boolean logic to determine if a key
192
+ condition may be extracted. For example, a query will be performed in the
193
+ following examples:
194
+
195
+ * `where{|r| r.id == '123' }`
196
+ * `where{|r| (r.id == '123') & (r.bar == 'baz') }`
197
+
198
+ But it will not be performed in these scenarios
199
+
200
+ * `where{|r| r.id != '123' }`
201
+ * `where{|r| !(r.id == '123') }`
202
+ * <code>where{ (r.id == '123') &#124; (r.bar == 'baz') }</code>
203
+
204
+ ## Combination of Filters
205
+ Multiple DAW filters can be combined. This will provides the ability to compose
206
+ filtering conditions to keep your code more readable and DRY.
207
+
208
+ ### Combining conditions with AND
209
+ ```ruby
210
+ class Foo
211
+ include Dynamoid::Document
212
+
213
+ field :bar
214
+ field :baz
215
+ end
216
+
217
+ filter1 = Foo.where{|r| r.bar == 'abcd' }
218
+ filter2 = Foo.where{|r| r.baz == 'dude' }
219
+
220
+ # All of these produce the same results
221
+ combination1 = filter1.where(filter2)
222
+ combination2 = filter1.and(filter2)
223
+ combination3 = filter1.where{|r| r.baz == 'dude' }
224
+ ```
225
+
226
+ ## Mutating Records
227
+ DAW provides the ability to modify records only if they meet the criteria defined
228
+ by the where block.
229
+
230
+ Changes are also provided in batch form, so you may change multiple values with a single call.
231
+ There may also be singleton methods provided for easy of use.
232
+
233
+ ## Batch Updates
234
+
235
+ ```ruby
236
+ Model.where{ conditions }.batch_update
237
+ .set_values(field_name1: 'value', field_name2: 123)
238
+ .append_to(arr_field_name: [1,2,3], set_field_name: %w[a b c])
239
+ .apply(hash_key, range_key)
240
+ ```
241
+
242
+ Like all conditional updates it will return the full record with the new data
243
+ if it successfully updates. If it fails to update, it will return nil.
244
+
245
+ If the specified hash key, or hash/range key combination is not already present
246
+ it will be inserted with the desired mutations (if possible).
247
+
248
+ ### Setting a single field
249
+ The batch updated method `set_values(attr_name: new_attr_value, other_atter: val)`
250
+
251
+ #### Shortcut Method
252
+ You map perform an upsert using the `.upsert` method. This method performs a
253
+ simple set on the provided hash and range key.
254
+
255
+ For example, consider the following example for conditionally updating a string
256
+ field.
257
+
258
+ ```ruby
259
+ class Foo
260
+ include Dynamoid::Document
261
+ field :a_string
262
+ field :a_number, number
263
+ end
264
+
265
+ item = Foo.create(a_number: 5, a_string: 'bar')
266
+
267
+ Foo.where{|r| r.a_number > 5 }.upsert(item.id, a_string: 'dude')
268
+
269
+ item.reload.a_string # => 'bar'
270
+
271
+ Foo.where{|r| r.a_number > 4 }.upsert(item.id, a_string: 'wassup')
272
+
273
+ item.reload.a_string # => 'wassup'
274
+ ```
275
+
276
+ `upsert` can also create a record if an existing one is not found, if the hash
277
+ key can be specified. By requiring the hash key be set, you can prevent an insert
278
+ and force an update to occur.
279
+
280
+ **Note:** Upsert must be called with the hash as the first parameter, and
281
+ the range key as the second parameter if required for the model.
282
+
283
+ *Note:** Upsert will return nil if no records were found that matched the provided
284
+ parameters
285
+
286
+ ### Appending values to a List or Set
287
+ You can append a set of values to an existing set or array by using the
288
+
289
+ ```ruby
290
+ append_to(
291
+ array_field: [1,2,3],
292
+ set_field: %w[foo bar],
293
+ )
294
+ ```
295
+
296
+ If the fields are unset, it will still apply the changes to am empty array.
297
+
298
+ ### Increment / Decrement a value
299
+
300
+ You may increment or decrement a numeric value by using `increment` or `decrement`
301
+
302
+ ```ruby
303
+ increment(:field_one, :field_two)
304
+ ```
305
+
306
+ ```ruby
307
+ decrement(:field_one, :field_two)
308
+ ```
309
+
310
+ You may also provide an optional `by:` config to increment by more than one.
311
+
312
+ ```ruby
313
+ increment(:field_one, :field_two, by: 3)
314
+ ```
315
+
316
+ ```ruby
317
+ decrement(:field_one, :field_two, by: 3)
318
+ ```
319
+
320
+ If the value of the field is currently unset, it will initialize to zero
321
+
322
+ ## Development
323
+
324
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
325
+
326
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
327
+
328
+ ### TODO:
329
+
330
+ #### Known issues
331
+ * If you specify multiple term nodes for a query it will generate an invalid
332
+ query
333
+ * No support for [custom types](https://github.com/Dynamoid/Dynamoid#custom-types)
334
+
335
+ #### Enhancements
336
+ * Support Global Secondary Index
337
+ * Conditions:
338
+ * Equality
339
+ * Partially implemented
340
+ * Not Equals
341
+ * less than
342
+ * Implemented for numerics, datetimes, dates stored as integer
343
+ * less than or equal to
344
+ * greater than
345
+ * greater than or equal to
346
+ * between
347
+ * in
348
+ * attribute_not_exists
349
+ * attribute_type
350
+ * begins with
351
+ * contains
352
+ * size
353
+ * Query enhancements
354
+ * Range key conditions:
355
+ * equality
356
+ * less than
357
+ * less than or equal to
358
+ * greater than
359
+ * greater than or equal to
360
+ * between
361
+ * begins with
362
+ * convert to bulk query if multiple hash key terms are specified
363
+ * Item mutation [Docs](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET)
364
+ * Update (without insert)
365
+ * Upserting
366
+ * Increment / Decrement number
367
+ * Append item(s) to list
368
+ * Prepend item(s) to list
369
+ * Adding nested map attribute (low priority)
370
+ * Set value if not set
371
+ * Remove attributes
372
+
373
+ ## Contributing
374
+
375
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/dynamoid-advanced-where.