elasticquery 0.1.0
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 +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +165 -0
- data/Rakefile +21 -0
- data/elasticquery.gemspec +29 -0
- data/lib/elasticquery.rb +6 -0
- data/lib/elasticquery/base.rb +80 -0
- data/lib/elasticquery/filters/base.rb +18 -0
- data/lib/elasticquery/filters/not.rb +30 -0
- data/lib/elasticquery/filters/range.rb +29 -0
- data/lib/elasticquery/filters/search.rb +53 -0
- data/lib/elasticquery/filters/term.rb +41 -0
- data/lib/elasticquery/queries/all.rb +34 -0
- data/lib/elasticquery/query.rb +72 -0
- data/lib/elasticquery/version.rb +4 -0
- data/test/base_test.rb +66 -0
- data/test/filters/not_test.rb +55 -0
- data/test/filters/range_test.rb +44 -0
- data/test/filters/search_test.rb +57 -0
- data/test/filters/term_test.rb +37 -0
- data/test/integration/chainable_call_test.rb +31 -0
- data/test/integration/not_case_test.rb +28 -0
- data/test/integration/queries_inheritence_test.rb +64 -0
- data/test/integration/range_case_test.rb +25 -0
- data/test/integration/search_case_test.rb +48 -0
- data/test/integration/term_case_test.rb +24 -0
- data/test/queries/all_test.rb +41 -0
- data/test/query_test.rb +70 -0
- data/test/test_helper.rb +8 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c617336d69d42299b02a328f05fa72f498320844
|
4
|
+
data.tar.gz: 83f604e40b5c09af43cce4ce10b67b48d308ea39
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2ec816c91083db94db3028071bccb70fea89ded4e4fb629866b007e8e72aa8fbaaefdc885d735e18033b32b1a3337236822aabce4ad1172fd9f07f91129d61f7
|
7
|
+
data.tar.gz: a49a99f48a728848c36995306d6739ad30d78614460b5a655ba487646a085a7b704313824299a0fd65f224f09aeab559aefc63aaba2d4f89d3516035e6abee6b
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Sergey Kuchmistov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# Elasticquery
|
2
|
+
|
3
|
+
[](https://travis-ci.org/caulfield/elasticquery)
|
4
|
+
[](https://codeclimate.com/github/caulfield/elasticquery)
|
5
|
+
[](https://codeclimate.com/github/caulfield/elasticquery)
|
6
|
+
|
7
|
+
A module for elasticquery ruby [libraries][elasticsearch_rails] for using user-friendly query generator. [Click here to view demo with code examples.][demo]
|
8
|
+
|
9
|
+
## Instalation
|
10
|
+
|
11
|
+
To install using [Bundler][bundler] grab the latest stable version:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'elasticquery'
|
15
|
+
```
|
16
|
+
To manually install `elasticquery` via [Rubygems][rubygems] simply gem install:
|
17
|
+
|
18
|
+
```bash
|
19
|
+
gem install elasticquery
|
20
|
+
```
|
21
|
+
|
22
|
+
## Getting Started
|
23
|
+
### First instruction
|
24
|
+
|
25
|
+
Elasticsearch was designed to be customized as you need to. Providing simple methods it allows you to build power and flexible form objects. For example:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class MyQuery < Elasticquery::Base
|
29
|
+
filtered do |params|
|
30
|
+
search params[:query]
|
31
|
+
term user_id: params[:user_id] if params[:user_id].present?
|
32
|
+
range.not :age, gte: 18
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
Then use it
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
query = MyQuery.new query: 'i typed', user_id: 5
|
41
|
+
query.build # => query for elasticsearch
|
42
|
+
Article.search query.build # => be happy
|
43
|
+
```
|
44
|
+
### Currently you have
|
45
|
+
|
46
|
+
1. [Term][es_term] filter. [Usage][term_examples]
|
47
|
+
2. [MultiMatch][es_search] filter. [Usage][search_examples]
|
48
|
+
3. [Range][es_range] filter. [Usage][range_examples]
|
49
|
+
|
50
|
+
#### Note! After first releases of elasticuqery it has scarce support of options and methods. Pull requests and issues with your ideas are welcome!
|
51
|
+
|
52
|
+
### Extended instruction
|
53
|
+
There are multiple ways to organize your query, using chaining calls, or custom filters. Better custom filters support in progress now.
|
54
|
+
|
55
|
+
- Chain calls
|
56
|
+
```ruby
|
57
|
+
PeopleQuery.new.search('hi', operator: :or).term(age: 21).build # => es-ready query
|
58
|
+
```
|
59
|
+
- Class methods
|
60
|
+
```ruby
|
61
|
+
class PeopleQuery < Elasticquery::Base
|
62
|
+
filtered do |params|
|
63
|
+
range :age, lte: prepare_age(params[:max_age])
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def prepare_age(param)
|
69
|
+
param.to_i
|
70
|
+
end
|
71
|
+
end
|
72
|
+
PeopleQuery.build(max_age: '42') # => es-ready
|
73
|
+
```
|
74
|
+
- Multiple `filtered` blocks
|
75
|
+
```ruby
|
76
|
+
class ChildQuery < Elasticquery::Base
|
77
|
+
filtered do |params|
|
78
|
+
term :'category.id' => params[:category_id]
|
79
|
+
end
|
80
|
+
|
81
|
+
filtered do |params|
|
82
|
+
term :'author.name' => User.find(params[:user_id]).name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
ChildQuery.build({user_id: 1, category_id: 14}) => # ;)
|
86
|
+
|
87
|
+
```
|
88
|
+
- Query inheritance
|
89
|
+
```ruby
|
90
|
+
class ParentQuery < Elasticquery::Base
|
91
|
+
filtered do |params|
|
92
|
+
term :'category.id' => params[:category_id]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ChildQuery < ParentQuery
|
97
|
+
filtered do |params|
|
98
|
+
term :'author.name' => User.find(params[:user_id]).name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
ChildQuery.build({user_id: 1, category_id: 14}) => # the same as in previous example
|
103
|
+
```
|
104
|
+
- Custom filter methods
|
105
|
+
```ruby
|
106
|
+
in progress...
|
107
|
+
```
|
108
|
+
|
109
|
+
- Custom filter classes
|
110
|
+
```ruby
|
111
|
+
in progress...
|
112
|
+
```
|
113
|
+
|
114
|
+
### Usage
|
115
|
+
#### term
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
# Simple one term filter
|
119
|
+
term category: 'Rock'
|
120
|
+
|
121
|
+
# Multiple filters joined by AND condition
|
122
|
+
term category: 'Soul', user: 'Aaron'
|
123
|
+
|
124
|
+
# Term exclusion
|
125
|
+
term.not category: 'Rap'
|
126
|
+
```
|
127
|
+
|
128
|
+
#### search (multimatch)
|
129
|
+
```ruby
|
130
|
+
# Simple all fields search in your index
|
131
|
+
search 'developers'
|
132
|
+
|
133
|
+
# The same as above
|
134
|
+
search 'developers', fields: "_all", operator: "and", type: "best_fields"
|
135
|
+
|
136
|
+
# Configure fields
|
137
|
+
search 'Jordan', fields: ['first_name', 'last_name'], operator: "or"
|
138
|
+
```
|
139
|
+
|
140
|
+
#### range
|
141
|
+
```ruby
|
142
|
+
# One side range
|
143
|
+
range :age, gte: 18
|
144
|
+
|
145
|
+
# Double sides range
|
146
|
+
range :volume, gte: 1, lte: 100
|
147
|
+
|
148
|
+
# Range exclusion
|
149
|
+
range.not :size, gte: 32, lte: 128
|
150
|
+
```
|
151
|
+
|
152
|
+
## Contributing
|
153
|
+
1. I'm happy to see any method you can be part of this.
|
154
|
+
|
155
|
+
|
156
|
+
[elasticsearch_rails]: https://github.com/elasticsearch/elasticsearch-rails
|
157
|
+
[demo]: http://elasticquery-demo.herokuapp.com
|
158
|
+
[bundler]: http://bundler.io/
|
159
|
+
[rubygems]: https://rubygems.org/
|
160
|
+
[es_term]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-term-filter.html
|
161
|
+
[es_search]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html
|
162
|
+
[es_range]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-range-query.html
|
163
|
+
[term_examples]: #
|
164
|
+
[search_examples]: #
|
165
|
+
[range_examples]: #
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'yard'
|
4
|
+
|
5
|
+
desc "Run unit tests"
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << 'lib'
|
8
|
+
t.libs << 'test'
|
9
|
+
t.pattern = "test/**/*_test.rb"
|
10
|
+
# t.verbose = true
|
11
|
+
# t.warning = false
|
12
|
+
end
|
13
|
+
|
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
|
+
task default: :test
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'elasticquery/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "elasticquery"
|
8
|
+
spec.version = Elasticquery::VERSION
|
9
|
+
spec.authors = ["Sergey Kuchmistov"]
|
10
|
+
spec.email = ["sergey.kuchmistov@gmail.com"]
|
11
|
+
spec.summary = %q{Elasticsearch query builder.}
|
12
|
+
spec.description = %q{Powerful and flexibal query factory for you elasticsearch&ruby application}
|
13
|
+
spec.homepage = "https://github.com/caulfield/elasticquery"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
+
spec.add_development_dependency "yard"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "minitest"
|
27
|
+
spec.add_development_dependency "minitest-reporters"
|
28
|
+
spec.add_development_dependency "codeclimate-test-reporter"
|
29
|
+
end
|
data/lib/elasticquery.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
require 'elasticquery/query'
|
3
|
+
require 'elasticquery/queries/all'
|
4
|
+
|
5
|
+
# Base class for query builder. Superclass of your all builders
|
6
|
+
module Elasticquery
|
7
|
+
class Base
|
8
|
+
include Elasticquery::Queries::All
|
9
|
+
|
10
|
+
class_attribute :filters
|
11
|
+
attr_reader :params, :query
|
12
|
+
|
13
|
+
self.filters = []
|
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
|
+
def self.filtered(&block)
|
30
|
+
self.filters += [block]
|
31
|
+
end
|
32
|
+
|
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
|
+
def filterable?
|
48
|
+
filters.any?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create new query objects with built empty query.
|
52
|
+
def initialize(params = {})
|
53
|
+
@params = params
|
54
|
+
@query = Query.new
|
55
|
+
end
|
56
|
+
|
57
|
+
# Build elasticquery query using defined filters.
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# query.build # => { query: { match_all: {} }
|
61
|
+
#
|
62
|
+
# @return [Hash] elasticqueriable hash
|
63
|
+
def build
|
64
|
+
filters.each { |filter| instance_exec @params, &filter }
|
65
|
+
query.to_hash
|
66
|
+
end
|
67
|
+
|
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
|
+
def self.build(params = {})
|
77
|
+
new(params).build
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module Elasticquery
|
4
|
+
module Filters
|
5
|
+
class Not < Base
|
6
|
+
|
7
|
+
def initialize(condition = {})
|
8
|
+
@condition = condition
|
9
|
+
end
|
10
|
+
|
11
|
+
def dup_with(*args)
|
12
|
+
raise StandardError, 'Cannot use Filters::Not twice'
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
condition = @condition
|
17
|
+
-> do
|
18
|
+
filter = filters.last.dup_with condition
|
19
|
+
if filter.valid?
|
20
|
+
subquery = filter.to_hash
|
21
|
+
q = subquery[:query][:filtered][:filter][:and]
|
22
|
+
{query: {filtered: {filter: {and: {not: {filter: q}}}}}}
|
23
|
+
else
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module Elasticquery
|
4
|
+
module Filters
|
5
|
+
class Range < Base
|
6
|
+
|
7
|
+
def initialize(field, gte: nil, lte: nil)
|
8
|
+
@field, @gte, @lte = field, gte, lte
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
@gte || @lte
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
valid? ? {query: {filtered: {filter: {and: {range: range}}}}} : {}
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def range
|
22
|
+
{@field => {}}.tap do |r|
|
23
|
+
r[@field][:lte] = @lte unless @lte.nil?
|
24
|
+
r[@field][:gte] = @gte unless @gte.nil?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|