indexes 0.0.1
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/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
|
+
[](http://badge.fury.io/rb/indexes)
|
2
|
+
[](https://codeclimate.com/github/mmontossi/indexes)
|
3
|
+
[](https://travis-ci.org/mmontossi/indexes)
|
4
|
+
[](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
|