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 +100 -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 +119 -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: 92ed86764764bc4cb565a2499dcbb686dcc82c043e2e6739d5e9a43a508096e7
4
+ data.tar.gz: ab2b862945f23e4289b7231346ad81a457fba65c222b0c1b4bd0f219f4ca2d8c
5
+ SHA512:
6
+ metadata.gz: 1ea844c390551ebfb5813ac4c6c4f66a5acd31699fbe964e10fa08b912d464f46ff1685397f29c0dc1aed951432ffa03e1040b9d82cc25fdc692941447688f6c
7
+ data.tar.gz: 964bcf57c3c19b94853d711b3bdfda1141a6ee113f9532d86452435372959beeb9c58ca81940c34520e03a32c4d9af2f3e5d91f4991bc027d6a8a94dd23dfce4
@@ -0,0 +1,100 @@
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
+ gem update bundler
32
+ bundle install
33
+ bundle exec appraisal install
34
+ - save_cache:
35
+ paths:
36
+ - ./vendor/bundle
37
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
38
+ - run: bundle exec appraisal rspec --format progress
39
+
40
+ "ruby-2.6":
41
+ docker:
42
+ - image: ruby:2.6
43
+ - image: amazon/dynamodb-local
44
+ environment:
45
+ MAX_HEAP_SIZE: 1024m
46
+ HEAP_NEWSIZE: 512m
47
+
48
+ steps:
49
+ - checkout
50
+ - restore_cache:
51
+ keys:
52
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
53
+ # fallback to using the latest cache if no exact match is found
54
+ - v1-dependencies-
55
+
56
+ - run:
57
+ name: install dependencies
58
+ command: |
59
+ gem update bundler
60
+ bundle install
61
+ bundle exec appraisal install
62
+ - save_cache:
63
+ paths:
64
+ - ./vendor/bundle
65
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
66
+ - run: bundle exec appraisal rspec --format progress
67
+
68
+ "ruby-2.7":
69
+ docker:
70
+ - image: ruby:2.7
71
+ - image: amazon/dynamodb-local
72
+ environment:
73
+ MAX_HEAP_SIZE: 1024m
74
+ HEAP_NEWSIZE: 512m
75
+
76
+ steps:
77
+ - checkout
78
+ - restore_cache:
79
+ keys:
80
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
81
+ # fallback to using the latest cache if no exact match is found
82
+ - v1-dependencies-
83
+
84
+ - run:
85
+ name: install dependencies
86
+ command: |
87
+ gem update bundler
88
+ bundle install
89
+ bundle exec appraisal install
90
+ - save_cache:
91
+ paths:
92
+ - ./vendor/bundle
93
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Appraisals" }}
94
+ - run: bundle exec appraisal rspec --format progress
95
+
96
+
97
+
98
+
99
+
100
+
@@ -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,119 @@
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
+ bundler-audit (0.6.1)
36
+ bundler (>= 1.2.0, < 3)
37
+ thor (~> 0.18)
38
+ childprocess (3.0.0)
39
+ coderay (1.1.2)
40
+ colorize (0.8.1)
41
+ concurrent-ruby (1.1.5)
42
+ diff-lcs (1.3)
43
+ dynamoid (3.4.1)
44
+ activemodel (>= 4)
45
+ aws-sdk-dynamodb (~> 1)
46
+ concurrent-ruby (>= 1.0)
47
+ null-logger
48
+ fasterer (0.8.2)
49
+ colorize (~> 0.7)
50
+ ruby_parser (>= 3.14.1)
51
+ i18n (1.8.2)
52
+ concurrent-ruby (~> 1.0)
53
+ iniparse (1.4.4)
54
+ jaro_winkler (1.5.4)
55
+ jmespath (1.4.0)
56
+ method_source (0.9.2)
57
+ minitest (5.14.0)
58
+ null-logger (0.1.5)
59
+ overcommit (0.52.1)
60
+ childprocess (>= 0.6.3, < 4)
61
+ iniparse (~> 1.4)
62
+ parallel (1.19.1)
63
+ parser (2.7.0.2)
64
+ ast (~> 2.4.0)
65
+ pry (0.12.2)
66
+ coderay (~> 1.1.0)
67
+ method_source (~> 0.9.0)
68
+ rainbow (3.0.0)
69
+ rake (10.5.0)
70
+ rspec (3.9.0)
71
+ rspec-core (~> 3.9.0)
72
+ rspec-expectations (~> 3.9.0)
73
+ rspec-mocks (~> 3.9.0)
74
+ rspec-core (3.9.1)
75
+ rspec-support (~> 3.9.1)
76
+ rspec-expectations (3.9.0)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.9.0)
79
+ rspec-mocks (3.9.1)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.9.0)
82
+ rspec-support (3.9.2)
83
+ rubocop (0.79.0)
84
+ jaro_winkler (~> 1.5.1)
85
+ parallel (~> 1.10)
86
+ parser (>= 2.7.0.1)
87
+ rainbow (>= 2.2.2, < 4.0)
88
+ ruby-progressbar (~> 1.7)
89
+ unicode-display_width (>= 1.4.0, < 1.7)
90
+ ruby-progressbar (1.10.1)
91
+ ruby_parser (3.14.2)
92
+ sexp_processor (~> 4.9)
93
+ sexp_processor (4.14.0)
94
+ thor (0.20.3)
95
+ thread_safe (0.3.6)
96
+ tzinfo (1.2.6)
97
+ thread_safe (~> 0.1)
98
+ unicode-display_width (1.6.1)
99
+ zeitwerk (2.2.2)
100
+
101
+ PLATFORMS
102
+ ruby
103
+ x86_64-darwin-17
104
+ x86_64-darwin-18
105
+
106
+ DEPENDENCIES
107
+ appraisal
108
+ bundler (>= 1.16)
109
+ bundler-audit
110
+ dynamoid_advanced_where!
111
+ fasterer
112
+ overcommit
113
+ pry
114
+ rake (~> 10.0)
115
+ rspec (~> 3.0)
116
+ rubocop
117
+
118
+ BUNDLED WITH
119
+ 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.