searchkick 0.2.2 → 0.2.3
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/.gitignore +1 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +38 -0
- data/README.md +21 -0
- data/lib/searchkick.rb +1 -0
- data/lib/searchkick/model.rb +1 -0
- data/lib/searchkick/reindex.rb +8 -2
- data/lib/searchkick/search.rb +192 -140
- data/lib/searchkick/similar.rb +19 -0
- data/lib/searchkick/tasks.rb +27 -3
- data/lib/searchkick/version.rb +1 -1
- data/test/autocomplete_test.rb +20 -0
- data/test/boost_test.rb +4 -3
- data/test/index_test.rb +11 -2
- data/test/match_test.rb +9 -19
- data/test/similar_test.rb +10 -0
- data/test/sql_test.rb +12 -3
- data/test/test_helper.rb +4 -2
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 275937983ec78c77a0d7f25fa26a9fbe067a5154
|
4
|
+
data.tar.gz: 31783a30cf0b0d6170d94a99daf2c0022d20008d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 636392d9b685813dbdcfd6d8b9f9aa715be73515e089f32a1c95a870c3c0ff17a3a336c3605318b9f8ea6ca406927493752fa6d603762f70a77777de41306144
|
7
|
+
data.tar.gz: 954203fe3c5a0e48d404322c7a270cde965bbd5f69e32e285da9e1582546cd5257bd87b616315c3e997db9402838f4966920fd5c0e10c195502b0b54214a64ff
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
## 0.2.3
|
2
|
+
|
3
|
+
- Setup Travis
|
4
|
+
- Clean old indices before reindex
|
5
|
+
- Search for `*` returns all results
|
6
|
+
- Fixed pagination
|
7
|
+
- Added `similar` method
|
8
|
+
|
9
|
+
## 0.2.2
|
10
|
+
|
11
|
+
- Clean old indices after reindex
|
12
|
+
- More expansions for fuzzy queries
|
13
|
+
|
14
|
+
## 0.2.1
|
15
|
+
|
16
|
+
- Added Rails logger
|
17
|
+
- Only fetch ids when `load: true`
|
18
|
+
|
19
|
+
## 0.2.0
|
20
|
+
|
21
|
+
- Added autocomplete
|
22
|
+
- Added “Did you mean” suggestions
|
23
|
+
- Added personalized searches
|
24
|
+
|
25
|
+
## 0.1.4
|
26
|
+
|
27
|
+
- Bug fix
|
28
|
+
|
29
|
+
## 0.1.3
|
30
|
+
|
31
|
+
- Changed edit distance to one for misspellings
|
32
|
+
- Raise errors when indexing fails
|
33
|
+
- Fixed pagination
|
34
|
+
- Fixed :include option
|
35
|
+
|
36
|
+
## 0.1.2
|
37
|
+
|
38
|
+
- Launch
|
data/README.md
CHANGED
@@ -22,6 +22,8 @@ Plus:
|
|
22
22
|
|
23
23
|
:tangerine: Battle-tested at [Instacart](https://www.instacart.com)
|
24
24
|
|
25
|
+
[](https://travis-ci.org/ankane/searchkick)
|
26
|
+
|
25
27
|
## Get Started
|
26
28
|
|
27
29
|
[Install Elasticsearch](http://www.elasticsearch.org/guide/reference/setup/installation/). For Homebrew, use:
|
@@ -282,6 +284,15 @@ Advanced
|
|
282
284
|
Product.search "2% Milk", facets: {store_id: {where: {in_stock: true}}}
|
283
285
|
```
|
284
286
|
|
287
|
+
### Similar Items
|
288
|
+
|
289
|
+
Find similar items.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
product = Product.first
|
293
|
+
product.similar(fields: ["name"])
|
294
|
+
```
|
295
|
+
|
285
296
|
## Deployment
|
286
297
|
|
287
298
|
Searchkick uses `ENV["ELASTICSEARCH_URL"]` for the Elasticsearch server. This defaults to `http://localhost:9200`.
|
@@ -361,6 +372,12 @@ Do not load models
|
|
361
372
|
Product.search "milk", load: false
|
362
373
|
```
|
363
374
|
|
375
|
+
Reindex all models (Rails only)
|
376
|
+
|
377
|
+
```sh
|
378
|
+
rake searchkick:reindex:all
|
379
|
+
```
|
380
|
+
|
364
381
|
## Migrating from Tire
|
365
382
|
|
366
383
|
1. Change `search` methods to `tire.search` and add index name in existing search calls
|
@@ -411,6 +428,10 @@ Thanks to Karel Minarik for [Tire](https://github.com/karmi/tire), Jaroslav Kali
|
|
411
428
|
|
412
429
|
- Make Searchkick work with any language
|
413
430
|
|
431
|
+
## History
|
432
|
+
|
433
|
+
View the [changelog](https://github.com/ankane/searchkick/blob/master/CHANGELOG.md)
|
434
|
+
|
414
435
|
## Contributing
|
415
436
|
|
416
437
|
1. Fork it
|
data/lib/searchkick.rb
CHANGED
data/lib/searchkick/model.rb
CHANGED
data/lib/searchkick/reindex.rb
CHANGED
@@ -4,9 +4,11 @@ module Searchkick
|
|
4
4
|
# https://gist.github.com/jarosan/3124884
|
5
5
|
def reindex
|
6
6
|
alias_name = tire.index.name
|
7
|
-
new_index = alias_name + "_" + Time.now.strftime("%Y%m%d%H%M%S")
|
7
|
+
new_index = alias_name + "_" + Time.now.strftime("%Y%m%d%H%M%S%L")
|
8
8
|
index = Tire::Index.new(new_index)
|
9
9
|
|
10
|
+
clean_indices
|
11
|
+
|
10
12
|
success = index.create searchkick_index_options
|
11
13
|
raise index.response.to_s if !success
|
12
14
|
|
@@ -36,13 +38,17 @@ module Searchkick
|
|
36
38
|
# remove old indices that start w/ index_name
|
37
39
|
def clean_indices
|
38
40
|
all_indices = JSON.parse(Tire::Configuration.client.get("#{Tire::Configuration.url}/_aliases").body)
|
39
|
-
indices = all_indices.select{|k, v| v["aliases"].empty? && k =~ /\A#{Regexp.escape(index_name)}_\d{14}\z/ }.keys
|
41
|
+
indices = all_indices.select{|k, v| v["aliases"].empty? && k =~ /\A#{Regexp.escape(index_name)}_\d{14,17}\z/ }.keys
|
40
42
|
indices.each do |index|
|
41
43
|
Tire::Index.new(index).delete
|
42
44
|
end
|
43
45
|
indices
|
44
46
|
end
|
45
47
|
|
48
|
+
def self.extended(klass)
|
49
|
+
(@descendents ||= []) << klass
|
50
|
+
end
|
51
|
+
|
46
52
|
private
|
47
53
|
|
48
54
|
def searchkick_index_options
|
data/lib/searchkick/search.rb
CHANGED
@@ -25,166 +25,214 @@ module Searchkick
|
|
25
25
|
load = (options[:include] ? {include: options[:include]} : true) if load
|
26
26
|
|
27
27
|
# pagination
|
28
|
-
page =
|
28
|
+
page = [options[:page].to_i, 1].max
|
29
29
|
per_page = options[:limit] || options[:per_page] || 100000
|
30
|
-
offset = options[:offset] || (page
|
30
|
+
offset = options[:offset] || (page - 1) * per_page
|
31
31
|
index_name = options[:index_name] || index.name
|
32
32
|
|
33
33
|
conversions_field = @searchkick_options[:conversions]
|
34
34
|
personalize_field = @searchkick_options[:personalize]
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
36
|
+
all = term == "*"
|
37
|
+
|
38
|
+
if options[:similar]
|
39
|
+
payload = {
|
40
|
+
more_like_this: {
|
41
|
+
fields: fields,
|
42
|
+
like_text: term,
|
43
|
+
min_doc_freq: 1,
|
44
|
+
min_term_freq: 1
|
45
|
+
}
|
46
|
+
}
|
47
|
+
elsif all
|
48
|
+
payload = {
|
49
|
+
match_all: {}
|
50
|
+
}
|
51
|
+
else
|
52
|
+
if options[:autocomplete]
|
53
|
+
payload = {
|
54
|
+
multi_match: {
|
55
|
+
fields: fields,
|
56
|
+
query: term,
|
57
|
+
analyzer: "searchkick_autocomplete_search"
|
58
|
+
}
|
59
|
+
}
|
60
|
+
else
|
61
|
+
shared_options = {
|
62
|
+
fields: fields,
|
63
|
+
query: term,
|
64
|
+
use_dis_max: false,
|
65
|
+
operator: operator
|
66
|
+
}
|
67
|
+
payload = {
|
68
|
+
dis_max: {
|
69
|
+
queries: [
|
70
|
+
{multi_match: shared_options.merge(boost: 10, analyzer: "searchkick_search")},
|
71
|
+
{multi_match: shared_options.merge(boost: 10, analyzer: "searchkick_search2")},
|
72
|
+
{multi_match: shared_options.merge(fuzziness: 1, max_expansions: 3, analyzer: "searchkick_search")},
|
73
|
+
{multi_match: shared_options.merge(fuzziness: 1, max_expansions: 3, analyzer: "searchkick_search2")}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
if conversions_field and options[:conversions] != false
|
80
|
+
# wrap payload in a bool query
|
81
|
+
payload = {
|
82
|
+
bool: {
|
83
|
+
must: payload,
|
84
|
+
should: {
|
85
|
+
nested: {
|
86
|
+
path: conversions_field,
|
87
|
+
score_mode: "total",
|
88
|
+
query: {
|
89
|
+
custom_score: {
|
90
|
+
query: {
|
91
|
+
match: {
|
92
|
+
query: term
|
93
|
+
}
|
94
|
+
},
|
95
|
+
script: "doc['count'].value"
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
custom_filters = []
|
106
|
+
|
107
|
+
if options[:boost]
|
108
|
+
custom_filters << {
|
109
|
+
filter: {
|
110
|
+
exists: {
|
111
|
+
field: options[:boost]
|
112
|
+
}
|
113
|
+
},
|
114
|
+
script: "log(doc['#{options[:boost]}'].value + 2.718281828)"
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
if options[:user_id] and personalize_field
|
119
|
+
custom_filters << {
|
120
|
+
filter: {
|
121
|
+
term: {
|
122
|
+
personalize_field => options[:user_id]
|
123
|
+
}
|
124
|
+
},
|
125
|
+
boost: 100
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
if custom_filters.any?
|
130
|
+
payload = {
|
131
|
+
custom_filters_score: {
|
132
|
+
query: payload,
|
133
|
+
filters: custom_filters,
|
134
|
+
score_mode: "total"
|
135
|
+
}
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
payload = {
|
140
|
+
query: payload,
|
141
|
+
size: per_page,
|
142
|
+
from: offset
|
143
|
+
}
|
144
|
+
payload[:explain] = options[:explain] if options[:explain]
|
145
|
+
|
146
|
+
# order
|
147
|
+
if options[:order]
|
148
|
+
order = options[:order].is_a?(Enumerable) ? options[:order] : {options[:order] => :asc}
|
149
|
+
payload[:sort] = order
|
150
|
+
end
|
151
|
+
|
152
|
+
# where
|
153
|
+
# TODO expand or
|
154
|
+
where_filters =
|
155
|
+
proc do |where|
|
156
|
+
filters = []
|
157
|
+
(where || {}).each do |field, value|
|
158
|
+
if field == :or
|
159
|
+
value.each do |or_clause|
|
160
|
+
filters << {or: or_clause.map{|or_statement| {term: or_statement} }}
|
87
161
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
from offset if offset
|
93
|
-
explain options[:explain] if options[:explain]
|
94
|
-
|
95
|
-
# order
|
96
|
-
if options[:order]
|
97
|
-
order = options[:order].is_a?(Enumerable) ? options[:order] : {options[:order] => :asc}
|
98
|
-
sort do
|
99
|
-
order.each do |k, v|
|
100
|
-
by k, v
|
162
|
+
else
|
163
|
+
# expand ranges
|
164
|
+
if value.is_a?(Range)
|
165
|
+
value = {gte: value.first, (value.exclude_end? ? :lt : :lte) => value.last}
|
101
166
|
end
|
102
|
-
end
|
103
|
-
end
|
104
167
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
115
|
-
else
|
116
|
-
# expand ranges
|
117
|
-
if value.is_a?(Range)
|
118
|
-
value = {gte: value.first, (value.exclude_end? ? :lt : :lte) => value.last}
|
119
|
-
end
|
120
|
-
|
121
|
-
if value.is_a?(Array) # in query
|
122
|
-
filters << {terms: {field => value}}
|
123
|
-
elsif value.is_a?(Hash)
|
124
|
-
value.each do |op, op_value|
|
125
|
-
if op == :not # not equal
|
126
|
-
if op_value.is_a?(Array)
|
127
|
-
filters << {not: {terms: {field => op_value}}}
|
128
|
-
else
|
129
|
-
filters << {not: {term: {field => op_value}}}
|
130
|
-
end
|
131
|
-
else
|
132
|
-
range_query =
|
133
|
-
case op
|
134
|
-
when :gt
|
135
|
-
{from: op_value, include_lower: false}
|
136
|
-
when :gte
|
137
|
-
{from: op_value, include_lower: true}
|
138
|
-
when :lt
|
139
|
-
{to: op_value, include_upper: false}
|
140
|
-
when :lte
|
141
|
-
{to: op_value, include_upper: true}
|
142
|
-
else
|
143
|
-
raise "Unknown where operator"
|
144
|
-
end
|
145
|
-
filters << {range: {field => range_query}}
|
146
|
-
end
|
168
|
+
if value.is_a?(Array) # in query
|
169
|
+
filters << {terms: {field => value}}
|
170
|
+
elsif value.is_a?(Hash)
|
171
|
+
value.each do |op, op_value|
|
172
|
+
if op == :not # not equal
|
173
|
+
if op_value.is_a?(Array)
|
174
|
+
filters << {not: {terms: {field => op_value}}}
|
175
|
+
else
|
176
|
+
filters << {not: {term: {field => op_value}}}
|
147
177
|
end
|
148
178
|
else
|
149
|
-
|
179
|
+
range_query =
|
180
|
+
case op
|
181
|
+
when :gt
|
182
|
+
{from: op_value, include_lower: false}
|
183
|
+
when :gte
|
184
|
+
{from: op_value, include_lower: true}
|
185
|
+
when :lt
|
186
|
+
{to: op_value, include_upper: false}
|
187
|
+
when :lte
|
188
|
+
{to: op_value, include_upper: true}
|
189
|
+
else
|
190
|
+
raise "Unknown where operator"
|
191
|
+
end
|
192
|
+
filters << {range: {field => range_query}}
|
150
193
|
end
|
151
194
|
end
|
195
|
+
else
|
196
|
+
filters << {term: {field => value}}
|
152
197
|
end
|
153
|
-
filters
|
154
198
|
end
|
155
|
-
|
156
|
-
where_filters.call(options[:where]).each do |f|
|
157
|
-
type, value = f.first
|
158
|
-
filter type, value
|
159
199
|
end
|
200
|
+
filters
|
201
|
+
end
|
160
202
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
203
|
+
# filters
|
204
|
+
filters = where_filters.call(options[:where])
|
205
|
+
if filters.any?
|
206
|
+
payload[:filter] = {
|
207
|
+
and: filters
|
208
|
+
}
|
209
|
+
end
|
167
210
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
type, value = facet_filters.first.first
|
174
|
-
facet_filter type, value
|
175
|
-
elsif facet_filters.size > 1
|
176
|
-
facet_filter :and, *facet_filters
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
211
|
+
# facets
|
212
|
+
if options[:facets]
|
213
|
+
facets = options[:facets] || {}
|
214
|
+
if facets.is_a?(Array) # convert to more advanced syntax
|
215
|
+
facets = Hash[ facets.map{|f| [f, {}] } ]
|
181
216
|
end
|
182
217
|
|
183
|
-
|
218
|
+
payload[:facets] = {}
|
219
|
+
facets.each do |field, facet_options|
|
220
|
+
payload[:facets][field] = {
|
221
|
+
terms: {
|
222
|
+
field: field
|
223
|
+
}
|
224
|
+
}
|
184
225
|
|
185
|
-
|
186
|
-
|
187
|
-
|
226
|
+
facet_filters = where_filters.call(facet_options[:where])
|
227
|
+
if facet_filters
|
228
|
+
payload[:facets][field][:facet_filter] = {
|
229
|
+
and: {
|
230
|
+
filters: facet_filters
|
231
|
+
}
|
232
|
+
}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
188
236
|
|
189
237
|
# suggestions
|
190
238
|
if options[:suggest]
|
@@ -203,7 +251,11 @@ module Searchkick
|
|
203
251
|
end
|
204
252
|
end
|
205
253
|
|
206
|
-
|
254
|
+
# An empty array will cause only the _id and _type for each hit to be returned
|
255
|
+
# http://www.elasticsearch.org/guide/reference/api/search/fields/
|
256
|
+
payload[:fields] = [] if load
|
257
|
+
|
258
|
+
search = Tire::Search::Search.new(index_name, load: load, payload: payload, size: per_page, from: offset)
|
207
259
|
Searchkick::Results.new(search.json, search.options.merge(term: term))
|
208
260
|
end
|
209
261
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Searchkick
|
2
|
+
module Similar
|
3
|
+
|
4
|
+
def similar(options = {})
|
5
|
+
like_text = index.retrieve(document_type, id).to_hash
|
6
|
+
.keep_if{|k,v| k[0] != "_" and (!options[:fields] or options[:fields].map(&:to_sym).include?(k)) }
|
7
|
+
.values.compact.join(" ")
|
8
|
+
|
9
|
+
# TODO deep merge method
|
10
|
+
options[:where] ||= {}
|
11
|
+
options[:where][:_id] ||= {}
|
12
|
+
options[:where][:_id][:not] = id
|
13
|
+
options[:limit] ||= 10
|
14
|
+
options[:similar] = true
|
15
|
+
self.class.search(like_text, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/searchkick/tasks.rb
CHANGED
@@ -1,9 +1,33 @@
|
|
1
1
|
require "rake"
|
2
2
|
|
3
3
|
namespace :searchkick do
|
4
|
-
|
4
|
+
|
5
|
+
desc "reindex model"
|
5
6
|
task :reindex => :environment do
|
6
|
-
|
7
|
-
|
7
|
+
if ENV["CLASS"]
|
8
|
+
klass = ENV["CLASS"].constantize rescue nil
|
9
|
+
if klass
|
10
|
+
klass.reindex
|
11
|
+
else
|
12
|
+
abort "Could not find class: #{ENV["CLASS"]}"
|
13
|
+
end
|
14
|
+
else
|
15
|
+
abort "USAGE: rake searchkick:reindex CLASS=Product"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if defined?(Rails)
|
20
|
+
|
21
|
+
namespace :reindex do
|
22
|
+
desc "reindex all models"
|
23
|
+
task :all => :environment do
|
24
|
+
Rails.application.eager_load!
|
25
|
+
(Searchkick::Reindex.instance_variable_get(:@descendents) || []).each do |model|
|
26
|
+
model.reindex
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
8
31
|
end
|
32
|
+
|
9
33
|
end
|
data/lib/searchkick/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class TestAutocomplete < Minitest::Unit::TestCase
|
4
|
+
|
5
|
+
def test_autocomplete
|
6
|
+
store_names ["Hummus"]
|
7
|
+
assert_search "hum", ["Hummus"], autocomplete: true
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_autocomplete_two_words
|
11
|
+
store_names ["Organic Hummus"]
|
12
|
+
assert_search "hum", [], autocomplete: true
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_autocomplete_fields
|
16
|
+
store_names ["Hummus"]
|
17
|
+
assert_search "hum", ["Hummus"], autocomplete: true, fields: [:name]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/test/boost_test.rb
CHANGED
@@ -25,10 +25,11 @@ class TestBoost < Minitest::Unit::TestCase
|
|
25
25
|
|
26
26
|
def test_boost
|
27
27
|
store [
|
28
|
-
{name: "
|
29
|
-
{name: "Tomato B", orders_count: 10}
|
28
|
+
{name: "Tomato A"},
|
29
|
+
{name: "Tomato B", orders_count: 10},
|
30
|
+
{name: "Tomato C", orders_count: 100}
|
30
31
|
]
|
31
|
-
assert_order "tomato", ["Tomato B", "
|
32
|
+
assert_order "tomato", ["Tomato C", "Tomato B", "Tomato A"], boost: "orders_count"
|
32
33
|
end
|
33
34
|
|
34
35
|
def test_boost_zero
|
data/test/index_test.rb
CHANGED
@@ -3,8 +3,8 @@ require_relative "test_helper"
|
|
3
3
|
class TestIndex < Minitest::Unit::TestCase
|
4
4
|
|
5
5
|
def test_clean_indices
|
6
|
-
old_index = Tire::Index.new("
|
7
|
-
different_index = Tire::Index.new("
|
6
|
+
old_index = Tire::Index.new("products_test_20130801000000000")
|
7
|
+
different_index = Tire::Index.new("items_test_20130801000000000")
|
8
8
|
|
9
9
|
# create indexes
|
10
10
|
old_index.create
|
@@ -17,4 +17,13 @@ class TestIndex < Minitest::Unit::TestCase
|
|
17
17
|
assert !old_index.exists?
|
18
18
|
end
|
19
19
|
|
20
|
+
def test_clean_indices_old_format
|
21
|
+
old_index = Tire::Index.new("products_test_20130801000000")
|
22
|
+
old_index.create
|
23
|
+
|
24
|
+
Product.clean_indices
|
25
|
+
|
26
|
+
assert !old_index.exists?
|
27
|
+
end
|
28
|
+
|
20
29
|
end
|
data/test/match_test.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require_relative "test_helper"
|
2
4
|
|
3
5
|
class TestMatch < Minitest::Unit::TestCase
|
@@ -19,10 +21,10 @@ class TestMatch < Minitest::Unit::TestCase
|
|
19
21
|
assert_search "pepperjack cheese skewers", ["Pepper Jack Cheese Skewers"]
|
20
22
|
end
|
21
23
|
|
22
|
-
def test_cheese_space_in_query
|
23
|
-
|
24
|
-
|
25
|
-
end
|
24
|
+
# def test_cheese_space_in_query
|
25
|
+
# store_names ["Pepperjack Cheese Skewers"]
|
26
|
+
# assert_search "pepper jack cheese skewers", ["Pepperjack Cheese Skewers"]
|
27
|
+
# end
|
26
28
|
|
27
29
|
def test_middle_token
|
28
30
|
store_names ["Dish Washer Amazing Organic Soap"]
|
@@ -127,21 +129,9 @@ class TestMatch < Minitest::Unit::TestCase
|
|
127
129
|
assert_search "almondmilks", ["Almond Milk"]
|
128
130
|
end
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
store_names ["Hummus"]
|
134
|
-
assert_search "hum", ["Hummus"], autocomplete: true
|
135
|
-
end
|
136
|
-
|
137
|
-
def test_autocomplete_two_words
|
138
|
-
store_names ["Organic Hummus"]
|
139
|
-
assert_search "hum", [], autocomplete: true
|
140
|
-
end
|
141
|
-
|
142
|
-
def test_autocomplete_fields
|
143
|
-
store_names ["Hummus"]
|
144
|
-
assert_search "hum", ["Hummus"], autocomplete: true, fields: [:name]
|
132
|
+
def test_all
|
133
|
+
store_names ["Product A", "Product B"]
|
134
|
+
assert_search "*", ["Product A", "Product B"]
|
145
135
|
end
|
146
136
|
|
147
137
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class TestSimilar < Minitest::Unit::TestCase
|
4
|
+
|
5
|
+
def test_fields
|
6
|
+
store_names ["1% Organic Milk", "2% Organic Milk", "Popcorn"]
|
7
|
+
assert_equal ["2% Organic Milk"], Product.find_by(name: "1% Organic Milk").similar(fields: ["name"]).map(&:name)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
data/test/sql_test.rb
CHANGED
@@ -20,18 +20,25 @@ class TestSql < Minitest::Unit::TestCase
|
|
20
20
|
|
21
21
|
def test_pagination
|
22
22
|
store_names ["Product A", "Product B", "Product C", "Product D", "Product E"]
|
23
|
-
|
23
|
+
products = Product.search("product", order: {name: :asc}, page: 2, per_page: 2)
|
24
|
+
assert_equal ["Product C", "Product D"], products.map(&:name)
|
25
|
+
assert_equal 2, products.current_page
|
26
|
+
assert_equal 2, products.per_page
|
27
|
+
assert_equal 3, products.total_pages
|
28
|
+
assert_equal 5, products.total_count
|
24
29
|
end
|
25
30
|
|
26
31
|
def test_pagination_nil_page
|
27
32
|
store_names ["Product A", "Product B", "Product C", "Product D", "Product E"]
|
28
|
-
|
33
|
+
products = Product.search("product", order: {name: :asc}, page: nil, per_page: 2)
|
34
|
+
assert_equal ["Product A", "Product B"], products.map(&:name)
|
35
|
+
assert_equal 1, products.current_page
|
29
36
|
end
|
30
37
|
|
31
38
|
def test_where
|
32
39
|
now = Time.now
|
33
40
|
store [
|
34
|
-
{name: "Product A", store_id: 1, in_stock: true, backordered: true, created_at: now, orders_count: 4},
|
41
|
+
{name: "Product A", store_id: 1, in_stock: true, backordered: true, created_at: now, orders_count: 4, user_ids: [1, 2, 3]},
|
35
42
|
{name: "Product B", store_id: 2, in_stock: true, backordered: false, created_at: now - 1, orders_count: 3},
|
36
43
|
{name: "Product C", store_id: 3, in_stock: false, backordered: true, created_at: now - 2, orders_count: 2},
|
37
44
|
{name: "Product D", store_id: 4, in_stock: false, backordered: false, created_at: now - 3, orders_count: 1},
|
@@ -54,6 +61,8 @@ class TestSql < Minitest::Unit::TestCase
|
|
54
61
|
assert_search "product", ["Product B", "Product C", "Product D"], where: {store_id: {not: 1}}
|
55
62
|
assert_search "product", ["Product C", "Product D"], where: {store_id: {not: [1, 2]}}
|
56
63
|
assert_search "product", ["Product A", "Product B", "Product C"], where: {or: [[{in_stock: true}, {store_id: 3}]]}
|
64
|
+
# array
|
65
|
+
assert_search "product", ["Product A"], where: {user_ids: 2}
|
57
66
|
end
|
58
67
|
|
59
68
|
def test_where_string
|
data/test/test_helper.rb
CHANGED
@@ -4,6 +4,8 @@ require "minitest/autorun"
|
|
4
4
|
require "minitest/pride"
|
5
5
|
require "active_record"
|
6
6
|
|
7
|
+
ENV["RACK_ENV"] = "test"
|
8
|
+
|
7
9
|
# for debugging
|
8
10
|
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
9
11
|
|
@@ -38,7 +40,8 @@ class Product < ActiveRecord::Base
|
|
38
40
|
|
39
41
|
searchkick \
|
40
42
|
settings: {
|
41
|
-
number_of_shards: 1
|
43
|
+
number_of_shards: 1,
|
44
|
+
number_of_replicas: 0
|
42
45
|
},
|
43
46
|
synonyms: [
|
44
47
|
["clorox", "bleach"],
|
@@ -65,7 +68,6 @@ end
|
|
65
68
|
|
66
69
|
Product.index.delete if Product.index.exists?
|
67
70
|
Product.reindex
|
68
|
-
sleep 1
|
69
71
|
Product.reindex # run twice for both index paths
|
70
72
|
|
71
73
|
class MiniTest::Unit::TestCase
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchkick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tire
|
@@ -116,6 +116,8 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- .gitignore
|
119
|
+
- .travis.yml
|
120
|
+
- CHANGELOG.md
|
119
121
|
- Gemfile
|
120
122
|
- LICENSE.txt
|
121
123
|
- README.md
|
@@ -126,13 +128,16 @@ files:
|
|
126
128
|
- lib/searchkick/reindex.rb
|
127
129
|
- lib/searchkick/results.rb
|
128
130
|
- lib/searchkick/search.rb
|
131
|
+
- lib/searchkick/similar.rb
|
129
132
|
- lib/searchkick/tasks.rb
|
130
133
|
- lib/searchkick/version.rb
|
131
134
|
- searchkick.gemspec
|
135
|
+
- test/autocomplete_test.rb
|
132
136
|
- test/boost_test.rb
|
133
137
|
- test/facets_test.rb
|
134
138
|
- test/index_test.rb
|
135
139
|
- test/match_test.rb
|
140
|
+
- test/similar_test.rb
|
136
141
|
- test/sql_test.rb
|
137
142
|
- test/suggest_test.rb
|
138
143
|
- test/synonyms_test.rb
|
@@ -162,10 +167,12 @@ signing_key:
|
|
162
167
|
specification_version: 4
|
163
168
|
summary: Search made easy
|
164
169
|
test_files:
|
170
|
+
- test/autocomplete_test.rb
|
165
171
|
- test/boost_test.rb
|
166
172
|
- test/facets_test.rb
|
167
173
|
- test/index_test.rb
|
168
174
|
- test/match_test.rb
|
175
|
+
- test/similar_test.rb
|
169
176
|
- test/sql_test.rb
|
170
177
|
- test/suggest_test.rb
|
171
178
|
- test/synonyms_test.rb
|