Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

promote index level operations off of model and into ElasticSearchabl…

…e namespace
  • Loading branch information...
commit af9467a60e1621667a55f2200f4b43eb835f2e91 1 parent c81387b
Ryan Sonnek authored
2  .rvmrc
... ... @@ -1 +1 @@
1   -rvm use ruby-1.9.2@elastic_searchable --create
  1 +rvm use ruby-1.9.3@elastic_searchable --create
34 lib/elastic_searchable.rb
@@ -4,14 +4,13 @@
4 4 require 'elastic_searchable/active_record_extensions'
5 5
6 6 module ElasticSearchable
7   - DEFAULT_INDEX = 'elastic_searchable'
8 7 include HTTParty
9 8 format :json
10 9 base_uri 'localhost:9200'
11 10
12 11 class ElasticError < StandardError; end
13 12 class << self
14   - attr_accessor :logger, :default_index, :offline
  13 + attr_accessor :logger, :index_name, :index_settings, :offline
15 14
16 15 # execute a block of work without reindexing objects
17 16 def offline(&block)
@@ -34,7 +33,7 @@ def encode_json(options = {})
34 33 # ElasticSearchable.debug_output outputs all http traffic to console
35 34 def request(method, url, options = {})
36 35 options.merge! :headers => {'Content-Type' => 'application/json'}
37   - options.merge! :body => ElasticSearchable.encode_json(options.delete(:json_body)) if options[:json_body]
  36 + options.merge! :body => self.encode_json(options.delete(:json_body)) if options[:json_body]
38 37
39 38 response = self.send(method, url, options)
40 39 logger.debug "elasticsearch request: #{method} #{url} #{"took #{response['took']}ms" if response['took']}"
@@ -47,6 +46,33 @@ def escape_query(string)
47 46 string.to_s.gsub(/([\(\)\[\]\{\}\?\\\"!\^\+\-\*:~])/,'\\\\\1')
48 47 end
49 48
  49 + # create the index
  50 + # http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html
  51 + def create_index
  52 + options = {}
  53 + options[:settings] = self.index_settings if self.index_settings
  54 + self.request :put, self.request_path, :json_body => options
  55 + end
  56 +
  57 + # explicitly refresh the index, making all operations performed since the last refresh
  58 + # available for search
  59 + #
  60 + # http://www.elasticsearch.com/docs/elasticsearch/rest_api/admin/indices/refresh/
  61 + def refresh_index
  62 + self.request :post, self.request_path('_refresh')
  63 + end
  64 +
  65 + # deletes the entire index
  66 + # http://www.elasticsearch.com/docs/elasticsearch/rest_api/admin/indices/delete_index/
  67 + def delete_index
  68 + self.request :delete, self.request_path
  69 + end
  70 +
  71 + # helper method to generate elasticsearch url for this index
  72 + def request_path(action = nil)
  73 + ['', index_name, action].compact.join('/')
  74 + end
  75 +
50 76 private
51 77 # all elasticsearch rest calls return a json response when an error occurs. ex:
52 78 # {error: 'an error occurred' }
@@ -63,5 +89,5 @@ def validate_response(response)
63 89
64 90 # configure default index to be elastic_searchable
65 91 # one index can hold many object 'types'
66   -ElasticSearchable.default_index = ElasticSearchable::DEFAULT_INDEX
  92 +ElasticSearchable.index_name = 'elastic_searchable'
67 93
9 lib/elastic_searchable/active_record_extensions.rb
@@ -8,9 +8,7 @@
8 8 module ElasticSearchable
9 9 module ActiveRecordExtensions
10 10 # Valid options:
11   - # :index (optional) configure index to store data in. default to ElasticSearchable.default_index
12 11 # :type (optional) configue type to store data in. default to model table name
13   - # :index_options (optional) configure index properties (ex: tokenizer)
14 12 # :mapping (optional) configure field properties for this model (ex: skip analyzer for field)
15 13 # :if (optional) reference symbol/proc condition to only index when condition is true
16 14 # :unless (optional) reference symbol/proc condition to skip indexing when condition is true
@@ -29,6 +27,13 @@ def elastic_searchable(options = {})
29 27 cattr_accessor :elastic_options
30 28 self.elastic_options = options.symbolize_keys.merge(:unless => Array.wrap(options[:unless]).push(:elasticsearch_offline?))
31 29
  30 + if self.elastic_options[:index_options]
  31 + ActiveSupport::Deprecation.warn ":index_options has been deprecated. Use ElasticSearchable.index_settings instead.", caller
  32 + end
  33 + if self.elastic_options[:index]
  34 + ActiveSupport::Deprecation.warn ":index has been deprecated. Use ElasticSearchable.index_name instead.", caller
  35 + end
  36 +
32 37 extend ElasticSearchable::Indexing::ClassMethods
33 38 extend ElasticSearchable::Queries
34 39
35 lib/elastic_searchable/index.rb
@@ -15,29 +15,6 @@ def update_index_mapping
15 15 end
16 16 end
17 17
18   - # create the index
19   - # http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html
20   - def create_index
21   - options = {}
22   - options.merge! :settings => self.elastic_options[:index_options] if self.elastic_options[:index_options]
23   - options.merge! :mappings => {index_type => self.elastic_options[:mapping]} if self.elastic_options[:mapping]
24   - ElasticSearchable.request :put, index_path, :json_body => options
25   - end
26   -
27   - # explicitly refresh the index, making all operations performed since the last refresh
28   - # available for search
29   - #
30   - # http://www.elasticsearch.com/docs/elasticsearch/rest_api/admin/indices/refresh/
31   - def refresh_index
32   - ElasticSearchable.request :post, index_path('_refresh')
33   - end
34   -
35   - # deletes the entire index
36   - # http://www.elasticsearch.com/docs/elasticsearch/rest_api/admin/indices/delete_index/
37   - def delete_index
38   - ElasticSearchable.request :delete, index_path
39   - end
40   -
41 18 # delete one record from the index
42 19 # http://www.elasticsearch.com/docs/elasticsearch/rest_api/delete/
43 20 def delete_id_from_index(id)
@@ -48,12 +25,7 @@ def delete_id_from_index(id)
48 25
49 26 # helper method to generate elasticsearch url for this object type
50 27 def index_type_path(action = nil)
51   - index_path [index_type, action].compact.join('/')
52   - end
53   -
54   - # helper method to generate elasticsearch url for this index
55   - def index_path(action = nil)
56   - ['', index_name, action].compact.join('/')
  28 + ElasticSearchable.request_path [index_type, action].compact.join('/')
57 29 end
58 30
59 31 # reindex all records using bulk api
@@ -78,7 +50,7 @@ def reindex(options = {})
78 50 next unless record.should_index?
79 51 begin
80 52 doc = ElasticSearchable.encode_json(record.as_json_for_index)
81   - actions << ElasticSearchable.encode_json({:index => {'_index' => index_name, '_type' => index_type, '_id' => record.id}})
  53 + actions << ElasticSearchable.encode_json({:index => {'_index' => ElasticSearchable.index_name, '_type' => index_type, '_id' => record.id}})
82 54 actions << doc
83 55 rescue => e
84 56 ElasticSearchable.logger.warn "Unable to bulk index record: #{record.inspect} [#{e.message}]"
@@ -97,9 +69,6 @@ def reindex(options = {})
97 69 end
98 70
99 71 private
100   - def index_name
101   - self.elastic_options[:index] || ElasticSearchable.default_index
102   - end
103 72 def index_type
104 73 self.elastic_options[:type] || self.table_name
105 74 end
2  test/helper.rb
@@ -19,6 +19,6 @@
19 19
20 20 class Test::Unit::TestCase
21 21 def delete_index
22   - ElasticSearchable.delete '/elastic_searchable' rescue nil
  22 + ElasticSearchable.delete_index rescue nil
23 23 end
24 24 end
86 test/test_elastic_searchable.rb
@@ -3,9 +3,9 @@
3 3 class TestElasticSearchable < Test::Unit::TestCase
4 4 def setup
5 5 delete_index
  6 + ElasticSearchable.index_settings = {'number_of_replicas' => 0, 'number_of_shards' => 1}
  7 + # ElasticSearchable.debug_output
6 8 end
7   - # ElasticSearchable.debug_output
8   - SINGLE_NODE_CLUSTER_CONFIG = {'number_of_replicas' => 0, 'number_of_shards' => 1}
9 9
10 10 context 'non elastic activerecord class' do
11 11 class Parent < ActiveRecord::Base
@@ -29,7 +29,7 @@ class Parent < ActiveRecord::Base
29 29 end
30 30
31 31 class Post < ActiveRecord::Base
32   - elastic_searchable :index_options => SINGLE_NODE_CLUSTER_CONFIG
  32 + elastic_searchable
33 33 after_index :indexed
34 34 after_index :indexed_on_create, :on => :create
35 35 after_index :indexed_on_update, :on => :update
@@ -76,10 +76,10 @@ def indexed_on_update?
76 76 end
77 77 end
78 78
79   - context 'Model.create_index' do
  79 + context 'ElasticSearchable.create_index' do
80 80 setup do
81   - Post.create_index
82   - Post.refresh_index
  81 + ElasticSearchable.create_index
  82 + ElasticSearchable.refresh_index
83 83 @status = ElasticSearchable.request :get, '/elastic_searchable/_status'
84 84 end
85 85 should 'have created index' do
@@ -140,11 +140,11 @@ def indexed_on_update?
140 140 context 'with empty index when multiple database records' do
141 141 setup do
142 142 Post.delete_all
143   - Post.create_index
  143 + ElasticSearchable.create_index
144 144 @first_post = Post.create :title => 'foo', :body => "first bar"
145 145 @second_post = Post.create :title => 'foo', :body => "second bar"
146   - Post.delete_index
147   - Post.create_index
  146 + ElasticSearchable.delete_index
  147 + ElasticSearchable.create_index
148 148 end
149 149 should 'not raise error if error occurs reindexing model' do
150 150 ElasticSearchable.expects(:request).raises(ElasticSearchable::ElasticError.new('faux error'))
@@ -161,7 +161,7 @@ def indexed_on_update?
161 161 context 'Model.reindex' do
162 162 setup do
163 163 Post.reindex :per_page => 1, :scope => Post.scoped(:order => 'body desc')
164   - Post.refresh_index
  164 + ElasticSearchable.refresh_index
165 165 end
166 166 should 'have reindexed both records' do
167 167 assert_nothing_raised do
@@ -174,10 +174,10 @@ def indexed_on_update?
174 174
175 175 context 'with index containing multiple results' do
176 176 setup do
177   - Post.create_index
  177 + ElasticSearchable.create_index
178 178 @first_post = Post.create :title => 'foo', :body => "first bar"
179 179 @second_post = Post.create :title => 'foo', :body => "second bar"
180   - Post.refresh_index
  180 + ElasticSearchable.refresh_index
181 181 end
182 182
183 183 context 'searching for results' do
@@ -267,7 +267,7 @@ def indexed_on_update?
267 267 context 'destroying one object' do
268 268 setup do
269 269 @first_post.destroy
270   - Post.refresh_index
  270 + ElasticSearchable.refresh_index
271 271 end
272 272 should 'be removed from the index' do
273 273 @request = ElasticSearchable.get "/elastic_searchable/posts/#{@first_post.id}"
@@ -278,7 +278,7 @@ def indexed_on_update?
278 278
279 279
280 280 class Blog < ActiveRecord::Base
281   - elastic_searchable :if => proc {|b| b.should_index? }, :index_options => SINGLE_NODE_CLUSTER_CONFIG
  281 + elastic_searchable :if => proc {|b| b.should_index? }
282 282 def should_index?
283 283 false
284 284 end
@@ -297,54 +297,19 @@ def should_index?
297 297 end
298 298 end
299 299
300   - class User < ActiveRecord::Base
301   - elastic_searchable :index_options => {
302   - 'number_of_replicas' => 0,
303   - 'number_of_shards' => 1,
304   - "analysis.analyzer.default.tokenizer" => 'standard',
305   - "analysis.analyzer.default.filter" => ["standard", "lowercase", 'porterStem']},
306   - :mapping => {:properties => {:name => {:type => 'string', :index => 'not_analyzed'}}}
307   - end
308   - context 'activerecord class with :index_options and :mapping' do
309   - context 'creating index' do
310   - setup do
311   - User.create_index
312   - end
313   - should 'have used custom index_options' do
314   - @status = ElasticSearchable.request :get, '/elastic_searchable/_settings'
315   - expected = {
316   - "index.number_of_replicas" => "0",
317   - "index.number_of_shards" => "1",
318   - "index.analysis.analyzer.default.tokenizer" => "standard",
319   - "index.analysis.analyzer.default.filter.0" => "standard",
320   - "index.analysis.analyzer.default.filter.1" => "lowercase",
321   - "index.analysis.analyzer.default.filter.2" => "porterStem"
322   - }
323   - assert_equal expected, @status['elastic_searchable']['settings'], @status.inspect
324   - end
325   - should 'have set mapping' do
326   - @status = ElasticSearchable.request :get, '/elastic_searchable/users/_mapping'
327   - expected = {
328   - "name"=> {"type"=>"string", "index"=>"not_analyzed"}
329   - }
330   - assert_equal expected, @status['users']['properties'], @status.inspect
331   - end
332   - end
333   - end
334   -
335 300 class Friend < ActiveRecord::Base
336 301 belongs_to :book
337   - elastic_searchable :json => {:include => {:book => {:only => :title}}, :only => :name}, :index_options => SINGLE_NODE_CLUSTER_CONFIG
  302 + elastic_searchable :json => {:include => {:book => {:only => :title}}, :only => :name}
338 303 end
339 304 context 'activerecord class with optional :json config' do
340 305 context 'creating index' do
341 306 setup do
342   - Friend.create_index
  307 + ElasticSearchable.create_index
343 308 @book = Book.create! :isbn => '123abc', :title => 'another world'
344 309 @friend = Friend.new :name => 'bob', :favorite_color => 'red'
345 310 @friend.book = @book
346 311 @friend.save!
347   - Friend.refresh_index
  312 + ElasticSearchable.refresh_index
348 313 end
349 314 should 'index json with configuration' do
350 315 @response = ElasticSearchable.request :get, "/elastic_searchable/friends/#{@friend.id}"
@@ -360,15 +325,16 @@ class Friend < ActiveRecord::Base
360 325 end
361 326 end
362 327
363   - context 'updating ElasticSearchable.default_index' do
  328 + context '.index_name' do
364 329 setup do
365   - ElasticSearchable.default_index = 'my_new_index'
  330 + @orig_index_name = ElasticSearchable.index_name
  331 + ElasticSearchable.index_name = 'my_new_index'
366 332 end
367 333 teardown do
368   - ElasticSearchable.default_index = ElasticSearchable::DEFAULT_INDEX
  334 + ElasticSearchable.index_name = @orig_index_name
369 335 end
370 336 should 'change default index' do
371   - assert_equal 'my_new_index', ElasticSearchable.default_index
  337 + assert_equal 'my_new_index', ElasticSearchable.index_name
372 338 end
373 339 end
374 340
@@ -385,7 +351,7 @@ def percolated
385 351 context 'Book class with after_percolate callback' do
386 352 context 'with created index' do
387 353 setup do
388   - Book.create_index
  354 + ElasticSearchable.create_index
389 355 end
390 356 context "when index has configured percolation" do
391 357 setup do
@@ -443,17 +409,17 @@ def percolated
443 409 end
444 410
445 411 class MaxPageSizeClass < ActiveRecord::Base
446   - elastic_searchable :index_options => SINGLE_NODE_CLUSTER_CONFIG
  412 + elastic_searchable
447 413 def self.max_per_page
448 414 1
449 415 end
450 416 end
451 417 context 'with 2 MaxPageSizeClass instances' do
452 418 setup do
453   - MaxPageSizeClass.create_index
  419 + ElasticSearchable.create_index
454 420 @first = MaxPageSizeClass.create! :name => 'foo one'
455 421 @second = MaxPageSizeClass.create! :name => 'foo two'
456   - MaxPageSizeClass.refresh_index
  422 + ElasticSearchable.refresh_index
457 423 end
458 424 context 'MaxPageSizeClass.search with default options and WillPaginate' do
459 425 setup do

0 comments on commit af9467a

Please sign in to comment.
Something went wrong with that request. Please try again.