searchkick 2.1.1 → 2.2.0

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: 5fcfaef2d99cf6def7035bc882eecf161f687dca
4
- data.tar.gz: a50200b8381e063e3c355586c898d1d8dc3b360a
3
+ metadata.gz: 85108ff6dc90bd7075f66c5627fc06eba581657f
4
+ data.tar.gz: 062420bd6cd4c9601026425d40923cc82e649364
5
5
  SHA512:
6
- metadata.gz: 6ddbce0ef6d29c51b2884ffc1791acedb886a3a4915889b059941fdcf0c9a5f9cbe68e50df7a82934d9cbc8b278b032bd789a2ec57d354f7b2fc09ebe9a91f8b
7
- data.tar.gz: ab3dbbb296596682c5b9caaf6ba47f5d5299e60fd66bc53f31d14ecd9ac99f3f2f96404ea5f71e36f4cf02e310ffabb8c0d5c1b9a155d05e474e1e03bae67764
6
+ metadata.gz: 680ef536ba5e15456b0df70412eec5e47d13f43424c02e7d039921abf3c83b06fbaefa95d824fa2ae0efeecde8c6488ad56b4aabed442eb6c191a309f2204c6c
7
+ data.tar.gz: d118d331ffd37c1fbec228bba2cc043f03730510a0ca45d7bd9afe9c04aa31a8ebeeaba82b57113c070fd7af3b1c8c347f0b22bd8cff43565f9246ed408a0c77
data/.travis.yml CHANGED
@@ -19,7 +19,7 @@ gemfile:
19
19
  - test/gemfiles/mongoid5.gemfile
20
20
  - test/gemfiles/mongoid6.gemfile
21
21
  env:
22
- - ELASTICSEARCH_VERSION=5.1.1
22
+ - ELASTICSEARCH_VERSION=5.2.0
23
23
  jdk: oraclejdk8
24
24
  matrix:
25
25
  include:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 2.2.0
2
+
3
+ - Fixed bug with text values longer than 256 characters and `_all` field - see [#850](https://github.com/ankane/searchkick/issues/850)
4
+ - Fixed issue with `_all` field in `searchable`
5
+ - Fixed `exclude` option with `word_start`
6
+
1
7
  ## 2.1.1
2
8
 
3
9
  - Fixed duplicate notifications
@@ -41,6 +47,10 @@ Breaking changes
41
47
  - Removed support for Elasticsearch 1 as it reaches [end of life](https://www.elastic.co/support/eol)
42
48
  - Removed facets, legacy options, and legacy methods
43
49
  - Invalid options now throw an `ArgumentError`
50
+ - The `query` and `json` options have been removed in favor of `body`
51
+ - The `include` option has been removed in favor of `includes`
52
+ - The `personalize` option has been removed in favor of `boost_where`
53
+ - The `partial` option has been removed in favor of `operator`
44
54
  - Renamed `select_v2` to `select` (legacy `select` no longer available)
45
55
  - The `_all` field is disabled if `searchable` option is used (for performance)
46
56
  - The `partial_reindex(:method_name)` method has been replaced with `reindex(:method_name)`
data/README.md CHANGED
@@ -47,9 +47,7 @@ Plus:
47
47
 
48
48
  ```sh
49
49
  brew install elasticsearch
50
-
51
- # start the server
52
- elasticsearch
50
+ brew services start elasticsearch
53
51
  ```
54
52
 
55
53
  Add this line to your application’s Gemfile:
@@ -385,6 +383,17 @@ If a user searches `butter`, they may also get results for `peanut butter`. To p
385
383
  Product.search "butter", exclude: ["peanut butter"]
386
384
  ```
387
385
 
386
+ You can map queries and terms to exclude with:
387
+
388
+ ```ruby
389
+ exclude_queries = {
390
+ "butter" => ["peanut butter"],
391
+ "cream" => ["ice cream"]
392
+ }
393
+
394
+ Product.search query, exclude: exclude_queries[query]
395
+ ```
396
+
388
397
  ### Emoji
389
398
 
390
399
  Search :ice_cream::cake: and get `ice cream cake`!
@@ -1018,7 +1027,7 @@ Product.search_index.tokens("dieg", analyzer: "searchkick_word_search")
1018
1027
  # ["dieg"] - match!!
1019
1028
  ```
1020
1029
 
1021
- See the [complete list of analyzers](lib/searchkick/index.rb#L209).
1030
+ See the [complete list of analyzers](https://github.com/ankane/searchkick/blob/31780ddac7a89eab1e0552a32b403f2040a37931/lib/searchkick/index_options.rb#L32).
1022
1031
 
1023
1032
  ## Deployment
1024
1033
 
@@ -1136,9 +1145,11 @@ Significantly increase performance with faster JSON generation. Add [Oj](https:/
1136
1145
  gem 'oj'
1137
1146
  ```
1138
1147
 
1148
+ This speeds up all JSON generation and parsing in your application (automatically!)
1149
+
1139
1150
  ### Persistent HTTP Connections
1140
1151
 
1141
- Significantly increase performance with persistent HTTP connections. Add [Typhoeus](https://github.com/typhoeus/typhoeus) to your Gemfile.
1152
+ Significantly increase performance with persistent HTTP connections. Add [Typhoeus](https://github.com/typhoeus/typhoeus) to your Gemfile and it’ll automatically be used.
1142
1153
 
1143
1154
  ```ruby
1144
1155
  gem 'typhoeus'
@@ -1199,18 +1210,19 @@ And use:
1199
1210
  Searchkick.reindex_status(index_name)
1200
1211
  ```
1201
1212
 
1202
- You can use [ActiveJob::TrafficControl](https://github.com/nickelser/activejob-traffic_control) to control concurrency. Install the gem and create an initializer with:
1213
+ You can use [ActiveJob::TrafficControl](https://github.com/nickelser/activejob-traffic_control) to control concurrency. Install the gem:
1203
1214
 
1204
1215
  ```ruby
1205
- require "active_job/traffic_control"
1216
+ gem 'activejob-traffic_control', '>= 0.1.3'
1217
+ ```
1206
1218
 
1207
- Searchkick.redis = Redis.new
1219
+ And create an initializer with:
1220
+
1221
+ ```ruby
1208
1222
  ActiveJob::TrafficControl.client = Searchkick.redis
1209
1223
 
1210
1224
  class Searchkick::BulkReindexJob
1211
- include ActiveJob::TrafficControl::Concurrency
1212
-
1213
- concurrency 3, drop: false
1225
+ concurrency 3
1214
1226
  end
1215
1227
  ```
1216
1228
 
@@ -1677,21 +1689,78 @@ Product.search "ah", misspellings: {prefix_length: 2} # ah, no aha
1677
1689
 
1678
1690
  ## Testing
1679
1691
 
1680
- This section could use some love.
1692
+ For performance, only enable Searchkick callbacks for the tests that need it.
1693
+
1694
+ ### Minitest
1695
+
1696
+ Add to your `test/test_helper.rb`:
1697
+
1698
+ ```ruby
1699
+ # reindex models
1700
+ Product.reindex
1701
+
1702
+ # and disable callbacks
1703
+ Searchkick.disable_callbacks
1704
+ ```
1705
+
1706
+ And use:
1707
+
1708
+ ```ruby
1709
+ class ProductTest < Minitest::Test
1710
+ def setup
1711
+ Searchkick.enable_callbacks
1712
+ end
1713
+
1714
+ def teardown
1715
+ Searchkick.disable_callbacks
1716
+ end
1717
+
1718
+ def test_search
1719
+ Product.create!(name: "Apple")
1720
+ Product.search_index.refresh
1721
+ assert_equal ["Apple"], Product.search("apple").map(&:name)
1722
+ end
1723
+ end
1724
+ ```
1681
1725
 
1682
1726
  ### RSpec
1683
1727
 
1728
+ Add to your `spec/spec_helper.rb`:
1729
+
1684
1730
  ```ruby
1685
- describe Product do
1686
- it "searches" do
1731
+ RSpec.configure do |config|
1732
+ config.before(:suite) do
1733
+ # reindex models
1687
1734
  Product.reindex
1688
- # test goes here...
1735
+
1736
+ # and disable callbacks
1737
+ Searchkick.disable_callbacks
1738
+ end
1739
+
1740
+ config.around(:each, search: true) do |example|
1741
+ Searchkick.enable_callbacks
1742
+ example.run
1743
+ Searchkick.disable_callbacks
1744
+ end
1745
+ end
1746
+ ```
1747
+
1748
+ And use:
1749
+
1750
+ ```ruby
1751
+ describe Product, search: true do
1752
+ it "searches" do
1753
+ Product.create!(name: "Apple")
1754
+ Product.search_index.refresh
1755
+ assert_equal ["Apple"], Product.search("apple").map(&:name)
1689
1756
  end
1690
1757
  end
1691
1758
  ```
1692
1759
 
1693
1760
  ### Factory Girl
1694
1761
 
1762
+ Manually reindex after an instance is created.
1763
+
1695
1764
  ```ruby
1696
1765
  product = FactoryGirl.create(:product)
1697
1766
  product.reindex(refresh: true)
@@ -1716,6 +1785,10 @@ Important notes are listed below.
1716
1785
  - Removed support for Elasticsearch 1 as it reaches [end of life](https://www.elastic.co/support/eol)
1717
1786
  - Removed facets, legacy options, and legacy methods
1718
1787
  - Invalid options now throw an `ArgumentError`
1788
+ - The `query` and `json` options have been removed in favor of `body`
1789
+ - The `include` option has been removed in favor of `includes`
1790
+ - The `personalize` option has been removed in favor of `boost_where`
1791
+ - The `partial` option has been removed in favor of `operator`
1719
1792
  - Renamed `select_v2` to `select` (legacy `select` no longer available)
1720
1793
  - The `_all` field is disabled if `searchable` option is used (for performance)
1721
1794
  - The `partial_reindex(:method_name)` method has been replaced with `reindex(:method_name)`
@@ -1754,6 +1827,15 @@ Before `0.3.0`, locations were indexed incorrectly. When upgrading, be sure to r
1754
1827
 
1755
1828
  ## Elasticsearch Gotchas
1756
1829
 
1830
+ ### Consistency
1831
+
1832
+ Elasticsearch is eventually consistent, meaning it can take up to a second for a change to reflect in search. You can use the `refresh` method to have it show up immediately.
1833
+
1834
+ ```ruby
1835
+ product.save!
1836
+ Product.search_index.refresh
1837
+ ```
1838
+
1757
1839
  ### Inconsistent Scores
1758
1840
 
1759
1841
  Due to the distributed nature of Elasticsearch, you can get incorrect results when the number of documents in the index is low. You can [read more about it here](https://www.elastic.co/blog/understanding-query-then-fetch-vs-dfs-query-then-fetch). To fix this, do:
@@ -159,6 +159,7 @@ module Searchkick
159
159
 
160
160
  # search
161
161
 
162
+ # TODO remove in next major version
162
163
  def search_model(searchkick_klass, term = "*", **options, &block)
163
164
  query = Searchkick::Query.new(searchkick_klass, term, options)
164
165
  yield(query.body) if block
@@ -25,7 +25,7 @@ module Searchkick
25
25
  }
26
26
  end
27
27
 
28
- keyword_mapping[:ignore_above] = (options[:ignore_above] || 256) unless below22
28
+ keyword_mapping[:ignore_above] = (options[:ignore_above] || 30000) unless below22
29
29
 
30
30
  settings = {
31
31
  analysis: {
@@ -227,6 +227,8 @@ module Searchkick
227
227
 
228
228
  word = options[:word] != false && (!options[:match] || options[:match] == :word)
229
229
 
230
+ mapping_options[:searchable].delete("_all")
231
+
230
232
  mapping_options.values.flatten.uniq.each do |field|
231
233
  fields = {}
232
234
 
@@ -286,8 +288,6 @@ module Searchkick
286
288
  dynamic_fields["{name}"] = {type: default_type, index: "no"}
287
289
  end
288
290
 
289
- dynamic_fields["{name}"][:ignore_above] = (options[:ignore_above] || 256) unless below22
290
-
291
291
  unless options[:searchable]
292
292
  if options[:match] && options[:match] != :word
293
293
  dynamic_fields[options[:match]] = {type: default_type, index: "analyzed", analyzer: "searchkick_#{options[:match]}_index"}
@@ -301,7 +301,7 @@ module Searchkick
301
301
  # http://www.elasticsearch.org/guide/reference/mapping/multi-field-type/
302
302
  multi_field = dynamic_fields["{name}"].merge(fields: dynamic_fields.except("{name}"))
303
303
 
304
- all_enabled = !options[:searchable] || options[:searchable].to_a.include?("_all")
304
+ all_enabled = !options[:searchable] || options[:searchable].to_a.map(&:to_s).include?("_all")
305
305
 
306
306
  mappings = {
307
307
  _default_: {
@@ -26,7 +26,7 @@ module Searchkick
26
26
 
27
27
  class << self
28
28
  def searchkick_search(term = "*", **options, &block)
29
- searchkick_index.search_model(self, term, options, &block)
29
+ Searchkick.search(term, {model: self}.merge(options), &block)
30
30
  end
31
31
  alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
32
32
 
@@ -144,6 +144,12 @@ module Searchkick
144
144
  def should_index?
145
145
  true
146
146
  end unless method_defined?(:should_index?)
147
+
148
+ if defined?(Cequel) && self < Cequel::Record && !method_defined?(:destroyed?)
149
+ def destroyed?
150
+ transient?
151
+ end
152
+ end
147
153
  end
148
154
  end
149
155
  end
@@ -6,7 +6,7 @@ module Searchkick
6
6
  model = class_name.constantize
7
7
 
8
8
  limit = model.searchkick_index.options[:batch_size] || 1000
9
- record_ids = Searchkick::ReindexQueue.new(model.searchkick_index.name).reserve(limit: limit)
9
+ record_ids = model.searchkick_index.reindex_queue.reserve(limit: limit)
10
10
  if record_ids.any?
11
11
  Searchkick::ProcessBatchJob.perform_later(
12
12
  class_name: model.name,
@@ -283,18 +283,25 @@ module Searchkick
283
283
 
284
284
  shared_options[:operator] = operator if match_type == :match
285
285
 
286
+ exclude_analyzer = nil
287
+ exclude_field = field
288
+
286
289
  if field == "_all" || field.end_with?(".analyzed")
287
290
  shared_options[:cutoff_frequency] = 0.001 unless operator == "and" || misspellings == false
288
291
  qs.concat [
289
292
  shared_options.merge(analyzer: "searchkick_search"),
290
293
  shared_options.merge(analyzer: "searchkick_search2")
291
294
  ]
295
+ exclude_analyzer = "searchkick_search2"
292
296
  elsif field.end_with?(".exact")
293
297
  f = field.split(".")[0..-2].join(".")
294
298
  queries_to_add << {match: {f => shared_options.merge(analyzer: "keyword")}}
299
+ exclude_field = f
300
+ exclude_analyzer = "keyword"
295
301
  else
296
302
  analyzer = field =~ /\.word_(start|middle|end)\z/ ? "searchkick_word_search" : "searchkick_autocomplete_search"
297
303
  qs << shared_options.merge(analyzer: analyzer)
304
+ exclude_analyzer = analyzer
298
305
  end
299
306
 
300
307
  if misspellings != false && match_type == :match
@@ -324,7 +331,10 @@ module Searchkick
324
331
  options[:exclude].map do |phrase|
325
332
  {
326
333
  match_phrase: {
327
- field => phrase
334
+ exclude_field => {
335
+ query: phrase,
336
+ analyzer: exclude_analyzer
337
+ }
328
338
  }
329
339
  }
330
340
  end
@@ -3,7 +3,8 @@ module Searchkick
3
3
  RECORD_NOT_FOUND_CLASSES = [
4
4
  "ActiveRecord::RecordNotFound",
5
5
  "Mongoid::Errors::DocumentNotFound",
6
- "NoBrainer::Error::DocumentNotFound"
6
+ "NoBrainer::Error::DocumentNotFound",
7
+ "Cequel::Record::RecordNotFound"
7
8
  ]
8
9
 
9
10
  queue_as :searchkick
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "2.1.1"
2
+ VERSION = "2.2.0"
3
3
  end
data/lib/searchkick.rb CHANGED
@@ -81,7 +81,17 @@ module Searchkick
81
81
  end
82
82
 
83
83
  def self.search(term = "*", **options, &block)
84
- query = Searchkick::Query.new(nil, term, options)
84
+ klass = options[:model]
85
+
86
+ # TODO add in next major version
87
+ # if !klass
88
+ # index_name = Array(options[:index_name])
89
+ # if index_name.size == 1 && index_name.first.respond_to?(:searchkick_index)
90
+ # klass = index_name.first
91
+ # end
92
+ # end
93
+
94
+ query = Searchkick::Query.new(klass, term, options.except(:model))
85
95
  block.call(query.body) if block
86
96
  if options[:execute] == false
87
97
  query
@@ -169,6 +179,8 @@ module Searchkick
169
179
  elsif records.respond_to?(:unscoped) && :id.respond_to?(:in)
170
180
  # Nobrainer
171
181
  records.unscoped.where(:id.in => ids)
182
+ elsif records.respond_to?(:key_column_names)
183
+ records.where(records.key_column_names.first => ids)
172
184
  end
173
185
 
174
186
  raise Searchkick::Error, "Not sure how to load records" if !records
data/test/aggs_test.rb CHANGED
@@ -6,8 +6,8 @@ class AggsTest < Minitest::Test
6
6
  store [
7
7
  {name: "Product Show", latitude: 37.7833, longitude: 12.4167, store_id: 1, in_stock: true, color: "blue", price: 21, created_at: 2.days.ago},
8
8
  {name: "Product Hide", latitude: 29.4167, longitude: -98.5000, store_id: 2, in_stock: false, color: "green", price: 25, created_at: 2.days.from_now},
9
- {name: "Product B", latitude: 43.9333, longitude: -122.4667, store_id: 2, in_stock: false, color: "red", price: 5},
10
- {name: "Foo", latitude: 43.9333, longitude: 12.4667, store_id: 3, in_stock: false, color: "yellow", price: 15}
9
+ {name: "Product B", latitude: 43.9333, longitude: -122.4667, store_id: 2, in_stock: false, color: "red", price: 5, created_at: Time.now},
10
+ {name: "Foo", latitude: 43.9333, longitude: 12.4667, store_id: 3, in_stock: false, color: "yellow", price: 15, created_at: Time.now}
11
11
  ]
12
12
  end
13
13
 
data/test/boost_test.rb CHANGED
@@ -86,6 +86,14 @@ class BoostTest < Minitest::Test
86
86
  assert_order "red", ["Red", "White"], fields: [{"name^10" => :word_start}, "color"]
87
87
  end
88
88
 
89
+ # for issue #855
90
+ def test_apostrophes
91
+ store_names ["Valentine's Day Special"]
92
+ assert_search "Valentines", ["Valentine's Day Special"], fields: ["name^5"]
93
+ assert_search "Valentine's", ["Valentine's Day Special"], fields: ["name^5"]
94
+ assert_search "Valentine", ["Valentine's Day Special"], fields: ["name^5"]
95
+ end
96
+
89
97
  def test_boost_by
90
98
  store [
91
99
  {name: "Tomato A"},
@@ -157,6 +165,8 @@ class BoostTest < Minitest::Test
157
165
  end
158
166
 
159
167
  def test_boost_by_indices
168
+ skip if cequel?
169
+
160
170
  store_names ["Rex"], Animal
161
171
  store_names ["Rexx"], Product
162
172
 
data/test/errors_test.rb CHANGED
@@ -2,8 +2,8 @@ require_relative "test_helper"
2
2
 
3
3
  class ErrorsTest < Minitest::Test
4
4
  def test_bulk_import_raises_error
5
- valid_dog = Dog.new(name: "2016-01-02")
6
- invalid_dog = Dog.new(name: "Ol' One-Leg")
5
+ valid_dog = Product.new(name: "2016-01-02")
6
+ invalid_dog = Product.new(name: "Ol' One-Leg")
7
7
  index = Searchkick::Index.new "dogs", mappings: {
8
8
  dog: {
9
9
  properties: {
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in searchkick.gemspec
4
+ gemspec path: "../../"
5
+
6
+ gem "sqlite3"
7
+ gem "activerecord", "~> 5.1.0.beta1"
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in searchkick.gemspec
4
+ gemspec path: "../../"
5
+
6
+ gem "cequel"
7
+ gem "activejob"
8
+ gem "redis"
@@ -46,6 +46,11 @@ class HighlightTest < Minitest::Test
46
46
  assert_equal "&lt;b&gt;<em>Hello</em>&lt;&#x2F;b&gt;", Product.search("hello", fields: [:name], highlight: {encoder: "html"}, misspellings: false).first.search_highlights[:name]
47
47
  end
48
48
 
49
+ def test_word_middle
50
+ store_names ["Two Door Cinema Club"]
51
+ assert_equal "Two Door <em>Cinema</em> Club", Product.search("ine", fields: [:name], match: :word_middle, highlight: true).first.search_highlights[:name]
52
+ end
53
+
49
54
  def test_body
50
55
  skip if ENV["MATCH"] == "word_start"
51
56
  store_names ["Two Door Cinema Club"]
data/test/index_test.rb CHANGED
@@ -1,6 +1,11 @@
1
1
  require_relative "test_helper"
2
2
 
3
3
  class IndexTest < Minitest::Test
4
+ def setup
5
+ super
6
+ Region.destroy_all
7
+ end
8
+
4
9
  def test_clean_indices
5
10
  old_index = Searchkick::Index.new("products_test_20130801000000000")
6
11
  different_index = Searchkick::Index.new("items_test_20130801000000000")
@@ -127,8 +132,20 @@ class IndexTest < Minitest::Test
127
132
 
128
133
  def test_large_value
129
134
  skip if nobrainer?
135
+ large_value = 1000.times.map { "hello" }.join(" ")
136
+ store [{name: "Product A", text: large_value}], Region
137
+ assert_search "product", ["Product A"], {}, Region
138
+ assert_search "hello", ["Product A"], {fields: [:name, :text]}, Region
139
+ assert_search "hello", ["Product A"], {}, Region
140
+ end
141
+
142
+ def test_very_large_value
143
+ skip if nobrainer? || elasticsearch_below22?
130
144
  large_value = 10000.times.map { "hello" }.join(" ")
131
- store [{name: "Product A", alt_description: large_value}]
132
- assert_search "product", ["Product A"]
145
+ store [{name: "Product A", text: large_value}], Region
146
+ assert_search "product", ["Product A"], {}, Region
147
+ assert_search "hello", ["Product A"], {fields: [:name, :text]}, Region
148
+ # values that exceed ignore_above are not included in _all field :(
149
+ # assert_search "hello", ["Product A"], {}, Region
133
150
  end
134
151
  end
@@ -1,6 +1,11 @@
1
1
  require_relative "test_helper"
2
2
 
3
3
  class InheritanceTest < Minitest::Test
4
+ def setup
5
+ skip if defined?(Cequel)
6
+ super
7
+ end
8
+
4
9
  def test_child_reindex
5
10
  store_names ["Max"], Cat
6
11
  assert Dog.reindex
data/test/match_test.rb CHANGED
@@ -161,21 +161,31 @@ class MatchTest < Minitest::Test
161
161
 
162
162
  # butter
163
163
 
164
- def test_butter
164
+ def test_exclude_butter
165
165
  store_names ["Butter Tub", "Peanut Butter Tub"]
166
166
  assert_search "butter", ["Butter Tub"], exclude: ["peanut butter"]
167
167
  end
168
168
 
169
- def test_butter_word_start
169
+ def test_exclude_butter_word_start
170
170
  store_names ["Butter Tub", "Peanut Butter Tub"]
171
171
  assert_search "butter", ["Butter Tub"], exclude: ["peanut butter"], match: :word_start
172
172
  end
173
173
 
174
- def test_butter_exact
174
+ def test_exclude_butter_exact
175
175
  store_names ["Butter Tub", "Peanut Butter Tub"]
176
176
  assert_search "butter", [], exclude: ["peanut butter"], match: :exact
177
177
  end
178
178
 
179
+ def test_exclude_same_exact
180
+ store_names ["Butter Tub", "Peanut Butter Tub"]
181
+ assert_search "Butter Tub", [], exclude: ["Butter Tub"], match: :exact
182
+ end
183
+
184
+ def test_exclude_egg_word_start
185
+ store_names ["eggs", "eggplant"]
186
+ assert_search "egg", ["eggs"], exclude: ["eggplant"], match: :word_start
187
+ end
188
+
179
189
  # other
180
190
 
181
191
  def test_all
@@ -184,6 +194,7 @@ class MatchTest < Minitest::Test
184
194
  end
185
195
 
186
196
  def test_no_arguments
197
+ store_names []
187
198
  assert_equal [], Product.search.to_a
188
199
  end
189
200
 
data/test/model_test.rb CHANGED
@@ -37,6 +37,6 @@ class ModelTest < Minitest::Test
37
37
  def test_multiple_models
38
38
  store_names ["Product A"]
39
39
  store_names ["Product B"], Speaker
40
- assert_equal Product.all + Speaker.all, Searchkick.search("product", index_name: [Product, Speaker], fields: [:name], order: "name").to_a
40
+ assert_equal Product.all.to_a + Speaker.all.to_a, Searchkick.search("product", index_name: [Product, Speaker], fields: [:name], order: "name").to_a
41
41
  end
42
42
  end
data/test/order_test.rb CHANGED
@@ -12,6 +12,8 @@ class OrderTest < Minitest::Test
12
12
  end
13
13
 
14
14
  def test_order_id
15
+ skip if cequel?
16
+
15
17
  store_names ["Product A", "Product B"]
16
18
  product_a = Product.where(name: "Product A").first
17
19
  product_b = Product.where(name: "Product B").first
data/test/records_test.rb CHANGED
@@ -2,6 +2,8 @@ require_relative "test_helper"
2
2
 
3
3
  class RecordsTest < Minitest::Test
4
4
  def test_records
5
+ skip if cequel?
6
+
5
7
  store_names ["Milk", "Apple"]
6
8
  assert_equal Product.search("milk").records.where(name: "Milk").count, 1
7
9
  end
data/test/reindex_test.rb CHANGED
@@ -2,7 +2,7 @@ require_relative "test_helper"
2
2
 
3
3
  class ReindexTest < Minitest::Test
4
4
  def test_scoped
5
- skip if nobrainer?
5
+ skip if nobrainer? || cequel?
6
6
 
7
7
  store_names ["Product A"]
8
8
  Searchkick.callbacks(false) do
@@ -13,7 +13,7 @@ class ReindexTest < Minitest::Test
13
13
  end
14
14
 
15
15
  def test_associations
16
- skip if nobrainer?
16
+ skip if nobrainer? || cequel?
17
17
 
18
18
  store_names ["Product A"]
19
19
  store = Store.create!(name: "Test")
data/test/test_helper.rb CHANGED
@@ -45,6 +45,10 @@ def nobrainer?
45
45
  defined?(NoBrainer)
46
46
  end
47
47
 
48
+ def cequel?
49
+ defined?(Cequel)
50
+ end
51
+
48
52
  if defined?(Mongoid)
49
53
  Mongoid.logger.level = Logger::INFO
50
54
  Mongo::Logger.logger.level = Logger::INFO if defined?(Mongo::Logger)
@@ -162,6 +166,88 @@ elsif defined?(NoBrainer)
162
166
 
163
167
  class Cat < Animal
164
168
  end
169
+ elsif defined?(Cequel)
170
+ cequel =
171
+ Cequel.connect(
172
+ host: "127.0.0.1",
173
+ port: 9042,
174
+ keyspace: "searchkick_test",
175
+ default_consistency: :all
176
+ )
177
+ # cequel.logger = ActiveSupport::Logger.new(STDOUT)
178
+ cequel.schema.drop! if cequel.schema.exists?
179
+ cequel.schema.create!
180
+ Cequel::Record.connection = cequel
181
+
182
+ class Product
183
+ include Cequel::Record
184
+
185
+ key :id, :uuid, auto: true
186
+ column :name, :text, index: true
187
+ column :store_id, :int
188
+ column :in_stock, :boolean
189
+ column :backordered, :boolean
190
+ column :orders_count, :int
191
+ column :found_rate, :decimal
192
+ column :price, :int
193
+ column :color, :text
194
+ column :latitude, :decimal
195
+ column :longitude, :decimal
196
+ column :description, :text
197
+ column :alt_description, :text
198
+ column :created_at, :timestamp
199
+ end
200
+
201
+ class Store
202
+ include Cequel::Record
203
+
204
+ key :id, :timeuuid, auto: true
205
+ column :name, :text
206
+
207
+ # has issue with id serialization
208
+ def search_data
209
+ {
210
+ name: name
211
+ }
212
+ end
213
+ end
214
+
215
+ class Region
216
+ include Cequel::Record
217
+
218
+ key :id, :timeuuid, auto: true
219
+ column :name, :text
220
+ column :text, :text
221
+ end
222
+
223
+ class Speaker
224
+ include Cequel::Record
225
+
226
+ key :id, :timeuuid, auto: true
227
+ column :name, :text
228
+ end
229
+
230
+ class Animal
231
+ include Cequel::Record
232
+
233
+ key :id, :timeuuid, auto: true
234
+ column :name, :text
235
+
236
+ # has issue with id serialization
237
+ def search_data
238
+ {
239
+ name: name
240
+ }
241
+ end
242
+ end
243
+
244
+ class Dog < Animal
245
+ end
246
+
247
+ class Cat < Animal
248
+ end
249
+
250
+ [Product, Store, Region, Speaker, Animal].each(&:synchronize_schema)
165
251
  else
166
252
  require "active_record"
167
253
 
data/test/where_test.rb CHANGED
@@ -10,16 +10,22 @@ class WhereTest < Minitest::Test
10
10
  {name: "Product D", store_id: 4, in_stock: false, backordered: false, created_at: now - 3, orders_count: 1}
11
11
  ]
12
12
  assert_search "product", ["Product A", "Product B"], where: {in_stock: true}
13
- # date
14
- assert_search "product", ["Product A"], where: {created_at: {gt: now - 1}}
15
- assert_search "product", ["Product A", "Product B"], where: {created_at: {gte: now - 1}}
16
- assert_search "product", ["Product D"], where: {created_at: {lt: now - 2}}
17
- assert_search "product", ["Product C", "Product D"], where: {created_at: {lte: now - 2}}
13
+
14
+ # due to precision
15
+ unless cequel?
16
+ # date
17
+ assert_search "product", ["Product A"], where: {created_at: {gt: now - 1}}
18
+ assert_search "product", ["Product A", "Product B"], where: {created_at: {gte: now - 1}}
19
+ assert_search "product", ["Product D"], where: {created_at: {lt: now - 2}}
20
+ assert_search "product", ["Product C", "Product D"], where: {created_at: {lte: now - 2}}
21
+ end
22
+
18
23
  # integer
19
24
  assert_search "product", ["Product A"], where: {store_id: {lt: 2}}
20
25
  assert_search "product", ["Product A", "Product B"], where: {store_id: {lte: 2}}
21
26
  assert_search "product", ["Product D"], where: {store_id: {gt: 3}}
22
27
  assert_search "product", ["Product C", "Product D"], where: {store_id: {gte: 3}}
28
+
23
29
  # range
24
30
  assert_search "product", ["Product A", "Product B"], where: {store_id: 1..2}
25
31
  assert_search "product", ["Product A"], where: {store_id: 1...2}
@@ -27,23 +33,30 @@ class WhereTest < Minitest::Test
27
33
  assert_search "product", ["Product B", "Product C", "Product D"], where: {store_id: {not: 1}}
28
34
  assert_search "product", ["Product C", "Product D"], where: {store_id: {not: [1, 2]}}
29
35
  assert_search "product", ["Product A"], where: {user_ids: {lte: 2, gte: 2}}
36
+
30
37
  # or
31
38
  assert_search "product", ["Product A", "Product B", "Product C"], where: {or: [[{in_stock: true}, {store_id: 3}]]}
32
39
  assert_search "product", ["Product A", "Product B", "Product C"], where: {or: [[{orders_count: [2, 4]}, {store_id: [1, 2]}]]}
33
40
  assert_search "product", ["Product A", "Product D"], where: {or: [[{orders_count: 1}, {created_at: {gte: now - 1}, backordered: true}]]}
41
+
34
42
  # _or
35
43
  assert_search "product", ["Product A", "Product B", "Product C"], where: {_or: [{in_stock: true}, {store_id: 3}]}
36
44
  assert_search "product", ["Product A", "Product B", "Product C"], where: {_or: [{orders_count: [2, 4]}, {store_id: [1, 2]}]}
37
45
  assert_search "product", ["Product A", "Product D"], where: {_or: [{orders_count: 1}, {created_at: {gte: now - 1}, backordered: true}]}
46
+
38
47
  # _and
39
48
  assert_search "product", ["Product A"], where: {_and: [{in_stock: true}, {backordered: true}]}
49
+
40
50
  # _not
41
51
  assert_search "product", ["Product B", "Product C"], where: {_not: {_or: [{orders_count: 1}, {created_at: {gte: now - 1}, backordered: true}]}}
52
+
42
53
  # all
43
54
  assert_search "product", ["Product A", "Product C"], where: {user_ids: {all: [1, 3]}}
44
55
  assert_search "product", [], where: {user_ids: {all: [1, 2, 3, 4]}}
56
+
45
57
  # any / nested terms
46
58
  assert_search "product", ["Product B", "Product C"], where: {user_ids: {not: [2], in: [1, 3]}}
59
+
47
60
  # not / exists
48
61
  assert_search "product", ["Product D"], where: {user_ids: nil}
49
62
  assert_search "product", ["Product A", "Product B", "Product C"], where: {user_ids: {not: nil}}
@@ -78,7 +91,7 @@ class WhereTest < Minitest::Test
78
91
 
79
92
  def test_where_id
80
93
  store_names ["Product A"]
81
- product = Product.last
94
+ product = Product.first
82
95
  assert_search "product", ["Product A"], where: {id: product.id.to_s}
83
96
  end
84
97
 
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: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-18 00:00:00.000000000 Z
11
+ date: 2017-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -138,7 +138,9 @@ files:
138
138
  - test/gemfiles/activerecord40.gemfile
139
139
  - test/gemfiles/activerecord41.gemfile
140
140
  - test/gemfiles/activerecord42.gemfile
141
+ - test/gemfiles/activerecord51.gemfile
141
142
  - test/gemfiles/apartment.gemfile
143
+ - test/gemfiles/cequel.gemfile
142
144
  - test/gemfiles/mongoid2.gemfile
143
145
  - test/gemfiles/mongoid3.gemfile
144
146
  - test/gemfiles/mongoid4.gemfile
@@ -209,7 +211,9 @@ test_files:
209
211
  - test/gemfiles/activerecord40.gemfile
210
212
  - test/gemfiles/activerecord41.gemfile
211
213
  - test/gemfiles/activerecord42.gemfile
214
+ - test/gemfiles/activerecord51.gemfile
212
215
  - test/gemfiles/apartment.gemfile
216
+ - test/gemfiles/cequel.gemfile
213
217
  - test/gemfiles/mongoid2.gemfile
214
218
  - test/gemfiles/mongoid3.gemfile
215
219
  - test/gemfiles/mongoid4.gemfile