indexes 0.0.1 → 4.0.0.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 +4 -4
- data/README.md +78 -58
- data/lib/generators/{indexes → index}/index_generator.rb +1 -1
- data/lib/generators/{indexes → index}/templates/index.rb +1 -1
- data/lib/generators/indexes/{install_generator.rb → install/install_generator.rb} +1 -1
- data/lib/generators/indexes/{templates → install/templates}/initializer.rb +0 -0
- data/lib/indexes.rb +12 -29
- data/lib/indexes/collection.rb +42 -51
- data/lib/indexes/configuration.rb +1 -1
- data/lib/indexes/definitions.rb +25 -0
- data/lib/indexes/dsl/{serializer.rb → serialization.rb} +3 -3
- data/lib/indexes/index.rb +3 -3
- data/lib/indexes/proxy.rb +2 -2
- data/lib/indexes/railtie.rb +3 -3
- data/lib/indexes/version.rb +1 -1
- data/test/dummy/app/indexes/products_index.rb +2 -2
- data/test/dummy/app/indexes/shops_index.rb +2 -2
- data/test/dummy/config/initializers/indexes.rb +1 -1
- data/test/dummy/log/test.log +2087 -0
- data/test/{generators_test.rb → generator_test.rb} +5 -5
- data/test/{indexes_test.rb → index_test.rb} +2 -2
- data/test/search_test.rb +3 -3
- data/test/{tasks_test.rb → task_test.rb} +1 -1
- metadata +14 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5563006cc938b7b0590e7979fc6c4ef5da6ef18
|
4
|
+
data.tar.gz: 64a3d9f1ced530ead478aae6354d4b374341bbfa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd39393556426f5ffcde4aa3281089dd681b5dfd6eb52ef34270a99ed7a2d12fea661caab391bd32a23f33605aa9fe11c29f5e3add2f34f54d7fadd5b673e0be
|
7
|
+
data.tar.gz: 2e8fb3183fe2713156897df745e5fc392b35f303890639a2112fe9abe35eedd7a47cfc89c0fdd9dddc18ec5c6c6c3c38d8b0c3d9a2c302db53133032ef9e1f5e
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ NOTE: This gem is tested agains version 2.4.
|
|
38
38
|
|
39
39
|
## Configuration
|
40
40
|
|
41
|
-
|
41
|
+
Generate the configuration file:
|
42
42
|
```
|
43
43
|
$ bundle exec rails g indexes:install
|
44
44
|
```
|
@@ -70,43 +70,17 @@ Indexes.configure do |config|
|
|
70
70
|
end
|
71
71
|
end
|
72
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
73
|
end
|
102
74
|
```
|
103
75
|
|
76
|
+
### Definitions
|
77
|
+
|
104
78
|
Generate an index:
|
105
79
|
```
|
106
|
-
$ bundle exec rails g
|
80
|
+
$ bundle exec rails g index products
|
107
81
|
```
|
108
82
|
|
109
|
-
Define the index:
|
83
|
+
Define the mappings, serialization and search in the index:
|
110
84
|
```ruby
|
111
85
|
Indexes.define :products do
|
112
86
|
|
@@ -114,8 +88,8 @@ Indexes.define :products do
|
|
114
88
|
properties :name, :category, :price, :product_suggestions
|
115
89
|
end
|
116
90
|
|
117
|
-
|
118
|
-
|
91
|
+
serialization do |record|
|
92
|
+
extract record, :name, :category, :price
|
119
93
|
product_suggestions do
|
120
94
|
input [record.name, transliterate(record.name)].uniq
|
121
95
|
output record.name
|
@@ -141,97 +115,143 @@ Indexes.define :products do
|
|
141
115
|
end
|
142
116
|
```
|
143
117
|
|
144
|
-
|
118
|
+
If you need to personalize the analysis, you have to add it to the configuration:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
Indexes.configure do |config|
|
122
|
+
|
123
|
+
config.analysis do
|
124
|
+
filter do
|
125
|
+
ngram do
|
126
|
+
type 'nGram'
|
127
|
+
min_gram 2
|
128
|
+
max_gram 20
|
129
|
+
end
|
130
|
+
end
|
131
|
+
analyzer do
|
132
|
+
ngram do
|
133
|
+
type 'custom'
|
134
|
+
tokenizer 'standard'
|
135
|
+
filter %w(lowercase ngram)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
```
|
145
142
|
|
146
143
|
### Indexing
|
147
144
|
|
148
|
-
|
145
|
+
The index will be updated every time a record is created, updated or destroyed:
|
149
146
|
```ruby
|
150
147
|
product = Product.create(name: 'Les Paul', category: 'Gibson')
|
151
148
|
```
|
152
149
|
|
153
|
-
You can force
|
150
|
+
You can force this actions manually with:
|
154
151
|
```ruby
|
155
152
|
product.index
|
156
153
|
product.reindex
|
157
154
|
product.unindex
|
158
155
|
```
|
159
156
|
|
160
|
-
|
161
|
-
```
|
162
|
-
$ bundle exec rake indexes:rebuild
|
163
|
-
```
|
157
|
+
### Rake tasks
|
164
158
|
|
165
|
-
|
159
|
+
At any time you can build/rebuild your indexes using:
|
166
160
|
```
|
167
161
|
$ bundle exec rake indexes:build
|
162
|
+
$ bundle exec rake indexes:rebuild
|
168
163
|
```
|
169
164
|
|
170
165
|
### Search
|
171
166
|
|
172
|
-
|
167
|
+
Use the included search method in the model:
|
173
168
|
```ruby
|
174
|
-
products = Product.search(
|
169
|
+
products = Product.search('Les Paul')
|
175
170
|
```
|
176
171
|
|
177
|
-
|
172
|
+
The result can be used as a collection in views:
|
178
173
|
```erb
|
179
174
|
<%= render products %>
|
180
175
|
```
|
181
176
|
|
182
177
|
### Includes
|
183
178
|
|
184
|
-
|
179
|
+
Similar to using activerecod:
|
185
180
|
```ruby
|
186
|
-
Product.search(
|
181
|
+
Product.search.includes(:shop)
|
187
182
|
```
|
188
183
|
|
189
184
|
### With / Without
|
190
185
|
|
191
186
|
You can force a record to be part of the results by id:
|
192
187
|
```ruby
|
193
|
-
|
188
|
+
Products.search.with(4)
|
194
189
|
```
|
195
190
|
|
196
191
|
Or the opposite:
|
197
192
|
```ruby
|
198
|
-
|
193
|
+
Products.search.without(4)
|
199
194
|
```
|
200
195
|
|
201
196
|
### Pagination
|
202
197
|
|
203
198
|
Works the same as [pagers gem](https://github.com/mmontossi/pagers):
|
204
199
|
```ruby
|
205
|
-
|
200
|
+
Products.search.page(1, padding: 4, length: 30)
|
206
201
|
```
|
207
202
|
|
208
|
-
And you can send the collection directly to the helper
|
203
|
+
And you can send the collection directly to the view helper:
|
209
204
|
```erb
|
210
205
|
<%= paginate products %>
|
211
206
|
```
|
212
207
|
|
213
208
|
### Order
|
214
209
|
|
215
|
-
|
210
|
+
Same as using activerecord:
|
216
211
|
```ruby
|
217
|
-
|
212
|
+
Product.search.order(name: :asc)
|
218
213
|
```
|
219
214
|
|
220
|
-
|
215
|
+
You can use a computed sort by declare it in the configuration:
|
221
216
|
```ruby
|
222
|
-
|
223
|
-
```
|
217
|
+
Indexes.configure do |config|
|
224
218
|
|
225
|
-
|
219
|
+
config.computed_sort :price do |direction|
|
220
|
+
_script do
|
221
|
+
type 'number'
|
222
|
+
script do
|
223
|
+
inline "if (_source.currency == 'UYU') { doc['price'].value * 30 }"
|
224
|
+
end
|
225
|
+
order direction
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
```
|
226
231
|
|
227
232
|
### Suggestions
|
228
233
|
|
229
|
-
|
234
|
+
You need to first define the logic in the configuration:
|
235
|
+
```ruby
|
236
|
+
Indexes.configure do |config|
|
237
|
+
|
238
|
+
config.suggestions do |name, term, options={}|
|
239
|
+
type = name.to_s.singularize
|
240
|
+
text (term || '')
|
241
|
+
completion do
|
242
|
+
field "#{type}_suggestions"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
Then you can get suggestions using the suggest method:
|
230
250
|
```ruby
|
231
251
|
Indexes.suggest :products, 'gibson'
|
232
252
|
```
|
233
253
|
|
234
|
-
|
254
|
+
The result is an array of hashes with a text property:
|
235
255
|
```ruby
|
236
256
|
[{ text: 'Les Paul' }, ...]
|
237
257
|
```
|
File without changes
|
data/lib/indexes.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
require 'generators/indexes/index_generator'
|
2
|
-
require 'generators/indexes/install_generator'
|
3
1
|
require 'indexes/dsl/api'
|
4
2
|
require 'indexes/dsl/mappings'
|
5
3
|
require 'indexes/dsl/search'
|
6
|
-
require 'indexes/dsl/
|
4
|
+
require 'indexes/dsl/serialization'
|
5
|
+
require 'indexes/collection'
|
7
6
|
require 'indexes/concern'
|
8
7
|
require 'indexes/configuration'
|
8
|
+
require 'indexes/definitions'
|
9
9
|
require 'indexes/index'
|
10
10
|
require 'indexes/pagination'
|
11
11
|
require 'indexes/proxy'
|
12
12
|
require 'indexes/railtie'
|
13
|
-
require 'indexes/collection'
|
14
13
|
require 'indexes/version'
|
15
14
|
|
16
15
|
module Indexes
|
@@ -41,22 +40,12 @@ module Indexes
|
|
41
40
|
@configuration ||= Configuration.new
|
42
41
|
end
|
43
42
|
|
44
|
-
def
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
def add(*args)
|
49
|
-
index = Index.new(*args)
|
50
|
-
registry[index.name] = index
|
43
|
+
def definitions
|
44
|
+
@definitions ||= Definitions.new
|
51
45
|
end
|
52
46
|
|
53
|
-
def
|
54
|
-
|
55
|
-
end
|
56
|
-
alias_method :[], :find
|
57
|
-
|
58
|
-
def each(&block)
|
59
|
-
registry.values.sort.each &block
|
47
|
+
def define(*args, &block)
|
48
|
+
Proxy.new *args, &block
|
60
49
|
end
|
61
50
|
|
62
51
|
def build
|
@@ -66,11 +55,7 @@ module Indexes
|
|
66
55
|
body: { settings: configuration.analysis }
|
67
56
|
)
|
68
57
|
end
|
69
|
-
each &:build
|
70
|
-
end
|
71
|
-
|
72
|
-
def exist?(type)
|
73
|
-
client.indices.exists? index: namespace, type: type
|
58
|
+
definitions.each &:build
|
74
59
|
end
|
75
60
|
|
76
61
|
def rebuild
|
@@ -78,6 +63,10 @@ module Indexes
|
|
78
63
|
build
|
79
64
|
end
|
80
65
|
|
66
|
+
def exist?(type)
|
67
|
+
client.indices.exists? index: namespace, type: type
|
68
|
+
end
|
69
|
+
|
81
70
|
def destroy
|
82
71
|
if client.indices.exists?(index: namespace)
|
83
72
|
client.indices.delete index: namespace
|
@@ -92,11 +81,5 @@ module Indexes
|
|
92
81
|
response['suggestions'].first['options'].map &:symbolize_keys
|
93
82
|
end
|
94
83
|
|
95
|
-
private
|
96
|
-
|
97
|
-
def registry
|
98
|
-
@registry ||= {}
|
99
|
-
end
|
100
|
-
|
101
84
|
end
|
102
85
|
end
|
data/lib/indexes/collection.rb
CHANGED
@@ -8,17 +8,29 @@ module Indexes
|
|
8
8
|
|
9
9
|
alias_method :to_ary, :to_a
|
10
10
|
|
11
|
-
def initialize(index,
|
11
|
+
def initialize(index, args=[], options={}, &block)
|
12
12
|
@loaded = false
|
13
13
|
@index = index
|
14
|
-
@options = args.extract_options!
|
15
14
|
@args = args
|
15
|
+
@options = options
|
16
16
|
@block = block
|
17
17
|
end
|
18
18
|
|
19
|
+
def with(ids)
|
20
|
+
chain with: ids
|
21
|
+
end
|
22
|
+
|
23
|
+
def without(ids)
|
24
|
+
chain without: ids
|
25
|
+
end
|
26
|
+
|
27
|
+
def includes(*args)
|
28
|
+
chain includes: args
|
29
|
+
end
|
30
|
+
|
19
31
|
def page(number, options={})
|
20
|
-
length = (
|
21
|
-
padding = (
|
32
|
+
length = page_option(options, :length, 10)
|
33
|
+
padding = page_option(options, :padding, 0)
|
22
34
|
current_page = [number.to_i, 1].max
|
23
35
|
values = Module.new do
|
24
36
|
define_method :page_length do
|
@@ -31,32 +43,28 @@ module Indexes
|
|
31
43
|
current_page
|
32
44
|
end
|
33
45
|
end
|
34
|
-
|
46
|
+
chain(
|
47
|
+
Pagination,
|
48
|
+
values,
|
35
49
|
from: ((length * (current_page - 1)) + padding),
|
36
50
|
size: length
|
37
|
-
|
38
|
-
%i(with without).each do |name|
|
39
|
-
if options.has_key?(name)
|
40
|
-
overrides[name] = options[name]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
chain Pagination, values, overrides
|
51
|
+
)
|
44
52
|
end
|
45
53
|
|
46
54
|
def order(options)
|
47
55
|
mappings = Indexes.configuration.mappings
|
48
|
-
|
56
|
+
values = []
|
49
57
|
options.each do |property, direction|
|
50
58
|
if block = Indexes.configuration.computed_sorts[property]
|
51
|
-
|
59
|
+
values << Dsl::Api.new(direction, &block).to_h
|
52
60
|
elsif property == :id
|
53
|
-
|
61
|
+
values << { _uid: { order: direction } }
|
54
62
|
elsif mappings.has_key?(property) && mappings[property][:type] == 'string'
|
55
|
-
|
63
|
+
values << { "#{property}.raw" => { order: direction } }
|
56
64
|
end
|
57
65
|
end
|
58
|
-
if
|
59
|
-
chain sort:
|
66
|
+
if values.any?
|
67
|
+
chain sort: values
|
60
68
|
else
|
61
69
|
chain
|
62
70
|
end
|
@@ -75,7 +83,7 @@ module Indexes
|
|
75
83
|
@query ||= begin
|
76
84
|
pagination = options.slice(:from, :size, :sort)
|
77
85
|
without_ids = fetch_ids(options[:without])
|
78
|
-
body = Dsl::Search.new(args
|
86
|
+
body = Dsl::Search.new(args, &block).to_h[:query]
|
79
87
|
request = Dsl::Search.new do
|
80
88
|
if without_ids.any?
|
81
89
|
query do
|
@@ -97,9 +105,9 @@ module Indexes
|
|
97
105
|
else
|
98
106
|
query body
|
99
107
|
end
|
100
|
-
%i(from size).each do |
|
101
|
-
if pagination.has_key?(
|
102
|
-
send
|
108
|
+
%i(from size).each do |name|
|
109
|
+
if pagination.has_key?(name)
|
110
|
+
send name, pagination[name]
|
103
111
|
end
|
104
112
|
end
|
105
113
|
if pagination.has_key?(:sort)
|
@@ -122,47 +130,30 @@ module Indexes
|
|
122
130
|
|
123
131
|
def records
|
124
132
|
@records ||= begin
|
133
|
+
hit_ids = response['hits']['hits'].map{ |hit| hit['_id'].to_i }
|
134
|
+
missing_ids = (fetch_ids(options[:with]) - hit_ids)
|
125
135
|
if missing_ids.any?
|
126
136
|
last_index = -(missing_ids.length + 1)
|
127
137
|
ids = (missing_ids.sort.reverse + hit_ids.to(last_index))
|
128
138
|
else
|
129
139
|
ids = hit_ids
|
130
140
|
end
|
141
|
+
includes = options.fetch(:includes, [])
|
131
142
|
index.model.includes(includes).where(id: ids).sort do |a,b|
|
132
143
|
ids.index(a.id) <=> ids.index(b.id)
|
133
144
|
end
|
134
145
|
end
|
135
146
|
end
|
136
147
|
|
137
|
-
def
|
138
|
-
|
139
|
-
end
|
140
|
-
|
141
|
-
def hit_ids
|
142
|
-
@hit_ids ||= response['hits']['hits'].map{ |hit| hit['_id'].to_i }
|
143
|
-
end
|
144
|
-
|
145
|
-
def missing_ids
|
146
|
-
@missing_ids ||= (with_ids - hit_ids)
|
147
|
-
end
|
148
|
-
|
149
|
-
def includes
|
150
|
-
@inclues ||= begin
|
151
|
-
if options.has_key?(:includes)
|
152
|
-
Array options[:includes]
|
153
|
-
else
|
154
|
-
[]
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def fetch_option(options, name)
|
160
|
-
options[name] || begin
|
148
|
+
def page_option(source, name, default)
|
149
|
+
source[name] || begin
|
161
150
|
if Rails.configuration.cache_classes == false
|
162
151
|
Rails.application.eager_load!
|
163
152
|
end
|
164
153
|
if defined?(Pagers)
|
165
|
-
Pagers.
|
154
|
+
Pagers.configuration.send name
|
155
|
+
else
|
156
|
+
default
|
166
157
|
end
|
167
158
|
end
|
168
159
|
end
|
@@ -184,11 +175,11 @@ module Indexes
|
|
184
175
|
|
185
176
|
def chain(*extensions)
|
186
177
|
overrides = extensions.extract_options!
|
187
|
-
Collection.new(index,
|
188
|
-
|
189
|
-
|
190
|
-
end
|
178
|
+
collection = Collection.new(index, args, options.merge(overrides), &block)
|
179
|
+
extensions.each do |extension|
|
180
|
+
collection.extend extension
|
191
181
|
end
|
182
|
+
collection
|
192
183
|
end
|
193
184
|
|
194
185
|
end
|