chewy 0.10.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +240 -0
- data/.rubocop.yml +25 -25
- data/Appraisals +12 -10
- data/CHANGELOG.md +252 -263
- data/Gemfile +5 -1
- data/LICENSE.txt +1 -1
- data/README.md +142 -78
- data/chewy.gemspec +10 -12
- data/gemfiles/{rails.4.2.mongoid.5.1.gemfile → rails.5.2.activerecord.gemfile} +6 -4
- data/gemfiles/{rails.4.2.activerecord.gemfile → rails.5.2.mongoid.6.4.gemfile} +6 -4
- data/gemfiles/{rails.4.0.activerecord.gemfile → rails.6.0.activerecord.gemfile} +6 -3
- data/gemfiles/rails.6.1.activerecord.gemfile +19 -0
- data/gemfiles/sequel.4.45.gemfile +2 -2
- data/lib/chewy.rb +2 -1
- data/lib/chewy/backports/duplicable.rb +1 -1
- data/lib/chewy/config.rb +10 -39
- data/lib/chewy/fields/base.rb +40 -28
- data/lib/chewy/fields/root.rb +18 -11
- data/lib/chewy/index.rb +3 -1
- data/lib/chewy/index/actions.rb +27 -15
- data/lib/chewy/index/settings.rb +2 -0
- data/lib/chewy/index/specification.rb +12 -10
- data/lib/chewy/minitest/helpers.rb +6 -6
- data/lib/chewy/minitest/search_index_receiver.rb +17 -17
- data/lib/chewy/multi_search.rb +62 -0
- data/lib/chewy/railtie.rb +4 -4
- data/lib/chewy/rake_helper.rb +5 -5
- data/lib/chewy/rspec/update_index.rb +3 -5
- data/lib/chewy/search.rb +4 -11
- data/lib/chewy/search/loader.rb +1 -1
- data/lib/chewy/search/pagination/will_paginate.rb +4 -2
- data/lib/chewy/search/parameters.rb +24 -6
- data/lib/chewy/search/parameters/allow_partial_search_results.rb +27 -0
- data/lib/chewy/search/parameters/concerns/query_storage.rb +4 -3
- data/lib/chewy/search/parameters/indices.rb +123 -0
- data/lib/chewy/search/parameters/none.rb +1 -3
- data/lib/chewy/search/request.rb +100 -74
- data/lib/chewy/search/scrolling.rb +7 -6
- data/lib/chewy/stash.rb +30 -21
- data/lib/chewy/strategy/active_job.rb +1 -1
- data/lib/chewy/strategy/atomic.rb +1 -1
- data/lib/chewy/strategy/sidekiq.rb +1 -1
- data/lib/chewy/type.rb +5 -2
- data/lib/chewy/type/adapter/active_record.rb +1 -1
- data/lib/chewy/type/adapter/base.rb +9 -9
- data/lib/chewy/type/adapter/mongoid.rb +2 -4
- data/lib/chewy/type/adapter/orm.rb +7 -4
- data/lib/chewy/type/adapter/sequel.rb +5 -7
- data/lib/chewy/type/crutch.rb +1 -1
- data/lib/chewy/type/import.rb +13 -11
- data/lib/chewy/type/import/bulk_builder.rb +1 -1
- data/lib/chewy/type/import/bulk_request.rb +4 -2
- data/lib/chewy/type/import/journal_builder.rb +3 -3
- data/lib/chewy/type/import/routine.rb +3 -3
- data/lib/chewy/type/mapping.rb +42 -36
- data/lib/chewy/type/observe.rb +16 -12
- data/lib/chewy/type/syncer.rb +15 -14
- data/lib/chewy/type/witchcraft.rb +11 -7
- data/lib/chewy/type/wrapper.rb +14 -4
- data/lib/chewy/version.rb +1 -1
- data/lib/sequel/plugins/chewy_observe.rb +4 -19
- data/migration_guide.md +18 -0
- data/spec/chewy/config_spec.rb +16 -21
- data/spec/chewy/fields/base_spec.rb +70 -70
- data/spec/chewy/fields/root_spec.rb +56 -9
- data/spec/chewy/index/actions_spec.rb +63 -7
- data/spec/chewy/index/specification_spec.rb +25 -16
- data/spec/chewy/index_spec.rb +75 -45
- data/spec/chewy/journal_spec.rb +33 -29
- data/spec/chewy/minitest/search_index_receiver_spec.rb +11 -9
- data/spec/chewy/multi_search_spec.rb +85 -0
- data/spec/chewy/rake_helper_spec.rb +123 -95
- data/spec/chewy/rspec/update_index_spec.rb +47 -46
- data/spec/chewy/runtime_spec.rb +2 -2
- data/spec/chewy/search/pagination/kaminari_spec.rb +7 -3
- data/spec/chewy/search/pagination/will_paginate_spec.rb +9 -3
- data/spec/chewy/search/parameters/indices_spec.rb +190 -0
- data/spec/chewy/search/parameters/none_spec.rb +1 -1
- data/spec/chewy/search/parameters_spec.rb +21 -4
- data/spec/chewy/search/request_spec.rb +101 -70
- data/spec/chewy/search/response_spec.rb +27 -17
- data/spec/chewy/search/scrolling_spec.rb +25 -16
- data/spec/chewy/search_spec.rb +49 -35
- data/spec/chewy/stash_spec.rb +15 -13
- data/spec/chewy/strategy/active_job_spec.rb +15 -2
- data/spec/chewy/strategy/shoryuken_spec.rb +8 -2
- data/spec/chewy/strategy/sidekiq_spec.rb +6 -2
- data/spec/chewy/type/adapter/active_record_spec.rb +16 -4
- data/spec/chewy/type/import/bulk_builder_spec.rb +9 -94
- data/spec/chewy/type/import/journal_builder_spec.rb +17 -15
- data/spec/chewy/type/import_spec.rb +6 -0
- data/spec/chewy/type/mapping_spec.rb +51 -18
- data/spec/chewy/type/observe_spec.rb +4 -4
- data/spec/chewy/type/witchcraft_spec.rb +31 -0
- data/spec/chewy/type/wrapper_spec.rb +3 -1
- data/spec/chewy_spec.rb +0 -7
- data/spec/spec_helper.rb +5 -1
- data/spec/support/active_record.rb +20 -0
- metadata +46 -116
- data/.travis.yml +0 -53
- data/LEGACY_DSL.md +0 -497
- data/gemfiles/rails.4.1.activerecord.gemfile +0 -14
- data/gemfiles/rails.5.0.activerecord.gemfile +0 -15
- data/gemfiles/rails.5.0.mongoid.6.0.gemfile +0 -15
- data/gemfiles/rails.5.1.activerecord.gemfile +0 -15
- data/gemfiles/rails.5.1.mongoid.6.1.gemfile +0 -15
- data/lib/chewy/query.rb +0 -1098
- data/lib/chewy/query/compose.rb +0 -68
- data/lib/chewy/query/criteria.rb +0 -191
- data/lib/chewy/query/filters.rb +0 -227
- data/lib/chewy/query/loading.rb +0 -111
- data/lib/chewy/query/nodes/and.rb +0 -25
- data/lib/chewy/query/nodes/base.rb +0 -17
- data/lib/chewy/query/nodes/bool.rb +0 -34
- data/lib/chewy/query/nodes/equal.rb +0 -34
- data/lib/chewy/query/nodes/exists.rb +0 -20
- data/lib/chewy/query/nodes/expr.rb +0 -28
- data/lib/chewy/query/nodes/field.rb +0 -110
- data/lib/chewy/query/nodes/has_child.rb +0 -15
- data/lib/chewy/query/nodes/has_parent.rb +0 -15
- data/lib/chewy/query/nodes/has_relation.rb +0 -59
- data/lib/chewy/query/nodes/match_all.rb +0 -11
- data/lib/chewy/query/nodes/missing.rb +0 -20
- data/lib/chewy/query/nodes/not.rb +0 -25
- data/lib/chewy/query/nodes/or.rb +0 -25
- data/lib/chewy/query/nodes/prefix.rb +0 -19
- data/lib/chewy/query/nodes/query.rb +0 -20
- data/lib/chewy/query/nodes/range.rb +0 -63
- data/lib/chewy/query/nodes/raw.rb +0 -15
- data/lib/chewy/query/nodes/regexp.rb +0 -35
- data/lib/chewy/query/nodes/script.rb +0 -20
- data/lib/chewy/query/pagination.rb +0 -25
- data/spec/chewy/query/criteria_spec.rb +0 -700
- data/spec/chewy/query/filters_spec.rb +0 -201
- data/spec/chewy/query/loading_spec.rb +0 -124
- data/spec/chewy/query/nodes/and_spec.rb +0 -12
- data/spec/chewy/query/nodes/bool_spec.rb +0 -14
- data/spec/chewy/query/nodes/equal_spec.rb +0 -32
- data/spec/chewy/query/nodes/exists_spec.rb +0 -18
- data/spec/chewy/query/nodes/has_child_spec.rb +0 -59
- data/spec/chewy/query/nodes/has_parent_spec.rb +0 -59
- data/spec/chewy/query/nodes/match_all_spec.rb +0 -11
- data/spec/chewy/query/nodes/missing_spec.rb +0 -16
- data/spec/chewy/query/nodes/not_spec.rb +0 -13
- data/spec/chewy/query/nodes/or_spec.rb +0 -12
- data/spec/chewy/query/nodes/prefix_spec.rb +0 -16
- data/spec/chewy/query/nodes/query_spec.rb +0 -12
- data/spec/chewy/query/nodes/range_spec.rb +0 -32
- data/spec/chewy/query/nodes/raw_spec.rb +0 -11
- data/spec/chewy/query/nodes/regexp_spec.rb +0 -43
- data/spec/chewy/query/nodes/script_spec.rb +0 -15
- data/spec/chewy/query/pagination/kaminari_spec.rb +0 -5
- data/spec/chewy/query/pagination/will_paginate_spec.rb +0 -5
- data/spec/chewy/query/pagination_spec.rb +0 -39
- data/spec/chewy/query_spec.rb +0 -636
- data/spec/chewy/search/parameters/indices_boost_spec.rb +0 -83
data/.travis.yml
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
services:
|
4
|
-
- mongodb
|
5
|
-
jdk:
|
6
|
-
- oraclejdk8
|
7
|
-
rvm:
|
8
|
-
- 2.2.7
|
9
|
-
- 2.3.4
|
10
|
-
- 2.4.1
|
11
|
-
env:
|
12
|
-
global:
|
13
|
-
- TEST_CLUSTER_NODES=1
|
14
|
-
matrix:
|
15
|
-
- ES_VERSION=2.4.4
|
16
|
-
- ES_VERSION=5.4.0
|
17
|
-
gemfile:
|
18
|
-
- gemfiles/rails.4.0.activerecord.gemfile
|
19
|
-
- gemfiles/rails.4.1.activerecord.gemfile
|
20
|
-
- gemfiles/rails.4.2.activerecord.gemfile
|
21
|
-
- gemfiles/rails.5.0.activerecord.gemfile
|
22
|
-
- gemfiles/rails.5.1.activerecord.gemfile
|
23
|
-
- gemfiles/rails.4.2.mongoid.5.1.gemfile
|
24
|
-
- gemfiles/rails.5.0.mongoid.6.0.gemfile
|
25
|
-
- gemfiles/rails.5.1.mongoid.6.1.gemfile
|
26
|
-
- gemfiles/sequel.4.45.gemfile
|
27
|
-
matrix:
|
28
|
-
exclude:
|
29
|
-
- rvm: 2.2.7
|
30
|
-
env: ES_VERSION=5.4.0
|
31
|
-
- rvm: 2.3.4
|
32
|
-
env: ES_VERSION=2.4.4
|
33
|
-
- gemfile: gemfiles/rails.4.0.activerecord.gemfile
|
34
|
-
env: ES_VERSION=5.4.0
|
35
|
-
- gemfile: gemfiles/rails.4.1.activerecord.gemfile
|
36
|
-
env: ES_VERSION=5.4.0
|
37
|
-
- gemfile: gemfiles/rails.4.2.mongoid.5.1.gemfile
|
38
|
-
env: ES_VERSION=5.4.0
|
39
|
-
- gemfile: gemfiles/rails.5.1.mongoid.6.0.gemfile
|
40
|
-
env: ES_VERSION=5.4.0
|
41
|
-
- rvm: 2.4.1
|
42
|
-
gemfile: gemfiles/rails.4.0.activerecord.gemfile
|
43
|
-
- rvm: 2.4.1
|
44
|
-
gemfile: gemfiles/rails.4.1.activerecord.gemfile
|
45
|
-
before_install:
|
46
|
-
- curl -s https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.4/elasticsearch-2.4.4.tar.gz | tar xz -C /tmp
|
47
|
-
- curl -s https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.0.tar.gz | tar xz -C /tmp
|
48
|
-
before_script:
|
49
|
-
- /tmp/elasticsearch-2.4.4/bin/plugin install delete-by-query
|
50
|
-
- TEST_CLUSTER_COMMAND=/tmp/elasticsearch-$ES_VERSION/bin/elasticsearch TEST_CLUSTER_LOGS=/tmp/log rake es:start
|
51
|
-
script:
|
52
|
-
- bundle exec rspec
|
53
|
-
- bundle exec rubocop
|
data/LEGACY_DSL.md
DELETED
@@ -1,497 +0,0 @@
|
|
1
|
-
# Previous DSL readme.
|
2
|
-
|
3
|
-
See `Chewy::Query` for details.
|
4
|
-
|
5
|
-
* [Index querying](#index-querying)
|
6
|
-
* [Additional query action.](#additional-query-action)
|
7
|
-
* [Filters query DSL](#filters-query-dsl)
|
8
|
-
* [Faceting](#faceting)
|
9
|
-
* [Aggregations](#aggregations)
|
10
|
-
* [Script fields](#script-fields)
|
11
|
-
* [Script scoring](#script-scoring)
|
12
|
-
* [Boost Factor](#boost-factor)
|
13
|
-
* [Objects loading](#objects-loading)
|
14
|
-
|
15
|
-
## Index querying
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
scope = UsersIndex.query(term: {name: 'foo'})
|
19
|
-
.filter(range: {rating: {gte: 100}})
|
20
|
-
.order(created: :desc)
|
21
|
-
.limit(20).offset(100)
|
22
|
-
|
23
|
-
scope.to_a # => will produce array of UserIndex::User or other types instances
|
24
|
-
scope.map { |user| user.email }
|
25
|
-
scope.total_count # => will return total objects count
|
26
|
-
|
27
|
-
scope.per(10).page(3) # supports kaminari pagination
|
28
|
-
scope.explain.map { |user| user._explanation }
|
29
|
-
scope.only(:id, :email) # returns ids and emails only
|
30
|
-
|
31
|
-
scope.merge(other_scope) # queries could be merged
|
32
|
-
```
|
33
|
-
|
34
|
-
Also, queries can be performed on a type individually:
|
35
|
-
|
36
|
-
```ruby
|
37
|
-
UsersIndex::User.filter(term: {name: 'foo'}) # will return UserIndex::User collection only
|
38
|
-
```
|
39
|
-
|
40
|
-
If you are performing more than one `filter` or `query` in the chain, all the filters and queries will be concatenated in the way specified by
|
41
|
-
`filter_mode` and `query_mode` respectively.
|
42
|
-
|
43
|
-
The default `filter_mode` is `:and` and the default `query_mode` is `bool`.
|
44
|
-
|
45
|
-
Available filter modes are: `:and`, `:or`, `:must`, `:should` and any minimum_should_match-acceptable value
|
46
|
-
|
47
|
-
Available query modes are: `:must`, `:should`, `:dis_max`, any minimum_should_match-acceptable value or float value for dis_max query with tie_breaker specified.
|
48
|
-
|
49
|
-
```ruby
|
50
|
-
UsersIndex::User.filter{ name == 'Fred' }.filter{ age < 42 } # will be wrapped with `and` filter
|
51
|
-
UsersIndex::User.filter{ name == 'Fred' }.filter{ age < 42 }.filter_mode(:should) # will be wrapped with bool `should` filter
|
52
|
-
UsersIndex::User.filter{ name == 'Fred' }.filter{ age < 42 }.filter_mode('75%') # will be wrapped with bool `should` filter with `minimum_should_match: '75%'`
|
53
|
-
```
|
54
|
-
|
55
|
-
See [query.rb](lib/chewy/query.rb) for more details.
|
56
|
-
|
57
|
-
## Additional query action.
|
58
|
-
|
59
|
-
You may also perform additional actions on the query scope, such as deleting of all the scope documents:
|
60
|
-
|
61
|
-
```ruby
|
62
|
-
UsersIndex.delete_all
|
63
|
-
UsersIndex::User.delete_all
|
64
|
-
UsersIndex.filter{ age < 42 }.delete_all
|
65
|
-
UsersIndex::User.filter{ age < 42 }.delete_all
|
66
|
-
```
|
67
|
-
|
68
|
-
## Filters query DSL
|
69
|
-
|
70
|
-
There is a test version of the filter-creating DSL:
|
71
|
-
|
72
|
-
```ruby
|
73
|
-
UsersIndex.filter{ name == 'Fred' } # will produce `term` filter.
|
74
|
-
UsersIndex.filter{ age <= 42 } # will produce `range` filter.
|
75
|
-
```
|
76
|
-
|
77
|
-
The basis of the DSL is the expression. There are 2 types of expressions:
|
78
|
-
|
79
|
-
* Simple function
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
UsersIndex.filter{ s('doc["num"] > 1') } # script expression
|
83
|
-
UsersIndex.filter{ q(query_string: {query: 'lazy fox'}) } # query expression
|
84
|
-
```
|
85
|
-
|
86
|
-
* Field-dependent composite expression
|
87
|
-
Consists of the field name (with or without dot notation), a value, and an action operator between them. The field name might take additional options for passing to the resulting expression.
|
88
|
-
|
89
|
-
```ruby
|
90
|
-
UsersIndex.filter{ name == 'Name' } # simple field term filter
|
91
|
-
UsersIndex.filter{ name(:bool) == ['Name1', 'Name2'] } # terms query with `execution: :bool` option passed
|
92
|
-
UsersIndex.filter{ answers.title =~ /regexp/ } # regexp filter for `answers.title` field
|
93
|
-
```
|
94
|
-
|
95
|
-
You can combine expressions as you wish with the help of combination operators.
|
96
|
-
|
97
|
-
```ruby
|
98
|
-
UsersIndex.filter{ (name == 'Name') & (email == 'Email') } # combination produces `and` filter
|
99
|
-
UsersIndex.filter{
|
100
|
-
must(
|
101
|
-
should(name =~ 'Fr').should_not(name == 'Fred') & (age == 42), email =~ /gmail\.com/
|
102
|
-
) | ((roles.admin == true) & name?)
|
103
|
-
} # many of the combination possibilities
|
104
|
-
```
|
105
|
-
|
106
|
-
There is also a special syntax for cache enabling:
|
107
|
-
|
108
|
-
```ruby
|
109
|
-
UsersIndex.filter{ ~name == 'Name' } # you can apply tilde to the field name
|
110
|
-
UsersIndex.filter{ ~(name == 'Name') } # or to the whole expression
|
111
|
-
|
112
|
-
# if you are applying cache to the one part of range filter
|
113
|
-
# the whole filter will be cached:
|
114
|
-
UsersIndex.filter{ ~(age > 42) & (age <= 50) }
|
115
|
-
|
116
|
-
# You can pass cache options as a field option also.
|
117
|
-
UsersIndex.filter{ name(cache: true) == 'Name' }
|
118
|
-
UsersIndex.filter{ name(cache: false) == 'Name' }
|
119
|
-
|
120
|
-
# With regexp filter you can pass _cache_key
|
121
|
-
UsersIndex.filter{ name(cache: 'name_regexp') =~ /Name/ }
|
122
|
-
# Or not
|
123
|
-
UsersIndex.filter{ name(cache: true) =~ /Name/ }
|
124
|
-
```
|
125
|
-
|
126
|
-
Compliance cheatsheet for filters and DSL expressions:
|
127
|
-
|
128
|
-
* Term filter
|
129
|
-
|
130
|
-
```json
|
131
|
-
{"term": {"name": "Fred"}}
|
132
|
-
{"not": {"term": {"name": "Johny"}}}
|
133
|
-
```
|
134
|
-
|
135
|
-
```ruby
|
136
|
-
UsersIndex.filter{ name == 'Fred' }
|
137
|
-
UsersIndex.filter{ name != 'Johny' }
|
138
|
-
```
|
139
|
-
|
140
|
-
* Terms filter
|
141
|
-
|
142
|
-
```json
|
143
|
-
{"terms": {"name": ["Fred", "Johny"]}}
|
144
|
-
{"not": {"terms": {"name": ["Fred", "Johny"]}}}
|
145
|
-
|
146
|
-
{"terms": {"name": ["Fred", "Johny"], "execution": "or"}}
|
147
|
-
|
148
|
-
{"terms": {"name": ["Fred", "Johny"], "execution": "and"}}
|
149
|
-
|
150
|
-
{"terms": {"name": ["Fred", "Johny"], "execution": "bool"}}
|
151
|
-
|
152
|
-
{"terms": {"name": ["Fred", "Johny"], "execution": "fielddata"}}
|
153
|
-
```
|
154
|
-
|
155
|
-
```ruby
|
156
|
-
UsersIndex.filter{ name == ['Fred', 'Johny'] }
|
157
|
-
UsersIndex.filter{ name != ['Fred', 'Johny'] }
|
158
|
-
|
159
|
-
UsersIndex.filter{ name(:|) == ['Fred', 'Johny'] }
|
160
|
-
UsersIndex.filter{ name(:or) == ['Fred', 'Johny'] }
|
161
|
-
UsersIndex.filter{ name(execution: :or) == ['Fred', 'Johny'] }
|
162
|
-
|
163
|
-
UsersIndex.filter{ name(:&) == ['Fred', 'Johny'] }
|
164
|
-
UsersIndex.filter{ name(:and) == ['Fred', 'Johny'] }
|
165
|
-
UsersIndex.filter{ name(execution: :and) == ['Fred', 'Johny'] }
|
166
|
-
|
167
|
-
UsersIndex.filter{ name(:b) == ['Fred', 'Johny'] }
|
168
|
-
UsersIndex.filter{ name(:bool) == ['Fred', 'Johny'] }
|
169
|
-
UsersIndex.filter{ name(execution: :bool) == ['Fred', 'Johny'] }
|
170
|
-
|
171
|
-
UsersIndex.filter{ name(:f) == ['Fred', 'Johny'] }
|
172
|
-
UsersIndex.filter{ name(:fielddata) == ['Fred', 'Johny'] }
|
173
|
-
UsersIndex.filter{ name(execution: :fielddata) == ['Fred', 'Johny'] }
|
174
|
-
```
|
175
|
-
|
176
|
-
* Regexp filter (== and =~ are equivalent)
|
177
|
-
|
178
|
-
```json
|
179
|
-
{"regexp": {"name.first": "s.*y"}}
|
180
|
-
|
181
|
-
{"not": {"regexp": {"name.first": "s.*y"}}}
|
182
|
-
|
183
|
-
{"regexp": {"name.first": {"value": "s.*y", "flags": "ANYSTRING|INTERSECTION"}}}
|
184
|
-
```
|
185
|
-
|
186
|
-
```ruby
|
187
|
-
UsersIndex.filter{ name.first == /s.*y/ }
|
188
|
-
UsersIndex.filter{ name.first =~ /s.*y/ }
|
189
|
-
|
190
|
-
UsersIndex.filter{ name.first != /s.*y/ }
|
191
|
-
UsersIndex.filter{ name.first !~ /s.*y/ }
|
192
|
-
|
193
|
-
UsersIndex.filter{ name.first(:anystring, :intersection) == /s.*y/ }
|
194
|
-
UsersIndex.filter{ name.first(flags: [:anystring, :intersection]) == /s.*y/ }
|
195
|
-
```
|
196
|
-
|
197
|
-
* Prefix filter
|
198
|
-
|
199
|
-
```json
|
200
|
-
{"prefix": {"name": "Fre"}}
|
201
|
-
{"not": {"prefix": {"name": "Joh"}}}
|
202
|
-
```
|
203
|
-
|
204
|
-
```ruby
|
205
|
-
UsersIndex.filter{ name =~ re' }
|
206
|
-
UsersIndex.filter{ name !~ 'Joh' }
|
207
|
-
```
|
208
|
-
|
209
|
-
* Exists filter
|
210
|
-
|
211
|
-
```json
|
212
|
-
{"exists": {"field": "name"}}
|
213
|
-
```
|
214
|
-
|
215
|
-
```ruby
|
216
|
-
UsersIndex.filter{ name? }
|
217
|
-
UsersIndex.filter{ !!name }
|
218
|
-
UsersIndex.filter{ !!name? }
|
219
|
-
UsersIndex.filter{ name != nil }
|
220
|
-
UsersIndex.filter{ !(name == nil) }
|
221
|
-
```
|
222
|
-
|
223
|
-
* Missing filter
|
224
|
-
|
225
|
-
```json
|
226
|
-
{"missing": {"field": "name", "existence": true, "null_value": false}}
|
227
|
-
{"missing": {"field": "name", "existence": true, "null_value": true}}
|
228
|
-
{"missing": {"field": "name", "existence": false, "null_value": true}}
|
229
|
-
```
|
230
|
-
|
231
|
-
```ruby
|
232
|
-
UsersIndex.filter{ !name }
|
233
|
-
UsersIndex.filter{ !name? }
|
234
|
-
UsersIndex.filter{ name == nil }
|
235
|
-
```
|
236
|
-
|
237
|
-
* Range
|
238
|
-
|
239
|
-
```json
|
240
|
-
{"range": {"age": {"gt": 42}}}
|
241
|
-
{"range": {"age": {"gte": 42}}}
|
242
|
-
{"range": {"age": {"lt": 42}}}
|
243
|
-
{"range": {"age": {"lte": 42}}}
|
244
|
-
|
245
|
-
{"range": {"age": {"gt": 40, "lt": 50}}}
|
246
|
-
{"range": {"age": {"gte": 40, "lte": 50}}}
|
247
|
-
|
248
|
-
{"range": {"age": {"gt": 40, "lte": 50}}}
|
249
|
-
{"range": {"age": {"gte": 40, "lt": 50}}}
|
250
|
-
```
|
251
|
-
|
252
|
-
```ruby
|
253
|
-
UsersIndex.filter{ age > 42 }
|
254
|
-
UsersIndex.filter{ age >= 42 }
|
255
|
-
UsersIndex.filter{ age < 42 }
|
256
|
-
UsersIndex.filter{ age <= 42 }
|
257
|
-
|
258
|
-
UsersIndex.filter{ age == (40..50) }
|
259
|
-
UsersIndex.filter{ (age > 40) & (age < 50) }
|
260
|
-
UsersIndex.filter{ age == [40..50] }
|
261
|
-
UsersIndex.filter{ (age >= 40) & (age <= 50) }
|
262
|
-
|
263
|
-
UsersIndex.filter{ (age > 40) & (age <= 50) }
|
264
|
-
UsersIndex.filter{ (age >= 40) & (age < 50) }
|
265
|
-
```
|
266
|
-
|
267
|
-
* Bool filter
|
268
|
-
|
269
|
-
```json
|
270
|
-
{"bool": {
|
271
|
-
"must": [{"term": {"name": "Name"}}],
|
272
|
-
"should": [{"term": {"age": 42}}, {"term": {"age": 45}}]
|
273
|
-
}}
|
274
|
-
```
|
275
|
-
|
276
|
-
```ruby
|
277
|
-
UsersIndex.filter{ must(name == 'Name').should(age == 42, age == 45) }
|
278
|
-
```
|
279
|
-
|
280
|
-
* And filter
|
281
|
-
|
282
|
-
```json
|
283
|
-
{"and": [{"term": {"name": "Name"}}, {"range": {"age": {"lt": 42}}}]}
|
284
|
-
```
|
285
|
-
|
286
|
-
```ruby
|
287
|
-
UsersIndex.filter{ (name == 'Name') & (age < 42) }
|
288
|
-
```
|
289
|
-
|
290
|
-
* Or filter
|
291
|
-
|
292
|
-
```json
|
293
|
-
{"or": [{"term": {"name": "Name"}}, {"range": {"age": {"lt": 42}}}]}
|
294
|
-
```
|
295
|
-
|
296
|
-
```ruby
|
297
|
-
UsersIndex.filter{ (name == 'Name') | (age < 42) }
|
298
|
-
```
|
299
|
-
|
300
|
-
```json
|
301
|
-
{"not": {"term": {"name": "Name"}}}
|
302
|
-
{"not": {"range": {"age": {"lt": 42}}}}
|
303
|
-
```
|
304
|
-
|
305
|
-
```ruby
|
306
|
-
UsersIndex.filter{ !(name == 'Name') } # or UsersIndex.filter{ name != 'Name' }
|
307
|
-
UsersIndex.filter{ !(age < 42) }
|
308
|
-
```
|
309
|
-
|
310
|
-
* Match all filter
|
311
|
-
|
312
|
-
```json
|
313
|
-
{"match_all": {}}
|
314
|
-
```
|
315
|
-
|
316
|
-
```ruby
|
317
|
-
UsersIndex.filter{ match_all }
|
318
|
-
```
|
319
|
-
|
320
|
-
* Has child filter
|
321
|
-
|
322
|
-
```json
|
323
|
-
{"has_child": {"type": "blog_tag", "query": {"term": {"tag": "something"}}}
|
324
|
-
{"has_child": {"type": "comment", "filter": {"term": {"user": "john"}}}
|
325
|
-
```
|
326
|
-
|
327
|
-
```ruby
|
328
|
-
UsersIndex.filter{ has_child(:blog_tag).query(term: {tag: 'something'}) }
|
329
|
-
UsersIndex.filter{ has_child(:comment).filter{ user == 'john' } }
|
330
|
-
```
|
331
|
-
|
332
|
-
* Has parent filter
|
333
|
-
|
334
|
-
```json
|
335
|
-
{"has_parent": {"type": "blog", "query": {"term": {"tag": "something"}}}}
|
336
|
-
{"has_parent": {"type": "blog", "filter": {"term": {"text": "bonsai three"}}}}
|
337
|
-
```
|
338
|
-
|
339
|
-
```ruby
|
340
|
-
UsersIndex.filter{ has_parent(:blog).query(term: {tag: 'something'}) }
|
341
|
-
UsersIndex.filter{ has_parent(:blog).filter{ text == 'bonsai three' } }
|
342
|
-
```
|
343
|
-
|
344
|
-
See [filters.rb](lib/chewy/query/filters.rb) for more details.
|
345
|
-
|
346
|
-
## Faceting
|
347
|
-
|
348
|
-
Facets are an optional sidechannel you can request from Elasticsearch describing certain fields of the resulting collection. The most common use for facets is to allow the user to continue filtering specifically within the subset, as opposed to the global index.
|
349
|
-
|
350
|
-
For instance, let's request the `country` field as a facet along with our users collection. We can do this with the #facets method like so:
|
351
|
-
|
352
|
-
```ruby
|
353
|
-
UsersIndex.filter{ [...] }.facets({countries: {terms: {field: 'country'}}})
|
354
|
-
```
|
355
|
-
|
356
|
-
Let's look at what we asked from Elasticsearch. The facets setter method accepts a hash. You can choose custom/semantic key names for this hash for your own convenience (in this case I used the plural version of the actual field), in our case `countries`. The following nested hash tells ES to grab and aggregate values (terms) from the `country` field on our indexed records.
|
357
|
-
|
358
|
-
The response will include the `:facets` sidechannel:
|
359
|
-
|
360
|
-
```
|
361
|
-
< { ... ,"facets":{"countries":{"_type":"terms","missing":?,"total":?,"other":?,"terms":[{"term":"USA","count":?},{"term":"Brazil","count":?}, ...}}
|
362
|
-
```
|
363
|
-
|
364
|
-
## Aggregations
|
365
|
-
|
366
|
-
Aggregations are part of the optional sidechannel that can be requested with a query.
|
367
|
-
|
368
|
-
You interact with aggregations using the composable #aggregations method (or its alias #aggs)
|
369
|
-
|
370
|
-
Let's look at an example.
|
371
|
-
|
372
|
-
```ruby
|
373
|
-
class UsersIndex < Chewy::Index
|
374
|
-
define_type User do
|
375
|
-
field :name
|
376
|
-
field :rating
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
all_johns = UsersIndex::User.filter { name == 'john' }.aggs({ avg_rating: { avg: { field: 'rating' } } })
|
381
|
-
|
382
|
-
avg_johns_rating = all_johns.aggs
|
383
|
-
# => {"avg_rating"=>{"value"=>3.5}}
|
384
|
-
```
|
385
|
-
|
386
|
-
It is convenient to name aggregations that you intend to reuse regularly. This is achieve with the .aggregation method,
|
387
|
-
which is also available under the .agg alias method.
|
388
|
-
|
389
|
-
Here's the same example from before
|
390
|
-
|
391
|
-
```ruby
|
392
|
-
class UsersIndex < Chewy::Index
|
393
|
-
define_type User do
|
394
|
-
field :name
|
395
|
-
field :rating, type: "long"
|
396
|
-
agg :avg_rating do
|
397
|
-
{ avg: { field: 'rating' } }
|
398
|
-
end
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
all_johns = UsersIndex::User.filter { name == 'john' }.aggs(:avg_rating)
|
403
|
-
|
404
|
-
avg_johns_rating = all_johns.aggs
|
405
|
-
# => {"avg_rating"=>{"value"=>3.5}}
|
406
|
-
```
|
407
|
-
|
408
|
-
It is possible to run into collisions between named aggregations. This occurs when there is more than one aggregation
|
409
|
-
with the same name. To explicitly reference an aggregation you provide a string to the #aggs method of the form:
|
410
|
-
`index_name#document_type.aggregation_name`
|
411
|
-
|
412
|
-
Consider this example where there are two separate aggregations named `avg_rating`
|
413
|
-
|
414
|
-
```ruby
|
415
|
-
class UsersIndex < Chewy::Index
|
416
|
-
define_type User do
|
417
|
-
field :name
|
418
|
-
field :rating, type: "long"
|
419
|
-
agg :avg_rating do
|
420
|
-
{ avg: { field: 'rating' } }
|
421
|
-
end
|
422
|
-
end
|
423
|
-
define_type Post do
|
424
|
-
field :title
|
425
|
-
field :body
|
426
|
-
field :comments do
|
427
|
-
field :message
|
428
|
-
field :rating, type: "long"
|
429
|
-
end
|
430
|
-
agg :avg_rating do
|
431
|
-
{ avg: { field: 'comments.rating' } }
|
432
|
-
end
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
all_docs = UsersIndex.filter {match_all}.aggs("users#user.avg_rating")
|
437
|
-
all_docs.aggs
|
438
|
-
# => {"users#user.avg_rating"=>{"value"=>3.5}}
|
439
|
-
```
|
440
|
-
|
441
|
-
## Script fields
|
442
|
-
|
443
|
-
Script fields allow you to execute Elasticsearch's scripting languages such as groovy and javascript. More about supported languages and what scripting is [here](https://www.elastic.co/guide/en/elasticsearch/reference/0.90/modules-scripting.html). This feature allows you to calculate the distance between geo points, for example. This is how to use the DSL:
|
444
|
-
|
445
|
-
```ruby
|
446
|
-
UsersIndex.script_fields(
|
447
|
-
distance: {
|
448
|
-
params: {
|
449
|
-
lat: 37.569976,
|
450
|
-
lon: -122.351591
|
451
|
-
},
|
452
|
-
script: "doc['coordinates'].distanceInMiles(lat, lon)"
|
453
|
-
}
|
454
|
-
)
|
455
|
-
```
|
456
|
-
Here, `coordinates` is a field with type `geo_point`. There will be a `distance` field for the index's model in the search result.
|
457
|
-
|
458
|
-
## Script scoring
|
459
|
-
|
460
|
-
Script scoring is used to score the search results. All scores are added to the search request and combined according to boost mode and score mode. This can be useful if, for example, a score function is computationally expensive and it is sufficient to compute the score on a filtered set of documents. For example, you might want to multiply the score by another numeric field in the doc:
|
461
|
-
|
462
|
-
```ruby
|
463
|
-
UsersIndex.script_score("_score * doc['my_numeric_field'].value")
|
464
|
-
```
|
465
|
-
|
466
|
-
## Boost Factor
|
467
|
-
|
468
|
-
Boost factors are a way to add a boost to a query where documents match the filter. If you have some users who are experts and some who are regular users, you might want to give the experts a higher score and boost to the top of the search results. You can accomplish this by using the #boost_factor method and adding a boost score of 5 for an expert user:
|
469
|
-
|
470
|
-
```ruby
|
471
|
-
UsersIndex.boost_factor(5, filter: {term: {type: 'Expert'}})
|
472
|
-
```
|
473
|
-
|
474
|
-
## Objects loading
|
475
|
-
|
476
|
-
It is possible to load source objects from the database for every search result:
|
477
|
-
|
478
|
-
```ruby
|
479
|
-
scope = UsersIndex.filter(range: {rating: {gte: 100}})
|
480
|
-
|
481
|
-
scope.load # => scope is marked to return User instances array
|
482
|
-
scope.load.query(...) # => since objects are loaded lazily you can complete scope
|
483
|
-
scope.load(user: { scope: ->{ includes(:country) }}) # you can also pass loading scopes for each
|
484
|
-
# possibly returned type
|
485
|
-
scope.load(user: { scope: User.includes(:country) }) # the second scope passing way.
|
486
|
-
scope.load(scope: ->{ includes(:country) }) # and more common scope applied to every loaded object type.
|
487
|
-
|
488
|
-
scope.only(:id).load # it is optimal to request ids only if you are not planning to use type objects
|
489
|
-
```
|
490
|
-
|
491
|
-
The `preload` method takes the same options as `load` and ORM/ODM objects will be loaded, but the scope will still return an array of Chewy wrappers. To access real objects use the `_object` wrapper method:
|
492
|
-
|
493
|
-
```ruby
|
494
|
-
UsersIndex.filter(range: {rating: {gte: 100}}).preload(...).query(...).map(&:_object)
|
495
|
-
```
|
496
|
-
|
497
|
-
See [loading.rb](lib/chewy/query/loading.rb) for more details.
|