Commit 723eb52dce591c07dc73bb4e484cdb5a1d8d5d53
1 parent
aafc2c33
Exists in
master
and in
21 other branches
Added support for single table inheritance - closes #49 and #30
Showing
5 changed files
with
79 additions
and
22 deletions
Show diff stats
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["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 |
... | ... | @@ -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 | ... | ... |