dynamoid_advanced_where 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +100 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Appraisals +8 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +119 -0
- data/README.md +375 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dynamoid_advanced_where.gemspec +41 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/dynamoid_3.4.gemfile +8 -0
- data/gemfiles/dynamoid_3.4.gemfile.lock +118 -0
- data/gemfiles/dynamoid_latest.gemfile +8 -0
- data/gemfiles/dynamoid_latest.gemfile.lock +118 -0
- data/lib/dynamoid_advanced_where.rb +8 -0
- data/lib/dynamoid_advanced_where/batched_updater.rb +229 -0
- data/lib/dynamoid_advanced_where/filter_builder.rb +136 -0
- data/lib/dynamoid_advanced_where/integrations/model.rb +34 -0
- data/lib/dynamoid_advanced_where/nodes.rb +15 -0
- data/lib/dynamoid_advanced_where/nodes/and_node.rb +43 -0
- data/lib/dynamoid_advanced_where/nodes/base_node.rb +18 -0
- data/lib/dynamoid_advanced_where/nodes/equality_node.rb +37 -0
- data/lib/dynamoid_advanced_where/nodes/exists_node.rb +44 -0
- data/lib/dynamoid_advanced_where/nodes/field_node.rb +186 -0
- data/lib/dynamoid_advanced_where/nodes/greater_than_node.rb +25 -0
- data/lib/dynamoid_advanced_where/nodes/includes.rb +29 -0
- data/lib/dynamoid_advanced_where/nodes/less_than_node.rb +27 -0
- data/lib/dynamoid_advanced_where/nodes/literal_node.rb +28 -0
- data/lib/dynamoid_advanced_where/nodes/not.rb +35 -0
- data/lib/dynamoid_advanced_where/nodes/null_node.rb +25 -0
- data/lib/dynamoid_advanced_where/nodes/operation_node.rb +44 -0
- data/lib/dynamoid_advanced_where/nodes/or_node.rb +41 -0
- data/lib/dynamoid_advanced_where/nodes/root_node.rb +47 -0
- data/lib/dynamoid_advanced_where/nodes/subfield.rb +17 -0
- data/lib/dynamoid_advanced_where/query_builder.rb +47 -0
- data/lib/dynamoid_advanced_where/query_materializer.rb +73 -0
- data/lib/dynamoid_advanced_where/version.rb +3 -0
- metadata +216 -0
checksums.yaml
ADDED
@@ -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
|
+
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -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'
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.3
|
data/.travis.yml
ADDED
data/Appraisals
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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{|r| (r.foo == 'bar') & (r.baz == 'nitch') }</code>
|
164
|
+
| <code>|</code> | or | <code> where{|r| (r.foo == 'bar') | (r.baz == 'nitch') } </code>
|
165
|
+
| `!` | negation | <code>where{|r| !( (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') | (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.
|