indexes 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +245 -0
- data/Rakefile +19 -0
- data/lib/generators/indexes/index_generator.rb +15 -0
- data/lib/generators/indexes/install_generator.rb +15 -0
- data/lib/generators/indexes/templates/index.rb +12 -0
- data/lib/generators/indexes/templates/initializer.rb +10 -0
- data/lib/indexes.rb +102 -0
- data/lib/indexes/collection.rb +195 -0
- data/lib/indexes/concern.rb +23 -0
- data/lib/indexes/configuration.rb +39 -0
- data/lib/indexes/dsl/api.rb +85 -0
- data/lib/indexes/dsl/mappings.rb +14 -0
- data/lib/indexes/dsl/search.rb +25 -0
- data/lib/indexes/dsl/serializer.rb +17 -0
- data/lib/indexes/index.rb +149 -0
- data/lib/indexes/pagination.rb +33 -0
- data/lib/indexes/proxy.rb +17 -0
- data/lib/indexes/railtie.rb +15 -0
- data/lib/indexes/version.rb +5 -0
- data/lib/tasks/indexes.rake +11 -0
- data/test/dsl_test.rb +92 -0
- data/test/dummy/Rakefile +5 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/indexes/products_index.rb +51 -0
- data/test/dummy/app/indexes/shops_index.rb +28 -0
- data/test/dummy/app/models/product.rb +5 -0
- data/test/dummy/app/models/shop.rb +5 -0
- data/test/dummy/app/views/layouts/application.html.erb +12 -0
- data/test/dummy/bin/bundle +4 -0
- data/test/dummy/bin/rails +5 -0
- data/test/dummy/bin/rake +5 -0
- data/test/dummy/bin/setup +30 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +25 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +7 -0
- data/test/dummy/config/database.yml.travis +3 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/indexes.rb +67 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/migrate/20161104164148_create_products.rb +14 -0
- data/test/dummy/db/migrate/20161104182219_create_shops.rb +9 -0
- data/test/dummy/db/schema.rb +36 -0
- data/test/dummy/log/development.log +38 -0
- data/test/dummy/log/test.log +408 -0
- data/test/dummy/public/404.html +61 -0
- data/test/dummy/public/422.html +61 -0
- data/test/dummy/public/500.html +60 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/generators_test.rb +25 -0
- data/test/indexes_test.rb +46 -0
- data/test/record_test.rb +25 -0
- data/test/search_test.rb +164 -0
- data/test/tasks_test.rb +25 -0
- data/test/test_helper.rb +14 -0
- metadata +230 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: faa93cb564f7f71a3010f4916dc4d92f81fd594f
|
4
|
+
data.tar.gz: 39befa425667065d4a45ce753f99ecce58258833
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce9a43f8196973a30f58699bc301cad72335aea1d11f2b69ca1a8e6aceab80bd5654044ce2f081cea2ffcd2de4923abfcabbd730c25e8fdc21e77e059fc85aec
|
7
|
+
data.tar.gz: f3ed34a51d459a4b49ca7848d422db677971e33cd7fb3fc4804c107bfc3a886cfaa8015d38ec5ac53c58c937e5b5eca0e36351de42059d2e712ca7996d7286cb
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Mathías Montossi
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/indexes.svg)](http://badge.fury.io/rb/indexes)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/mmontossi/indexes/badges/gpa.svg)](https://codeclimate.com/github/mmontossi/indexes)
|
3
|
+
[![Build Status](https://travis-ci.org/mmontossi/indexes.svg)](https://travis-ci.org/mmontossi/indexes)
|
4
|
+
[![Dependency Status](https://gemnasium.com/mmontossi/indexes.svg)](https://gemnasium.com/mmontossi/indexes)
|
5
|
+
|
6
|
+
# Indexes
|
7
|
+
|
8
|
+
Model search indexes with elasticsearch in rails.
|
9
|
+
|
10
|
+
## Why
|
11
|
+
|
12
|
+
I did this gem to:
|
13
|
+
|
14
|
+
- Gain control of the queries without losing simplicity.
|
15
|
+
- Have out of the box integration with activerecord and pagers.
|
16
|
+
- Deal with the just in time nature of elasticsearch.
|
17
|
+
- Integrate activerecord includes on it.
|
18
|
+
- Have a convention of how to use suggestions.
|
19
|
+
|
20
|
+
## Install
|
21
|
+
|
22
|
+
Put this line in your Gemfile:
|
23
|
+
```ruby
|
24
|
+
gem 'indexes'
|
25
|
+
```
|
26
|
+
|
27
|
+
Then bundle:
|
28
|
+
```
|
29
|
+
$ bundle
|
30
|
+
```
|
31
|
+
|
32
|
+
To install Redis you can use homebrew:
|
33
|
+
```
|
34
|
+
$ brew install elasticsearch24
|
35
|
+
```
|
36
|
+
|
37
|
+
NOTE: This gem is tested agains version 2.4.
|
38
|
+
|
39
|
+
## Configuration
|
40
|
+
|
41
|
+
Run the install generator:
|
42
|
+
```
|
43
|
+
$ bundle exec rails g indexes:install
|
44
|
+
```
|
45
|
+
|
46
|
+
Set the global settings:
|
47
|
+
```ruby
|
48
|
+
Indexes.configure do |config|
|
49
|
+
|
50
|
+
config.hosts = %w(localhost:9200)
|
51
|
+
config.log = false
|
52
|
+
config.trace = false
|
53
|
+
|
54
|
+
config.mappings do
|
55
|
+
name do
|
56
|
+
type 'string'
|
57
|
+
fields do
|
58
|
+
raw do
|
59
|
+
type 'string'
|
60
|
+
index 'not_analyzed'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
category type: 'string'
|
65
|
+
price type: 'long'
|
66
|
+
currency type: 'string'
|
67
|
+
product_suggestions do
|
68
|
+
type 'completion'
|
69
|
+
analyzer 'simple'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
config.analysis do
|
74
|
+
filter do
|
75
|
+
ngram do
|
76
|
+
type 'nGram'
|
77
|
+
min_gram 2
|
78
|
+
max_gram 20
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
config.suggestions do |name, term, options={}|
|
84
|
+
type = name.to_s.singularize
|
85
|
+
text (term || '')
|
86
|
+
completion do
|
87
|
+
field "#{type}_suggestions"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
config.add_computed_sort :price do |direction|
|
92
|
+
_script do
|
93
|
+
type 'number'
|
94
|
+
script do
|
95
|
+
inline "if (_source.currency == 'UYU') { doc['price'].value * 30 }"
|
96
|
+
end
|
97
|
+
order direction
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
Generate an index:
|
105
|
+
```
|
106
|
+
$ bundle exec rails g indexes:index products
|
107
|
+
```
|
108
|
+
|
109
|
+
Define the index:
|
110
|
+
```ruby
|
111
|
+
Indexes.define :products do
|
112
|
+
|
113
|
+
mappings do
|
114
|
+
properties :name, :category, :price, :product_suggestions
|
115
|
+
end
|
116
|
+
|
117
|
+
serializer do |record|
|
118
|
+
set record, :name, :category, :price
|
119
|
+
product_suggestions do
|
120
|
+
input [record.name, transliterate(record.name)].uniq
|
121
|
+
output record.name
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
search do |*args|
|
126
|
+
options = args.extract_options!
|
127
|
+
term = args.first
|
128
|
+
query do
|
129
|
+
if term.present?
|
130
|
+
multi_match do
|
131
|
+
query term
|
132
|
+
type 'phrase_prefix'
|
133
|
+
fields %w(name category)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
match_all
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
## Usage
|
145
|
+
|
146
|
+
### Indexing
|
147
|
+
|
148
|
+
Ocurrs everytime you create, update or destroy a record:
|
149
|
+
```ruby
|
150
|
+
product = Product.create(name: 'Les Paul', category: 'Gibson')
|
151
|
+
```
|
152
|
+
|
153
|
+
You can force it manually by:
|
154
|
+
```ruby
|
155
|
+
product.index
|
156
|
+
product.reindex
|
157
|
+
product.unindex
|
158
|
+
```
|
159
|
+
|
160
|
+
At any time you can force a full rebuild:
|
161
|
+
```
|
162
|
+
$ bundle exec rake indexes:rebuild
|
163
|
+
```
|
164
|
+
|
165
|
+
Or if you need just a build:
|
166
|
+
```
|
167
|
+
$ bundle exec rake indexes:build
|
168
|
+
```
|
169
|
+
|
170
|
+
### Search
|
171
|
+
|
172
|
+
The search parameters are sent to previous configured block:
|
173
|
+
```ruby
|
174
|
+
products = Product.search(name: 'Test')
|
175
|
+
```
|
176
|
+
|
177
|
+
You can use the returned value as a collection in views:
|
178
|
+
```erb
|
179
|
+
<%= render products %>
|
180
|
+
```
|
181
|
+
|
182
|
+
### Includes
|
183
|
+
|
184
|
+
Same as using activerecord relations:
|
185
|
+
```ruby
|
186
|
+
Product.search(includes: :shop)
|
187
|
+
```
|
188
|
+
|
189
|
+
### With / Without
|
190
|
+
|
191
|
+
You can force a record to be part of the results by id:
|
192
|
+
```ruby
|
193
|
+
Product.search(with: 4)
|
194
|
+
```
|
195
|
+
|
196
|
+
Or the opposite:
|
197
|
+
```ruby
|
198
|
+
Product.search(without: 4)
|
199
|
+
```
|
200
|
+
|
201
|
+
### Pagination
|
202
|
+
|
203
|
+
Works the same as [pagers gem](https://github.com/mmontossi/pagers):
|
204
|
+
```ruby
|
205
|
+
products.page 1, padding: 4, length: 30
|
206
|
+
```
|
207
|
+
|
208
|
+
And you can send the collection directly to the helper in views:
|
209
|
+
```erb
|
210
|
+
<%= paginate products %>
|
211
|
+
```
|
212
|
+
|
213
|
+
### Order
|
214
|
+
|
215
|
+
Works the same as in relations:
|
216
|
+
```ruby
|
217
|
+
products.order(name: :asc)
|
218
|
+
```
|
219
|
+
|
220
|
+
To use a computed_sort:
|
221
|
+
```ruby
|
222
|
+
products.order(price: :asc)
|
223
|
+
```
|
224
|
+
|
225
|
+
NOTE: To sort by a string column, you must declare the mapping raw.
|
226
|
+
|
227
|
+
### Suggestions
|
228
|
+
|
229
|
+
The suggestion parameters are sent to previous configured block:
|
230
|
+
```ruby
|
231
|
+
Indexes.suggest :products, 'gibson'
|
232
|
+
```
|
233
|
+
|
234
|
+
Returns array of hashes with a text property:
|
235
|
+
```ruby
|
236
|
+
[{ text: 'Les Paul' }, ...]
|
237
|
+
```
|
238
|
+
|
239
|
+
## Credits
|
240
|
+
|
241
|
+
This gem is maintained and funded by [mmontossi](https://github.com/mmontossi).
|
242
|
+
|
243
|
+
## License
|
244
|
+
|
245
|
+
It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = false
|
16
|
+
t.warning = false
|
17
|
+
end
|
18
|
+
|
19
|
+
task default: :test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Indexes
|
4
|
+
module Generators
|
5
|
+
class IndexGenerator < Rails::Generators::NamedBase
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def create_index_file
|
10
|
+
template 'index.rb', "app/indexes/#{table_name}_index.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Indexes
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def create_initializer_file
|
10
|
+
copy_file 'initializer.rb', 'config/initializers/indexes.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/indexes.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'generators/indexes/index_generator'
|
2
|
+
require 'generators/indexes/install_generator'
|
3
|
+
require 'indexes/dsl/api'
|
4
|
+
require 'indexes/dsl/mappings'
|
5
|
+
require 'indexes/dsl/search'
|
6
|
+
require 'indexes/dsl/serializer'
|
7
|
+
require 'indexes/concern'
|
8
|
+
require 'indexes/configuration'
|
9
|
+
require 'indexes/index'
|
10
|
+
require 'indexes/pagination'
|
11
|
+
require 'indexes/proxy'
|
12
|
+
require 'indexes/railtie'
|
13
|
+
require 'indexes/collection'
|
14
|
+
require 'indexes/version'
|
15
|
+
|
16
|
+
module Indexes
|
17
|
+
class << self
|
18
|
+
|
19
|
+
delegate :any?, :none?, to: :registry
|
20
|
+
|
21
|
+
def namespace
|
22
|
+
"#{Rails.application.class.parent_name} #{Rails.env}".parameterize('_')
|
23
|
+
end
|
24
|
+
|
25
|
+
def client
|
26
|
+
@client ||= begin
|
27
|
+
require 'elasticsearch'
|
28
|
+
Elasticsearch::Client.new(
|
29
|
+
hosts: configuration.hosts,
|
30
|
+
log: configuration.log,
|
31
|
+
trace: configuration.trace
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def configure
|
37
|
+
yield configuration
|
38
|
+
end
|
39
|
+
|
40
|
+
def configuration
|
41
|
+
@configuration ||= Configuration.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def define(*args, &block)
|
45
|
+
Proxy.new *args, &block
|
46
|
+
end
|
47
|
+
|
48
|
+
def add(*args)
|
49
|
+
index = Index.new(*args)
|
50
|
+
registry[index.name] = index
|
51
|
+
end
|
52
|
+
|
53
|
+
def find(name)
|
54
|
+
registry[name]
|
55
|
+
end
|
56
|
+
alias_method :[], :find
|
57
|
+
|
58
|
+
def each(&block)
|
59
|
+
registry.values.sort.each &block
|
60
|
+
end
|
61
|
+
|
62
|
+
def build
|
63
|
+
unless client.indices.exists?(index: namespace)
|
64
|
+
client.indices.create(
|
65
|
+
index: namespace,
|
66
|
+
body: { settings: configuration.analysis }
|
67
|
+
)
|
68
|
+
end
|
69
|
+
each &:build
|
70
|
+
end
|
71
|
+
|
72
|
+
def exist?(type)
|
73
|
+
client.indices.exists? index: namespace, type: type
|
74
|
+
end
|
75
|
+
|
76
|
+
def rebuild
|
77
|
+
destroy
|
78
|
+
build
|
79
|
+
end
|
80
|
+
|
81
|
+
def destroy
|
82
|
+
if client.indices.exists?(index: namespace)
|
83
|
+
client.indices.delete index: namespace
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def suggest(*args)
|
88
|
+
response = client.suggest(
|
89
|
+
index: namespace,
|
90
|
+
body: { suggestions: Dsl::Api.new(args, &configuration.suggestions).to_h }
|
91
|
+
)
|
92
|
+
response['suggestions'].first['options'].map &:symbolize_keys
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def registry
|
98
|
+
@registry ||= {}
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|