searchkick 0.3.1 → 0.3.2

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: 1da092cc431fe04831d998435718459f8bebf261
4
- data.tar.gz: 21a86486b94f81e6b9b9061fb6cf051ac7672650
3
+ metadata.gz: ff1b17b6561440bad2ebe3c20d1606b13d579310
4
+ data.tar.gz: 50475abfd988e11bca0b04385a4ec264bac80467
5
5
  SHA512:
6
- metadata.gz: 2b8bbdb5158b5c39994277ab80e46a14b222b5dd24a22204dcc3acb8d3b853b50b692e77a1b634774e5611ca930b1e495428200e9ddcd726254af7db3820a251
7
- data.tar.gz: 3661b1fbb97fd864e524745b2f7435486aeb4cec005381e01a7c37ed6683b38e55b5a5921d60635d46d1700012d18b7662abb99545ee655c635882b9b5b322d4
6
+ metadata.gz: 19e25e2f81fb811f7bf1964cb59a5deccfc6386d10640279bfe6cfb7bb81a1611691ebd6ccb8879c7dcbec4f530d5ccf2fae089e19cc8f783409985092d2315b
7
+ data.tar.gz: be76d99160e570515a3120ecb517a6faef29f2b3b95eb4ebc6556cbe2839d3554faa7295ff72ccc14caf82fb817b65e2286a74377fd95137d68c6666383bc26f
@@ -1,3 +1,8 @@
1
+ ## 0.3.2
2
+
3
+ - Added support for single table inheritance
4
+ - Removed Tire::Model::Search
5
+
1
6
  ## 0.3.1
2
7
 
3
8
  - Added index_prefix option
data/README.md CHANGED
@@ -360,7 +360,7 @@ class City < ActiveRecord::Base
360
360
  searchkick locations: ["location"]
361
361
 
362
362
  def search_data
363
- to_hash.merge location: [latitude.to_f, longitude.to_f]
363
+ to_hash.merge location: [latitude, longitude]
364
364
  end
365
365
  end
366
366
  ```
@@ -419,6 +419,29 @@ Then deploy and reindex:
419
419
  rake searchkick:reindex CLASS=Product
420
420
  ```
421
421
 
422
+ ## Inheritance
423
+
424
+ Searchkick supports single table inheritance.
425
+
426
+ ```ruby
427
+ class Dog < Animal
428
+ end
429
+ ```
430
+
431
+ The parent and child model can both reindex.
432
+
433
+ ```ruby
434
+ Animal.reindex
435
+ Dog.reindex # equivalent
436
+ ```
437
+
438
+ And to search, use:
439
+
440
+ ```ruby
441
+ Animal.search "*" # all animals
442
+ Dog.search "*" # just dogs
443
+ ```
444
+
422
445
  ## Reference
423
446
 
424
447
  Searchkick requires Elasticsearch `0.90.0` or higher.
@@ -2,26 +2,39 @@ module Searchkick
2
2
  module Model
3
3
 
4
4
  def searchkick(options = {})
5
- @searchkick_options = options.dup
6
- @searchkick_env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
7
- searchkick_env = @searchkick_env # for class_eval
8
-
9
5
  class_eval do
6
+ cattr_reader :searchkick_options, :searchkick_env, :searchkick_klass, :searchkick_index
7
+
8
+ class_variable_set :@@searchkick_options, options.dup
9
+ class_variable_set :@@searchkick_env, ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
10
+ class_variable_set :@@searchkick_klass, self
11
+
12
+ # set index name
13
+ # TODO support proc
14
+ index_name = options[:index_name] || [options[:index_prefix], model_name.plural, searchkick_env].compact.join("_")
15
+ class_variable_set :@@searchkick_index, Tire::Index.new(index_name)
16
+
10
17
  extend Searchkick::Search
11
18
  extend Searchkick::Reindex
12
19
  include Searchkick::Similar
13
- include Tire::Model::Search
14
- include Tire::Model::Callbacks unless options[:callbacks] == false
15
- tire do
16
- index_name options[:index_name] || [options[:index_prefix], klass.model_name.plural, searchkick_env].compact.join("_")
17
- end
18
20
 
19
21
  def reindex
20
- tire.update_index
22
+ index = self.class.searchkick_index
23
+ if destroyed?
24
+ index.remove self
25
+ else
26
+ index.store self
27
+ end
28
+ end
29
+
30
+ unless options[:callbacks] == false
31
+ # TODO ability to temporarily disable
32
+ after_save :reindex
33
+ after_destroy :reindex
21
34
  end
22
35
 
23
36
  def search_data
24
- to_hash.reject{|k, v| k == "id" }
37
+ respond_to?(:to_hash) ? to_hash : serializable_hash
25
38
  end
26
39
 
27
40
  def to_indexed_json
@@ -30,7 +43,7 @@ module Searchkick
30
43
  # stringify fields
31
44
  source = source.inject({}){|memo,(k,v)| memo[k.to_s] = v; memo}
32
45
 
33
- options = self.class.instance_variable_get("@searchkick_options")
46
+ options = self.class.searchkick_options
34
47
 
35
48
  # conversions
36
49
  conversions_field = options[:conversions]
@@ -45,11 +58,22 @@ module Searchkick
45
58
 
46
59
  # locations
47
60
  (options[:locations] || []).map(&:to_s).each do |field|
48
- source[field] = source[field].reverse if source[field]
61
+ source[field] = source[field].map(&:to_f).reverse if source[field]
49
62
  end
50
63
 
51
64
  source.to_json
52
65
  end
66
+
67
+ # TODO remove
68
+
69
+ def self.document_type
70
+ model_name.to_s.underscore
71
+ end
72
+
73
+ def document_type
74
+ self.class.document_type
75
+ end
76
+
53
77
  end
54
78
  end
55
79
 
@@ -3,7 +3,7 @@ module Searchkick
3
3
 
4
4
  # https://gist.github.com/jarosan/3124884
5
5
  def reindex
6
- alias_name = tire.index.name
6
+ alias_name = searchkick_index.name
7
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
 
@@ -28,7 +28,7 @@ module Searchkick
28
28
  raise response.to_s
29
29
  end
30
30
  else
31
- tire.index.delete if tire.index.exists?
31
+ searchkick_index.delete if searchkick_index.exists?
32
32
  response = Tire::Alias.create(name: alias_name, indices: [new_index])
33
33
  raise response.to_s if !response.success?
34
34
 
@@ -41,7 +41,7 @@ module Searchkick
41
41
  # remove old indices that start w/ index_name
42
42
  def clean_indices
43
43
  all_indices = JSON.parse(Tire::Configuration.client.get("#{Tire::Configuration.url}/_aliases").body)
44
- indices = all_indices.select{|k, v| v["aliases"].empty? && k =~ /\A#{Regexp.escape(tire.index.name)}_\d{14,17}\z/ }.keys
44
+ indices = all_indices.select{|k, v| v["aliases"].empty? && k =~ /\A#{Regexp.escape(searchkick_index.name)}_\d{14,17}\z/ }.keys
45
45
  indices.each do |index|
46
46
  Tire::Index.new(index).delete
47
47
  end
@@ -56,7 +56,8 @@ module Searchkick
56
56
 
57
57
  def searchkick_import(index)
58
58
  # use scope for import
59
- scope = respond_to?(:search_import) ? search_import : self
59
+ scope = searchkick_klass
60
+ scope = scope.search_import if scope.respond_to?(:search_import)
60
61
  if scope.respond_to?(:find_in_batches)
61
62
  scope.find_in_batches do |batch|
62
63
  index.import batch
@@ -77,7 +78,7 @@ module Searchkick
77
78
  end
78
79
 
79
80
  def searchkick_index_options
80
- options = @searchkick_options
81
+ options = searchkick_options
81
82
 
82
83
  settings = {
83
84
  analysis: {
@@ -148,7 +149,7 @@ module Searchkick
148
149
  }
149
150
  }
150
151
 
151
- if @searchkick_env == "test"
152
+ if searchkick_env == "test"
152
153
  settings.merge!(number_of_shards: 1, number_of_replicas: 0)
153
154
  end
154
155
 
@@ -220,7 +221,7 @@ module Searchkick
220
221
  end
221
222
 
222
223
  mappings = {
223
- document_type.to_sym => {
224
+ searchkick_klass.document_type.to_sym => {
224
225
  properties: mapping,
225
226
  # https://gist.github.com/kimchy/2898285
226
227
  dynamic_templates: [
@@ -12,7 +12,7 @@ module Searchkick
12
12
  end
13
13
  else
14
14
  if options[:autocomplete]
15
- (@searchkick_options[:autocomplete] || []).map{|f| "#{f}.autocomplete" }
15
+ (searchkick_options[:autocomplete] || []).map{|f| "#{f}.autocomplete" }
16
16
  else
17
17
  ["_all"]
18
18
  end
@@ -28,10 +28,10 @@ module Searchkick
28
28
  page = [options[:page].to_i, 1].max
29
29
  per_page = (options[:limit] || options[:per_page] || 100000).to_i
30
30
  offset = options[:offset] || (page - 1) * per_page
31
- index_name = options[:index_name] || tire.index.name
31
+ index_name = options[:index_name] || searchkick_index.name
32
32
 
33
- conversions_field = @searchkick_options[:conversions]
34
- personalize_field = @searchkick_options[:personalize]
33
+ conversions_field = searchkick_options[:conversions]
34
+ personalize_field = searchkick_options[:personalize]
35
35
 
36
36
  all = term == "*"
37
37
 
@@ -176,7 +176,7 @@ module Searchkick
176
176
  if value[:near]
177
177
  filters << {
178
178
  geo_distance: {
179
- field => value.delete(:near).reverse,
179
+ field => value.delete(:near).map(&:to_f).reverse,
180
180
  distance: value.delete(:within) || "50mi"
181
181
  }
182
182
  }
@@ -186,8 +186,8 @@ module Searchkick
186
186
  filters << {
187
187
  geo_bounding_box: {
188
188
  field => {
189
- top_left: value.delete(:top_left).reverse,
190
- bottom_right: value.delete(:bottom_right).reverse
189
+ top_left: value.delete(:top_left).map(&:to_f).reverse,
190
+ bottom_right: value.delete(:bottom_right).map(&:to_f).reverse
191
191
  }
192
192
  }
193
193
  }
@@ -269,7 +269,7 @@ module Searchkick
269
269
 
270
270
  # suggestions
271
271
  if options[:suggest]
272
- suggest_fields = (@searchkick_options[:suggest] || []).map(&:to_s)
272
+ suggest_fields = (searchkick_options[:suggest] || []).map(&:to_s)
273
273
  # intersection
274
274
  suggest_fields = suggest_fields & options[:fields].map(&:to_s) if options[:fields]
275
275
  if suggest_fields.any?
@@ -288,7 +288,9 @@ module Searchkick
288
288
  # http://www.elasticsearch.org/guide/reference/api/search/fields/
289
289
  payload[:fields] = [] if load
290
290
 
291
- search = Tire::Search::Search.new(index_name, load: load, payload: payload, size: per_page, from: offset)
291
+ tire_options = {load: load, payload: payload, size: per_page, from: offset}
292
+ tire_options[:type] = document_type if self != searchkick_klass
293
+ search = Tire::Search::Search.new(index_name, tire_options)
292
294
  response = search.json
293
295
 
294
296
  # apply facet limit in client due to
@@ -2,7 +2,7 @@ module Searchkick
2
2
  module Similar
3
3
 
4
4
  def similar(options = {})
5
- like_text = index.retrieve(document_type, id).to_hash
5
+ like_text = self.class.searchkick_index.retrieve(document_type, id).to_hash
6
6
  .keep_if{|k,v| k[0] != "_" and (!options[:fields] or options[:fields].map(&:to_sym).include?(k)) }
7
7
  .values.compact.join(" ")
8
8
 
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -12,7 +12,7 @@ class TestIndex < Minitest::Unit::TestCase
12
12
 
13
13
  Product.clean_indices
14
14
 
15
- assert Product.tire.index.exists?
15
+ assert Product.searchkick_index.exists?
16
16
  assert different_index.exists?
17
17
  assert !old_index.exists?
18
18
  end
@@ -0,0 +1,33 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestInheritance < Minitest::Unit::TestCase
4
+
5
+ def setup
6
+ super
7
+ Animal.destroy_all
8
+ end
9
+
10
+ def test_child_reindex
11
+ store_names ["Max"], Cat
12
+ assert Dog.reindex
13
+ Animal.searchkick_index.refresh
14
+ assert_equal 1, Animal.search("*").size
15
+ end
16
+
17
+ def test_child_index_name
18
+ assert_equal "animals_test", Dog.searchkick_index.name
19
+ end
20
+
21
+ def test_child_search
22
+ store_names ["Bear"], Dog
23
+ store_names ["Bear"], Cat
24
+ assert_equal 1, Dog.search("bear").size
25
+ end
26
+
27
+ def test_parent_search
28
+ store_names ["Bear"], Dog
29
+ store_names ["Bear"], Cat
30
+ assert_equal 2, Animal.search("bear").size
31
+ end
32
+
33
+ end
@@ -24,6 +24,16 @@ if ENV["MONGOID"]
24
24
  class Store
25
25
  include Mongoid::Document
26
26
  end
27
+
28
+ class Animal
29
+ include Mongoid::Document
30
+ end
31
+
32
+ class Dog < Animal
33
+ end
34
+
35
+ class Cat < Animal
36
+ end
27
37
  else
28
38
  require "active_record"
29
39
 
@@ -49,7 +59,12 @@ else
49
59
  t.timestamps
50
60
  end
51
61
 
52
- ActiveRecord::Migration.create_table :store, :force => true do |t|
62
+ ActiveRecord::Migration.create_table :stores, :force => true do |t|
63
+ end
64
+
65
+ ActiveRecord::Migration.create_table :animals, :force => true do |t|
66
+ t.string :name
67
+ t.string :type
53
68
  end
54
69
 
55
70
  class Product < ActiveRecord::Base
@@ -57,6 +72,15 @@ else
57
72
 
58
73
  class Store < ActiveRecord::Base
59
74
  end
75
+
76
+ class Animal < ActiveRecord::Base
77
+ end
78
+
79
+ class Dog < Animal
80
+ end
81
+
82
+ class Cat < Animal
83
+ end
60
84
  end
61
85
 
62
86
  class Product
@@ -80,14 +104,20 @@ class Product
80
104
  attr_accessor :conversions, :user_ids
81
105
 
82
106
  def search_data
83
- to_hash.merge conversions: conversions, user_ids: user_ids, location: [latitude.to_f, longitude.to_f]
107
+ attributes.merge conversions: conversions, user_ids: user_ids, location: [latitude, longitude]
84
108
  end
85
109
  end
86
110
 
87
- Product.tire.index.delete if Product.tire.index.exists?
111
+ class Animal
112
+ searchkick
113
+ end
114
+
115
+ Product.searchkick_index.delete if Product.searchkick_index.exists?
88
116
  Product.reindex
89
117
  Product.reindex # run twice for both index paths
90
118
 
119
+ Animal.reindex
120
+
91
121
  class MiniTest::Unit::TestCase
92
122
 
93
123
  def setup
@@ -96,15 +126,15 @@ class MiniTest::Unit::TestCase
96
126
 
97
127
  protected
98
128
 
99
- def store(documents)
129
+ def store(documents, klass = Product)
100
130
  documents.shuffle.each do |document|
101
- Product.create!(document)
131
+ klass.create!(document)
102
132
  end
103
- Product.tire.index.refresh
133
+ klass.searchkick_index.refresh
104
134
  end
105
135
 
106
- def store_names(names)
107
- store names.map{|name| {name: name} }
136
+ def store_names(names, klass = Product)
137
+ store names.map{|name| {name: name} }, klass
108
138
  end
109
139
 
110
140
  # no order
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.3.1
4
+ version: 0.3.2
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-11-02 00:00:00.000000000 Z
11
+ date: 2013-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tire
@@ -136,6 +136,7 @@ files:
136
136
  - test/boost_test.rb
137
137
  - test/facets_test.rb
138
138
  - test/index_test.rb
139
+ - test/inheritance_test.rb
139
140
  - test/match_test.rb
140
141
  - test/similar_test.rb
141
142
  - test/sql_test.rb
@@ -171,6 +172,7 @@ test_files:
171
172
  - test/boost_test.rb
172
173
  - test/facets_test.rb
173
174
  - test/index_test.rb
175
+ - test/inheritance_test.rb
174
176
  - test/match_test.rb
175
177
  - test/similar_test.rb
176
178
  - test/sql_test.rb