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 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
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+ - 2.1.0
5
+ - 2.0.0
6
+ addons:
7
+ code_climate:
8
+ repo_token: d69d849c7542eec3fd29893f6935c0bff0947376440fec1a5098dc7afb69cad5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elasticquery.gemspec
4
+ gemspec
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
+ [![Build Status](https://travis-ci.org/caulfield/elasticquery.svg?branch=master)](https://travis-ci.org/caulfield/elasticquery)
4
+ [![Code Climate](https://codeclimate.com/github/caulfield/elasticquery/badges/gpa.svg)](https://codeclimate.com/github/caulfield/elasticquery)
5
+ [![Test Coverage](https://codeclimate.com/github/caulfield/elasticquery/badges/coverage.svg)](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
@@ -0,0 +1,6 @@
1
+ require "elasticquery/version"
2
+
3
+ # Base gem module
4
+ module Elasticquery
5
+ autoload :Base, 'elasticquery/base'
6
+ end
@@ -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,18 @@
1
+ module Elasticquery
2
+ module Filters
3
+ class Base
4
+
5
+ def valid?
6
+ true
7
+ end
8
+
9
+ def invalid?
10
+ !valid?
11
+ end
12
+
13
+ def dup_with(*args)
14
+ self.class.new *args
15
+ end
16
+ end
17
+ end
18
+ 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