searchkick 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c7af3f444676ffd5380a5fec201895f7f1ddbcbe
4
- data.tar.gz: 07b653656f8914410a6bda9c1f0f3d1485d54465
3
+ metadata.gz: 275937983ec78c77a0d7f25fa26a9fbe067a5154
4
+ data.tar.gz: 31783a30cf0b0d6170d94a99daf2c0022d20008d
5
5
  SHA512:
6
- metadata.gz: acde708f7c77cddf74af3b647e37f97014a16ab958b7e03b11e9774abfa02ab57c518c6f0d2647d39434f4f8ed3c3203e9a536a4ac2b2d0c961f80201cb8121c
7
- data.tar.gz: 95ee8bd70933e4d04b0f04e96be972ed2075a065961b86da6f4164fe84ba013c2724c0801911299914b4554a405a5dc2f5751ba2843f0e80fed45b30055203c6
6
+ metadata.gz: 636392d9b685813dbdcfd6d8b9f9aa715be73515e089f32a1c95a870c3c0ff17a3a336c3605318b9f8ea6ca406927493752fa6d603762f70a77777de41306144
7
+ data.tar.gz: 954203fe3c5a0e48d404322c7a270cde965bbd5f69e32e285da9e1582546cd5257bd87b616315c3e997db9402838f4966920fd5c0e10c195502b0b54214a64ff
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  *.log
19
+ .DS_Store
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ services:
6
+ - elasticsearch
7
+ script: bundle exec rake test
8
+ before_script:
9
+ - psql -c 'create database searchkick_test;' -U postgres
10
+ notifications:
11
+ email:
12
+ on_success: never
13
+ on_failure: change
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
+ [![Build Status](https://travis-ci.org/ankane/searchkick.png?branch=master)](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
@@ -3,6 +3,7 @@ require "searchkick/version"
3
3
  require "searchkick/reindex"
4
4
  require "searchkick/results"
5
5
  require "searchkick/search"
6
+ require "searchkick/similar"
6
7
  require "searchkick/model"
7
8
  require "searchkick/tasks"
8
9
  require "searchkick/logger" if defined?(Rails)
@@ -7,6 +7,7 @@ module Searchkick
7
7
  class_eval do
8
8
  extend Searchkick::Search
9
9
  extend Searchkick::Reindex
10
+ include Searchkick::Similar
10
11
  include Tire::Model::Search
11
12
  include Tire::Model::Callbacks
12
13
  tire do
@@ -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
@@ -25,166 +25,214 @@ module Searchkick
25
25
  load = (options[:include] ? {include: options[:include]} : true) if load
26
26
 
27
27
  # pagination
28
- page = options.has_key?(:page) ? [options[:page].to_i, 1].max : nil
28
+ page = [options[:page].to_i, 1].max
29
29
  per_page = options[:limit] || options[:per_page] || 100000
30
- offset = options[:offset] || (page && (page - 1) * per_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
- # TODO lose Tire DSL for more flexibility
37
- s =
38
- Tire::Search::Search.new do
39
- query do
40
- custom_filters_score do
41
- query do
42
- boolean do
43
- must do
44
- if options[:autocomplete]
45
- match fields, term, analyzer: "searchkick_autocomplete_search"
46
- else
47
- dis_max do
48
- query do
49
- match fields, term, use_dis_max: false, boost: 10, operator: operator, analyzer: "searchkick_search"
50
- end
51
- query do
52
- match fields, term, use_dis_max: false, boost: 10, operator: operator, analyzer: "searchkick_search2"
53
- end
54
- query do
55
- match fields, term, use_dis_max: false, fuzziness: 1, max_expansions: 3, operator: operator, analyzer: "searchkick_search"
56
- end
57
- query do
58
- match fields, term, use_dis_max: false, fuzziness: 1, max_expansions: 3, operator: operator, analyzer: "searchkick_search2"
59
- end
60
- end
61
- end
62
- end
63
- if conversions_field and options[:conversions] != false
64
- should do
65
- nested path: conversions_field, score_mode: "total" do
66
- query do
67
- custom_score script: "doc['count'].value" do
68
- match "query", term
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end
76
- if options[:boost]
77
- filter do
78
- filter :exists, field: options[:boost]
79
- script "log(doc['#{options[:boost]}'].value + 2.718281828)"
80
- end
81
- end
82
- if options[:user_id] and personalize_field
83
- filter do
84
- filter :term, personalize_field => options[:user_id]
85
- boost 100
86
- end
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
- score_mode "total"
89
- end
90
- end
91
- size per_page
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
- # where
106
- # TODO expand or
107
- where_filters =
108
- proc do |where|
109
- filters = []
110
- (where || {}).each do |field, value|
111
- if field == :or
112
- value.each do |or_clause|
113
- filters << {or: or_clause.map{|or_statement| {term: or_statement} }}
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
- filters << {term: {field => value}}
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
- # facets
162
- if options[:facets]
163
- facets = options[:facets] || {}
164
- if facets.is_a?(Array) # convert to more advanced syntax
165
- facets = Hash[ facets.map{|f| [f, {}] } ]
166
- end
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
- facets.each do |field, facet_options|
169
- facet_filters = where_filters.call(facet_options[:where])
170
- facet field do
171
- terms field
172
- if facet_filters.size == 1
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
- payload = s.to_hash
218
+ payload[:facets] = {}
219
+ facets.each do |field, facet_options|
220
+ payload[:facets][field] = {
221
+ terms: {
222
+ field: field
223
+ }
224
+ }
184
225
 
185
- # An empty array will cause only the _id and _type for each hit to be returned
186
- # http://www.elasticsearch.org/guide/reference/api/search/fields/
187
- payload[:fields] = [] if load
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
- search = Tire::Search::Search.new(index_name, load: load, payload: payload)
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
@@ -1,9 +1,33 @@
1
1
  require "rake"
2
2
 
3
3
  namespace :searchkick do
4
- desc "re-index elasticsearch"
4
+
5
+ desc "reindex model"
5
6
  task :reindex => :environment do
6
- klass = ENV["CLASS"].constantize
7
- klass.reindex
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
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -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: "Organic Tomato A"},
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", "Organic Tomato A"], boost: "orders_count"
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("products_development_20130801000000")
7
- different_index = Tire::Index.new("items_development_20130801000000")
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
- store_names ["Pepperjack Cheese Skewers"]
24
- assert_search "pepper jack cheese skewers", ["Pepperjack Cheese Skewers"]
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
- # autocomplete
131
-
132
- def test_autocomplete
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
- assert_order "product", ["Product C", "Product D"], order: {name: :asc}, page: 2, per_page: 2
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
- assert_order "product", ["Product A", "Product B"], order: {name: :asc}, page: nil, per_page: 2
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.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-12 00:00:00.000000000 Z
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