Commit 723eb52dce591c07dc73bb4e484cdb5a1d8d5d53

Authored by Andrew Kane
1 parent aafc2c33

Added support for single table inheritance - closes #49 and #30

README.md
... ... @@ -419,6 +419,29 @@ Then deploy and reindex:
419 419 rake searchkick:reindex CLASS=Product
420 420 ```
421 421  
  422 +## Inheritance [master]
  423 +
  424 +Searchkick supports single table inheritance.
  425 +
  426 +```ruby
  427 +class Dog < Animal
  428 +end
  429 +```
  430 +
  431 +The parent or child model can both reindex.
  432 +
  433 +```ruby
  434 +Animal.reindex
  435 +Dog.reindex # equivalent
  436 +```
  437 +
  438 +And searching works like:
  439 +
  440 +```
  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.
... ...
lib/searchkick/model.rb
... ... @@ -2,21 +2,22 @@ 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 + class_variable_set :@@searchkick_options, options.dup
  7 + class_variable_set :@@searchkick_env, ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
  8 + class_variable_set :@@searchkick_klass, self
  9 + cattr_reader :searchkick_options, :searchkick_env, :searchkick_klass
  10 +
10 11 extend Searchkick::Search
11 12 extend Searchkick::Reindex
12 13 include Searchkick::Similar
13 14 include Tire::Model::Search
14   - tire do
15   - index_name options[:index_name] || [options[:index_prefix], klass.model_name.plural, searchkick_env].compact.join("_")
16   - end
17 15  
18   - class << self
19   - attr_reader :searchkick_options
  16 + index_name = options[:index_name] || [options[:index_prefix], model_name.plural, searchkick_env].compact.join("_")
  17 +
  18 + tire.index_name index_name
  19 + descendants.each do |subclass|
  20 + subclass.tire.index_name index_name
20 21 end
21 22  
22 23 def reindex
... ...
lib/searchkick/reindex.rb
... ... @@ -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: {
... ...
lib/searchkick/search.rb
... ... @@ -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
... ... @@ -30,8 +30,8 @@ module Searchkick
30 30 offset = options[:offset] || (page - 1) * per_page
31 31 index_name = options[:index_name] || tire.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  
... ... @@ -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
... ...
test/test_helper.rb
... ... @@ -24,6 +24,16 @@ if ENV[&quot;MONGOID&quot;]
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
... ... @@ -84,10 +108,16 @@ class Product
84 108 end
85 109 end
86 110  
  111 +class Animal
  112 + searchkick
  113 +end
  114 +
87 115 Product.tire.index.delete if Product.tire.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.tire.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
... ...