elasticquery 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +12 -0
- data/README.md +151 -70
- data/Rakefile +1 -9
- data/elasticquery.gemspec +2 -2
- data/lib/elasticquery.rb +1 -0
- data/lib/elasticquery/base.rb +6 -48
- data/lib/elasticquery/builder.rb +74 -17
- data/lib/elasticquery/es.rb +32 -0
- data/lib/elasticquery/filters/base.rb +4 -0
- data/lib/elasticquery/filters/exists.rb +24 -0
- data/lib/elasticquery/filters/not.rb +7 -11
- data/lib/elasticquery/filters/range.rb +3 -7
- data/lib/elasticquery/filters/term.rb +4 -28
- data/lib/elasticquery/filters/terms.rb +23 -0
- data/lib/elasticquery/queries/base.rb +16 -0
- data/lib/elasticquery/queries/multi_match.rb +34 -0
- data/lib/elasticquery/query.rb +25 -36
- data/lib/elasticquery/version.rb +1 -1
- data/test/base_test.rb +4 -4
- data/test/builder_test.rb +24 -8
- data/test/es_test.rb +23 -0
- data/test/filters/exists_test.rb +30 -0
- data/test/filters/not_test.rb +23 -10
- data/test/filters/range_test.rb +10 -11
- data/test/filters/term_test.rb +6 -7
- data/test/filters/terms_test.rb +50 -0
- data/test/integration/chainable_call_test.rb +6 -6
- data/test/integration/exists_case_test.rb +56 -0
- data/test/integration/not_case_test.rb +11 -5
- data/test/integration/queries_inheritence_test.rb +12 -4
- data/test/integration/range_case_test.rb +4 -2
- data/test/integration/search_case_test.rb +20 -11
- data/test/integration/term_case_test.rb +4 -2
- data/test/integration/terms_case_test.rb +49 -0
- data/test/queries/multi_match_test.rb +49 -0
- data/test/query_test.rb +40 -40
- metadata +26 -27
- data/lib/elasticquery/filters/search.rb +0 -54
- data/test/filters/search_test.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2b9c2cc27bc50734cdc0d3b33140ddf3d4d9ef9
|
4
|
+
data.tar.gz: e59c4f8544a438c6d7ea3761223498a46e6aa3f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f1ca3adbaa442c18253c14c75ba9c07a1f5f697261008fc506292149a587c12679b465f239c399ffe165dcc96aad914d904042af73df753899c9b8afaeb5253
|
7
|
+
data.tar.gz: 2852864ee870296ccbba95835083f1b11c28a42a09f4e20f39fe0109bb89cfed1d4de8e87486902b27d78a932334b2e3a5a6c01f2a90234d462b6ea8eee77a09
|
data/History.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
## Elasticquery (master)
|
2
2
|
|
3
|
+
* Separate all filters to filters and queries. Rename search to multi_match. Change dsl for using explisit rule type.
|
4
|
+
|
5
|
+
* Add `terms` filter
|
6
|
+
|
7
|
+
* Add `exists` filter. `exists.not` works as `missing`.
|
8
|
+
|
9
|
+
* Add `where` alias to `terms`
|
10
|
+
|
11
|
+
* Add `without`/`missing` filter
|
12
|
+
|
13
|
+
* Add `es` shortcut from `Elasticquery::Es` to model chain calls
|
14
|
+
|
3
15
|
## Elasticquery 0.1.2 (16 June 2015)
|
4
16
|
|
5
17
|
* Fix `range.not`. Exception was raised
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
[](https://codeclimate.com/github/caulfield/elasticquery)
|
6
6
|
[](https://codeclimate.com/github/caulfield/elasticquery)
|
7
7
|
|
8
|
-
A module for
|
8
|
+
A module for elasticsearch-rails [libraries][elasticsearch_rails] like as user-friendly query generator.
|
9
9
|
|
10
10
|
## Instalation
|
11
11
|
|
@@ -23,43 +23,148 @@ gem install elasticquery
|
|
23
23
|
## Getting Started
|
24
24
|
### First instruction
|
25
25
|
|
26
|
-
Elasticquery was designed to be customized as you need to. Providing simple methods it allows you to build
|
26
|
+
Elasticquery was designed to be customized as you need to. Providing simple methods it allows you to build flexible queries using [Elastic Query DSL][elastic_query_dsl]
|
27
27
|
|
28
28
|
```ruby
|
29
29
|
class MyQuery < Elasticquery::Base
|
30
30
|
filtered do |params|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
filters do
|
32
|
+
term "user.id" => params[:user_id]
|
33
|
+
range.not :age, gte: 18
|
34
|
+
end
|
35
|
+
queries do
|
36
|
+
multi_match params[:query]
|
37
|
+
end
|
34
38
|
end
|
35
39
|
end
|
36
40
|
```
|
37
41
|
|
38
|
-
Then use it
|
39
|
-
|
40
42
|
```ruby
|
41
43
|
query = MyQuery.new query: 'i typed', user_id: 5
|
42
|
-
query.build # => query for
|
43
|
-
Article.search query.build # =>
|
44
|
+
query.build # => query for elastic
|
45
|
+
Article.search query.build # => returns result
|
44
46
|
```
|
45
|
-
### Currently you have
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
## Currently you have
|
49
|
+
### Filters
|
50
|
+
[Term][es_term]
|
51
|
+
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# Simple one term filter
|
55
|
+
term category: 'Rock'
|
56
|
+
|
57
|
+
# _cache option support
|
58
|
+
term category: 'Soul', _cache: false
|
59
|
+
|
60
|
+
# Blank values are skipped. This query returns all records
|
61
|
+
term name: " "
|
62
|
+
```
|
63
|
+
[Terms][es_terms]
|
64
|
+
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# Standard terms options
|
68
|
+
terms a: 1, b: 2
|
69
|
+
|
70
|
+
# Skip empty values
|
71
|
+
terms a: 1, b: "" # => {a: 1} wil be passed
|
72
|
+
|
73
|
+
# Blank terms are skipped
|
74
|
+
terms a: "", b: nil # => match_all will be executed
|
75
|
+
|
76
|
+
# _cache and execution support
|
77
|
+
terms a: 1, b: 2, _cache: false, execution: "or"
|
78
|
+
|
79
|
+
# where alias. Usable in chain calls
|
80
|
+
where a: 1, b: 2, c: 3
|
81
|
+
```
|
82
|
+
[Range][es_range]
|
83
|
+
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# One side range
|
87
|
+
range :age, gte: 18
|
88
|
+
|
89
|
+
# Double sides range
|
90
|
+
range :volume, gte: 1, lte: 100
|
91
|
+
|
92
|
+
# _cache and execution options support
|
93
|
+
range :volume, gte: 1, lte: 100, _cache: true, execution: "fielddata"
|
94
|
+
```
|
95
|
+
[Exists][es_exists]
|
96
|
+
[Missing][es_missing]
|
97
|
+
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
# Field existence check
|
101
|
+
exists "last_name"
|
102
|
+
missing "last_name"
|
103
|
+
|
104
|
+
# Blank value skipped
|
105
|
+
exists ""
|
106
|
+
missing ""
|
107
|
+
|
108
|
+
# Has with alias
|
109
|
+
with 'first_name'
|
110
|
+
without 'first_name'
|
111
|
+
```
|
112
|
+
[Not][es_not]
|
113
|
+
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# Blank values are skipped. This query returns all records
|
117
|
+
range.not :age, lte: ' ', gte: nil
|
118
|
+
|
119
|
+
# Term exclusion
|
120
|
+
term.not category: 'Rap'
|
121
|
+
|
122
|
+
# Terms exclusion
|
123
|
+
terms.not category: 'Rap', name: "Guf"
|
124
|
+
|
125
|
+
# 'Exists not' uses missing
|
126
|
+
with.not #=> returns missing filter
|
127
|
+
```
|
128
|
+
|
129
|
+
All filters are joined by **AND** filter.
|
130
|
+
## Queries
|
131
|
+
[MultiMatch][es_search]
|
132
|
+
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
# Simple all fields search in your index
|
136
|
+
multi_match 'developers'
|
137
|
+
|
138
|
+
# The same as above
|
139
|
+
multi_match 'developers', fields: "_all", operator: "and", type: "best_fields"
|
140
|
+
|
141
|
+
# Configure fields
|
142
|
+
multi_match 'Jordan', fields: ['first_name', 'last_name'], operator: "or"
|
143
|
+
|
144
|
+
# Blank values are skipped. This query returns all records
|
145
|
+
multi_match ''
|
146
|
+
|
147
|
+
# Alias as search
|
148
|
+
search 'Hello!'
|
149
|
+
```
|
50
150
|
|
51
151
|
### Extended instruction
|
52
|
-
There are multiple ways to organize your query, using chaining calls, or custom filters.
|
152
|
+
There are multiple ways to organize your query, using chaining calls, or custom filters.
|
53
153
|
|
54
154
|
- Chain calls
|
55
155
|
```ruby
|
56
|
-
PeopleQuery.new.
|
156
|
+
PeopleQuery.new.queries.multi_match('hi', operator: :or).filters.term(age: 21).build # => returns hash
|
157
|
+
Query.new.queries./queries-chain/.filters./filters-chain/
|
57
158
|
```
|
159
|
+
|
58
160
|
- Class methods
|
161
|
+
|
59
162
|
```ruby
|
60
163
|
class PeopleQuery < Elasticquery::Base
|
61
164
|
filtered do |params|
|
62
|
-
|
165
|
+
filters do
|
166
|
+
range :age, lte: prepare_age(params[:max_age])
|
167
|
+
end
|
63
168
|
end
|
64
169
|
|
65
170
|
protected
|
@@ -68,95 +173,71 @@ class PeopleQuery < Elasticquery::Base
|
|
68
173
|
param.to_i
|
69
174
|
end
|
70
175
|
end
|
71
|
-
PeopleQuery.build(max_age: '42') # =>
|
176
|
+
PeopleQuery.build(max_age: '42') # => result
|
72
177
|
```
|
178
|
+
|
73
179
|
- Multiple `filtered` blocks
|
180
|
+
|
74
181
|
```ruby
|
75
182
|
class ChildQuery < Elasticquery::Base
|
76
183
|
filtered do |params|
|
77
|
-
|
184
|
+
filters do
|
185
|
+
term :'category.id' => params[:category_id]
|
186
|
+
end
|
78
187
|
end
|
79
188
|
|
80
189
|
filtered do |params|
|
81
|
-
|
190
|
+
filters do
|
191
|
+
term :'author.id' => User.find(params[:user_id]).name
|
192
|
+
end
|
82
193
|
end
|
83
194
|
end
|
84
|
-
ChildQuery.build({user_id: 1, category_id: 14}) =>
|
85
|
-
|
195
|
+
ChildQuery.build({user_id: 1, category_id: 14}) => returns both user and category filters
|
86
196
|
```
|
197
|
+
|
87
198
|
- Query inheritance
|
199
|
+
|
88
200
|
```ruby
|
89
201
|
class ParentQuery < Elasticquery::Base
|
90
202
|
filtered do |params|
|
91
|
-
|
203
|
+
filters do
|
204
|
+
term :'category.id' => params[:category_id]
|
205
|
+
end
|
92
206
|
end
|
93
207
|
end
|
94
208
|
|
95
209
|
class ChildQuery < ParentQuery
|
96
210
|
filtered do |params|
|
97
|
-
|
211
|
+
filters do
|
212
|
+
term :'author.id' => User.find(params[:user_id]).name
|
213
|
+
end
|
98
214
|
end
|
99
215
|
end
|
100
216
|
|
101
217
|
ChildQuery.build({user_id: 1, category_id: 14}) => # the same as in previous example
|
102
218
|
```
|
103
219
|
|
104
|
-
|
105
|
-
#### term
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
# Simple one term filter
|
109
|
-
term category: 'Rock'
|
110
|
-
|
111
|
-
# Multiple filters joined by AND condition
|
112
|
-
term category: 'Soul', user: 'Aaron'
|
113
|
-
|
114
|
-
# Term exclusion
|
115
|
-
term.not category: 'Rap'
|
116
|
-
|
117
|
-
# Blank values are skipped. This query returns all records
|
118
|
-
term name: " "
|
119
|
-
```
|
220
|
+
- Elasticsearch::Model support with `es` shortcut
|
120
221
|
|
121
|
-
#### search (multimatch)
|
122
222
|
```ruby
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
search 'developers', fields: "_all", operator: "and", type: "best_fields"
|
128
|
-
|
129
|
-
# Configure fields
|
130
|
-
search 'Jordan', fields: ['first_name', 'last_name'], operator: "or"
|
223
|
+
class Article
|
224
|
+
include Elasticsearch::Model
|
225
|
+
extend Elasticquery::Es
|
226
|
+
end
|
131
227
|
|
132
|
-
|
133
|
-
search
|
228
|
+
Article.es.filters.term(user_id: 12).with("published_at").queries.search("Verge").results # => collection of "hits"
|
229
|
+
Article.es.filters.term(user_id: 12).with("published_at").queries.search("Verge").records # => collection of records from db
|
134
230
|
```
|
135
231
|
|
136
|
-
#### range
|
137
|
-
```ruby
|
138
|
-
# One side range
|
139
|
-
range :age, gte: 18
|
140
|
-
|
141
|
-
# Double sides range
|
142
|
-
range :volume, gte: 1, lte: 100
|
143
|
-
|
144
|
-
# Range exclusion
|
145
|
-
range.not :size, gte: 32, lte: 128
|
146
|
-
|
147
|
-
# Blank values are skipped. This query returns all records
|
148
|
-
range.not :age, lte: ' ', gte: nil
|
149
|
-
|
150
|
-
# _cache and execution options support
|
151
|
-
range :volume, gte: 1, lte: 100, _cache: true, execution: "fielddata"
|
152
|
-
```
|
153
232
|
[elasticsearch_rails]: https://github.com/elasticsearch/elasticsearch-rails
|
154
233
|
[demo]: http://elasticquery-demo.herokuapp.com
|
155
234
|
[bundler]: http://bundler.io/
|
156
235
|
[rubygems]: https://rubygems.org/
|
157
236
|
[es_term]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-term-filter.html
|
237
|
+
[es_terms]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-terms-filter.html
|
238
|
+
[es_not]: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-not-filter.html
|
239
|
+
[es_exists]: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-filter.html
|
240
|
+
[es_missing]: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-missing-filter.html
|
158
241
|
[es_search]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html
|
159
242
|
[es_range]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-range-query.html
|
160
|
-
[
|
161
|
-
[search_examples]: #
|
162
|
-
[range_examples]: #
|
243
|
+
[elastic_query_dsl]: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
|
data/Rakefile
CHANGED
@@ -1,21 +1,13 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require 'rake/testtask'
|
3
|
-
require 'yard'
|
4
3
|
|
5
4
|
desc "Run unit tests"
|
6
5
|
Rake::TestTask.new do |t|
|
7
6
|
t.libs << 'lib'
|
8
7
|
t.libs << 'test'
|
9
|
-
t.pattern = "test
|
8
|
+
t.pattern = "test/**/**_test.rb"
|
10
9
|
# t.verbose = true
|
11
10
|
# t.warning = false
|
12
11
|
end
|
13
12
|
|
14
|
-
desc "Generate docs"
|
15
|
-
YARD::Rake::YardocTask.new do |t|
|
16
|
-
t.files = ['lib/**/*.rb']
|
17
|
-
t.options = ['--any', '--extra', '--opts']
|
18
|
-
t.stats_options = ['--list-undoc']
|
19
|
-
end
|
20
|
-
|
21
13
|
task default: :test
|
data/elasticquery.gemspec
CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = %q{Powerful and flexible elasticsearch query factory for you ruby application}
|
13
13
|
spec.homepage = "https://github.com/caulfield/elasticquery"
|
14
14
|
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.0.0"
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0")
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
@@ -19,12 +20,11 @@ Gem::Specification.new do |spec|
|
|
19
20
|
spec.require_paths = ["lib"]
|
20
21
|
|
21
22
|
spec.add_dependency "activesupport"
|
22
|
-
spec.add_dependency "deep_merge"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.6"
|
25
|
-
spec.add_development_dependency "yard"
|
26
25
|
spec.add_development_dependency "rake"
|
27
26
|
spec.add_development_dependency "minitest"
|
28
27
|
spec.add_development_dependency "minitest-reporters"
|
29
28
|
spec.add_development_dependency "codeclimate-test-reporter"
|
29
|
+
spec.add_development_dependency "elasticsearch-model"
|
30
30
|
end
|
data/lib/elasticquery.rb
CHANGED
data/lib/elasticquery/base.rb
CHANGED
@@ -7,72 +7,30 @@ module Elasticquery
|
|
7
7
|
class Base
|
8
8
|
include Elasticquery::Builder
|
9
9
|
|
10
|
-
class_attribute :
|
10
|
+
class_attribute :rules
|
11
11
|
attr_reader :params, :query
|
12
12
|
|
13
|
-
self.
|
13
|
+
self.rules = []
|
14
14
|
|
15
|
-
# Define params processor for es query.
|
16
|
-
#
|
17
|
-
# @note Multiple filtered blocks can be defined.
|
18
|
-
#
|
19
|
-
# @yield [params] Block with defined filters for passed yield param processing
|
20
|
-
#
|
21
|
-
# @yieldparam [Hash] passed params to processing
|
22
|
-
#
|
23
|
-
# @example query builder for id
|
24
|
-
# class PostQuery < Elasticquery::Base
|
25
|
-
# filtered do |params|
|
26
|
-
# term "id" => params[:id]
|
27
|
-
# end
|
28
|
-
# end
|
29
15
|
def self.filtered(&block)
|
30
|
-
self.
|
16
|
+
self.rules += [block]
|
31
17
|
end
|
32
18
|
|
33
|
-
# Is your object can process params to elasticqueriable.
|
34
|
-
#
|
35
|
-
# @return [Boolean]
|
36
|
-
#
|
37
|
-
# @example
|
38
|
-
# class EmptyQuery < Elasticquery::Base
|
39
|
-
# end
|
40
|
-
# EmptyQuery.new.filterable? #=> false
|
41
|
-
#
|
42
|
-
# @example
|
43
|
-
# class PostQuery < Elasticquery::Base
|
44
|
-
# filtered { |params| true }
|
45
|
-
# end
|
46
|
-
# PostQuery.new.filterable? #=> true
|
47
19
|
def filterable?
|
48
|
-
|
20
|
+
rules.any?
|
49
21
|
end
|
50
22
|
|
51
|
-
# Create new query objects with built empty query.
|
52
23
|
def initialize(params = {})
|
53
24
|
@params = params
|
54
25
|
@query = Query.new
|
55
26
|
end
|
56
27
|
|
57
|
-
# Build elasticquery query using defined filters.
|
58
|
-
#
|
59
|
-
# @example
|
60
|
-
# query.build # => { query: { match_all: {} }
|
61
|
-
#
|
62
|
-
# @return [Hash] elasticqueriable hash
|
63
28
|
def build
|
64
|
-
|
29
|
+
@namespace = nil
|
30
|
+
rules.each { |rule| instance_exec @params, &rule }
|
65
31
|
query.to_hash
|
66
32
|
end
|
67
33
|
|
68
|
-
# Build elasticquery query using defined filters.
|
69
|
-
#
|
70
|
-
# @example
|
71
|
-
# query.build # => { query: { match_all: {} }
|
72
|
-
#
|
73
|
-
# @see Elasticquery::Base#build
|
74
|
-
#
|
75
|
-
# @return [Hash] elasticqueriable hash
|
76
34
|
def self.build(params = {})
|
77
35
|
new(params).build
|
78
36
|
end
|