searchkick 2.1.1 → 2.2.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 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