chewy 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +35 -29
  4. data/Appraisals +37 -0
  5. data/CHANGELOG.md +115 -4
  6. data/Gemfile +2 -3
  7. data/README.md +135 -40
  8. data/chewy.gemspec +4 -3
  9. data/gemfiles/rails.3.2.activerecord.gemfile +13 -0
  10. data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +14 -0
  11. data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +14 -0
  12. data/gemfiles/rails.4.0.activerecord.gemfile +13 -0
  13. data/gemfiles/rails.4.0.activerecord.kaminari.gemfile +14 -0
  14. data/gemfiles/rails.4.0.activerecord.will_paginate.gemfile +14 -0
  15. data/gemfiles/rails.4.0.mongoid.gemfile +13 -0
  16. data/gemfiles/rails.4.0.mongoid.kaminari.gemfile +14 -0
  17. data/gemfiles/rails.4.0.mongoid.will_paginate.gemfile +14 -0
  18. data/gemfiles/rails.4.1.activerecord.gemfile +13 -0
  19. data/gemfiles/rails.4.1.activerecord.kaminari.gemfile +14 -0
  20. data/gemfiles/rails.4.1.activerecord.will_paginate.gemfile +14 -0
  21. data/gemfiles/rails.4.1.mongoid.gemfile +13 -0
  22. data/gemfiles/rails.4.1.mongoid.kaminari.gemfile +14 -0
  23. data/gemfiles/rails.4.1.mongoid.will_paginate.gemfile +14 -0
  24. data/gemfiles/rails.4.2.activerecord.gemfile +13 -0
  25. data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +14 -0
  26. data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +14 -0
  27. data/gemfiles/rails.4.2.mongoid.gemfile +13 -0
  28. data/gemfiles/rails.4.2.mongoid.kaminari.gemfile +14 -0
  29. data/gemfiles/rails.4.2.mongoid.will_paginate.gemfile +14 -0
  30. data/lib/chewy.rb +65 -0
  31. data/lib/chewy/config.rb +44 -93
  32. data/lib/chewy/errors.rb +14 -5
  33. data/lib/chewy/fields/base.rb +8 -7
  34. data/lib/chewy/fields/root.rb +2 -2
  35. data/lib/chewy/index.rb +7 -9
  36. data/lib/chewy/log_subscriber.rb +34 -0
  37. data/lib/chewy/query.rb +41 -27
  38. data/lib/chewy/query/criteria.rb +28 -23
  39. data/lib/chewy/query/scoping.rb +20 -0
  40. data/lib/chewy/railtie.rb +51 -13
  41. data/lib/chewy/repository.rb +61 -0
  42. data/lib/chewy/rspec/update_index.rb +3 -6
  43. data/lib/chewy/search.rb +28 -7
  44. data/lib/chewy/strategy.rb +60 -0
  45. data/lib/chewy/strategy/atomic.rb +31 -0
  46. data/lib/chewy/strategy/base.rb +27 -0
  47. data/lib/chewy/strategy/bypass.rb +15 -0
  48. data/lib/chewy/strategy/urgent.rb +17 -0
  49. data/lib/chewy/type.rb +19 -5
  50. data/lib/chewy/type/adapter/active_record.rb +28 -117
  51. data/lib/chewy/type/adapter/base.rb +35 -0
  52. data/lib/chewy/type/adapter/mongoid.rb +23 -123
  53. data/lib/chewy/type/adapter/object.rb +41 -19
  54. data/lib/chewy/type/adapter/orm.rb +142 -0
  55. data/lib/chewy/type/import.rb +43 -16
  56. data/lib/chewy/type/observe.rb +8 -21
  57. data/lib/chewy/version.rb +1 -1
  58. data/lib/tasks/chewy.rake +8 -4
  59. data/spec/chewy/config_spec.rb +20 -97
  60. data/spec/chewy/fields/base_spec.rb +24 -11
  61. data/spec/chewy/fields/time_fields_spec.rb +27 -0
  62. data/spec/chewy/index/settings_spec.rb +2 -1
  63. data/spec/chewy/index_spec.rb +98 -79
  64. data/spec/chewy/query/criteria_spec.rb +14 -0
  65. data/spec/chewy/query_spec.rb +1 -1
  66. data/spec/chewy/repository_spec.rb +50 -0
  67. data/spec/chewy/search_spec.rb +100 -0
  68. data/spec/chewy/strategy_spec.rb +109 -0
  69. data/spec/chewy/type/adapter/active_record_spec.rb +110 -46
  70. data/spec/chewy/type/adapter/mongoid_spec.rb +123 -74
  71. data/spec/chewy/type/adapter/object_spec.rb +51 -34
  72. data/spec/chewy/type/import_spec.rb +21 -21
  73. data/spec/chewy/type/observe_spec.rb +26 -29
  74. data/spec/chewy/type_spec.rb +19 -0
  75. data/spec/chewy_spec.rb +19 -3
  76. data/spec/spec_helper.rb +1 -1
  77. data/spec/support/active_record.rb +2 -1
  78. data/spec/support/mongoid.rb +29 -38
  79. metadata +85 -55
  80. data/gemfiles/Gemfile.rails-3.2.active_record +0 -6
  81. data/gemfiles/Gemfile.rails-3.2.active_record.kaminari +0 -7
  82. data/gemfiles/Gemfile.rails-3.2.active_record.will_paginate +0 -7
  83. data/gemfiles/Gemfile.rails-4.0.active_record +0 -6
  84. data/gemfiles/Gemfile.rails-4.0.active_record.kaminari +0 -7
  85. data/gemfiles/Gemfile.rails-4.0.active_record.will_paginate +0 -7
  86. data/gemfiles/Gemfile.rails-4.0.mongoid +0 -6
  87. data/gemfiles/Gemfile.rails-4.0.mongoid.kaminari +0 -7
  88. data/gemfiles/Gemfile.rails-4.0.mongoid.will_paginate +0 -7
  89. data/gemfiles/Gemfile.rails-4.1.active_record +0 -6
  90. data/gemfiles/Gemfile.rails-4.1.active_record.kaminari +0 -7
  91. data/gemfiles/Gemfile.rails-4.1.active_record.will_paginate +0 -7
  92. data/gemfiles/Gemfile.rails-4.1.mongoid +0 -6
  93. data/gemfiles/Gemfile.rails-4.1.mongoid.kaminari +0 -7
  94. data/gemfiles/Gemfile.rails-4.1.mongoid.will_paginate +0 -7
  95. data/gemfiles/Gemfile.rails-4.2.active_record +0 -6
  96. data/gemfiles/Gemfile.rails-4.2.active_record.kaminari +0 -7
  97. data/gemfiles/Gemfile.rails-4.2.active_record.will_paginate +0 -7
  98. data/gemfiles/Gemfile.rails-4.2.mongoid +0 -6
  99. data/gemfiles/Gemfile.rails-4.2.mongoid.kaminari +0 -7
  100. data/gemfiles/Gemfile.rails-4.2.mongoid.will_paginate +0 -7
  101. data/spec/chewy/index/search_spec.rb +0 -46
@@ -1,27 +1,65 @@
1
1
  module Chewy
2
2
  class Railtie < Rails::Railtie
3
+ def self.all_engines
4
+ Rails::Engine.subclasses.map(&:instance) + [Rails.application]
5
+ end
6
+
7
+ class RequestStrategy
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ if env['PATH_INFO'] =~ /^\/assets\//
14
+ @app.call(env)
15
+ else
16
+ Chewy.strategy(Chewy.request_strategy) { @app.call(env) }
17
+ end
18
+ end
19
+ end
20
+
21
+ module MigrationStrategy
22
+ extend ActiveSupport::Concern
23
+ included do
24
+ alias_method_chain :migrate, :chewy
25
+ end
26
+
27
+ def migrate_with_chewy(*args)
28
+ Chewy.strategy(:bypass) { migrate_without_chewy(*args) }
29
+ end
30
+ end
31
+
3
32
  rake_tasks do
4
33
  load 'tasks/chewy.rake'
5
34
  end
6
35
 
7
- initializer 'chewy.add_app_chewy_path' do |app|
8
- app.config.paths.add 'app/chewy'
9
- end
36
+ console do |app|
37
+ Chewy.logger = ActiveRecord::Base.logger if defined?(ActiveRecord)
10
38
 
11
- initializer 'chewy.add_requests_logging' do |app|
12
- ActiveSupport::Notifications.subscribe('import_objects.chewy') do |name, start, finish, id, payload|
13
- duration = ((finish - start).to_f * 10000).round / 10.0
14
- Rails.logger.debug(" \e[1m\e[32m#{payload[:type]} Import (#{duration}ms)\e[0m #{payload[:import]}")
39
+ if app.sandbox?
40
+ Chewy.strategy(:bypass)
41
+ else
42
+ Chewy.strategy(:urgent)
15
43
  end
44
+ end
45
+
46
+ initializer 'chewy.logger', after: 'active_record.logger' do
47
+ ActiveSupport.on_load(:active_record) { Chewy.logger ||= ActiveRecord::Base.logger }
48
+ end
16
49
 
17
- ActiveSupport::Notifications.subscribe('search_query.chewy') do |name, start, finish, id, payload|
18
- duration = ((finish - start).to_f * 10000).round / 10.0
19
- Rails.logger.debug(" \e[1m\e[32m#{payload[:index]} Search (#{duration}ms)\e[0m #{payload[:request]}")
50
+ initializer 'chewy.migration_strategy' do
51
+ ActiveSupport.on_load(:active_record) do
52
+ ActiveRecord::Migration.send(:include, MigrationStrategy)
20
53
  end
54
+ end
21
55
 
22
- ActiveSupport::Notifications.subscribe('delete_query.chewy') do |name, start, finish, id, payload|
23
- duration = ((finish - start).to_f * 10000).round / 10.0
24
- Rails.logger.debug(" \e[1m\e[32m#{payload[:index]} Delete by Query (#{duration}ms)\e[0m #{payload[:request]}")
56
+ initializer 'chewy.request_strategy' do |app|
57
+ app.config.middleware.insert_after(Rack::Runtime, RequestStrategy)
58
+ end
59
+
60
+ initializer 'chewy.add_app_chewy_path' do |app|
61
+ Chewy::Railtie.all_engines.each do |engine|
62
+ engine.paths.add 'app/chewy'
25
63
  end
26
64
  end
27
65
  end
@@ -0,0 +1,61 @@
1
+ module Chewy
2
+ class Repository
3
+ include Singleton
4
+
5
+ attr_reader :analyzers, :tokenizers, :filters, :char_filters
6
+
7
+ def self.delegated
8
+ public_instance_methods - self.superclass.public_instance_methods - Singleton.public_instance_methods
9
+ end
10
+
11
+ def self.repository name
12
+ plural_name = name.to_s.pluralize
13
+
14
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
15
+ def #{name}(name, options = nil)
16
+ options ? #{plural_name}[name.to_sym] = options : #{plural_name}[name.to_sym]
17
+ end
18
+ METHOD
19
+ end
20
+
21
+ # Analysers repository:
22
+ #
23
+ # Chewy.analyzer :my_analyzer2, {
24
+ # type: custom,
25
+ # tokenizer: 'my_tokenizer1',
26
+ # filter : ['my_token_filter1', 'my_token_filter2']
27
+ # char_filter : ['my_html']
28
+ # }
29
+ # Chewy.analyzer(:my_analyzer2) # => {type: 'custom', tokenizer: ...}
30
+ #
31
+ repository :analyzer
32
+
33
+ # Tokenizers repository:
34
+ #
35
+ # Chewy.tokenizer :my_tokenizer1, {type: standard, max_token_length: 900}
36
+ # Chewy.tokenizer(:my_tokenizer1) # => {type: standard, max_token_length: 900}
37
+ #
38
+ repository :tokenizer
39
+
40
+ # Token filters repository:
41
+ #
42
+ # Chewy.filter :my_token_filter1, {type: stop, stopwords: [stop1, stop2, stop3, stop4]}
43
+ # Chewy.filter(:my_token_filter1) # => {type: stop, stopwords: [stop1, stop2, stop3, stop4]}
44
+ #
45
+ repository :filter
46
+
47
+ # Char filters repository:
48
+ #
49
+ # Chewy.char_filter :my_html, {type: html_strip, escaped_tags: [xxx, yyy], read_ahead: 1024}
50
+ # Chewy.char_filter(:my_html) # => {type: html_strip, escaped_tags: [xxx, yyy], read_ahead: 1024}
51
+ #
52
+ repository :char_filter
53
+
54
+ def initialize
55
+ @analyzers = {}
56
+ @tokenizers = {}
57
+ @filters = {}
58
+ @char_filters = {}
59
+ end
60
+ end
61
+ end
@@ -18,7 +18,7 @@ require 'i18n/core_ext/hash'
18
18
  # Combined matcher chain methods:
19
19
  #
20
20
  # specify { expect { user1.destroy!; user2.save! } }
21
- # .to update_index(UsersIndex:User).and_reindex(user2).and_delete(user1)
21
+ # .to update_index(UsersIndex:User).and_reindex(user2).and_delete(user1) }
22
22
  #
23
23
  RSpec::Matchers.define :update_index do |type_name, options = {}|
24
24
 
@@ -107,11 +107,8 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
107
107
  {}
108
108
  end
109
109
 
110
- if options[:atomic] == false
111
- block.call
112
- else
113
- Chewy.atomic { block.call }
114
- end
110
+ ActiveSupport::Deprecation.warn('`atomic: false` option is removed and not effective anymore, use `strategy: :atomic` option instead') if options.key?(:atomic)
111
+ Chewy.strategy(options[:strategy] || :atomic) { block.call }
115
112
 
116
113
  @updated.each do |updated_document|
117
114
  if body = updated_document[:index]
@@ -15,20 +15,41 @@ module Chewy
15
15
 
16
16
  module ClassMethods
17
17
  def all
18
- Chewy::Query.new(search_index, types: search_type)
18
+ query_class.scopes.last || query_class.new(self)
19
19
  end
20
20
 
21
21
  def search_string query, options = {}
22
- options = options.merge(index: search_index.index_name, type: search_type, q: query)
23
- client.search(options)
22
+ options = options.merge(
23
+ index: all._indexes.map(&:index_name),
24
+ type: all._types.map(&:type_name),
25
+ q: query)
26
+ Chewy.client.search(options)
24
27
  end
25
28
 
26
- def search_index
27
- raise NotImplementedError
29
+ private
30
+
31
+ def query_class
32
+ @query_class ||= begin
33
+ query_class = Class.new(Chewy::Query)
34
+ if self < Chewy::Type
35
+ index_scopes = index.scopes - scopes
36
+
37
+ delegate_scoped index, query_class, index_scopes
38
+ delegate_scoped index, self, index_scopes
39
+ end
40
+ delegate_scoped self, query_class, scopes
41
+ const_set('Query', query_class)
42
+ end
28
43
  end
29
44
 
30
- def search_type
31
- raise NotImplementedError
45
+ def delegate_scoped source, destination, methods
46
+ methods.each do |method|
47
+ destination.class_eval do
48
+ define_method method do |*args, &block|
49
+ scoping { source.public_send(method, *args, &block) }
50
+ end
51
+ end
52
+ end
32
53
  end
33
54
  end
34
55
  end
@@ -0,0 +1,60 @@
1
+ require 'chewy/strategy/base'
2
+ require 'chewy/strategy/bypass'
3
+ require 'chewy/strategy/urgent'
4
+ require 'chewy/strategy/atomic'
5
+
6
+ module Chewy
7
+ # This class represents strategies stack with `:base`
8
+ # Strategy on top of it. This causes raising exceptions
9
+ # on every index update attempt, so other strategy must
10
+ # be choosen.
11
+ #
12
+ # User.first.save # Raises UndefinedUpdateStrategy exception
13
+ #
14
+ # Chewy.strategy(:atomic) do
15
+ # User.last.save # Save user according to the `:atomic` strategy rules
16
+ # end
17
+ #
18
+ class Strategy
19
+ def initialize
20
+ @stack = [resolve(Chewy.root_strategy).new]
21
+ end
22
+
23
+ def current
24
+ @stack.last
25
+ end
26
+
27
+ def push name
28
+ result = @stack.push resolve(name).new
29
+ debug "[#{@stack.size}] <- #{current.name}"
30
+ result
31
+ end
32
+
33
+ def pop
34
+ raise "Can't pop root strategy" if @stack.one?
35
+ debug "[#{@stack.size}] -> #{current.name}"
36
+ result = @stack.pop.tap(&:leave)
37
+ result
38
+ end
39
+
40
+ def wrap name
41
+ push name
42
+ yield
43
+ ensure
44
+ pop
45
+ end
46
+
47
+ private
48
+
49
+ def debug string
50
+ if Chewy.logger && Chewy.logger.debug?
51
+ line = caller.detect { |line| line !~ %r{lib/chewy/strategy.rb:|lib/chewy.rb:} }
52
+ Chewy.logger.debug(["Chewy strategies stack: #{string}", line.sub(/:in\s.+$/, '')].join(' @ '))
53
+ end
54
+ end
55
+
56
+ def resolve name
57
+ "Chewy::Strategy::#{name.to_s.camelize}".constantize or raise "Can't find update strategy `#{name}`"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,31 @@
1
+ module Chewy
2
+ class Strategy
3
+ # This strategy accomulates all the objects prepared for
4
+ # indexing and fires index process when strategy is popped
5
+ # from the strategies stack.
6
+ #
7
+ # Chewy.strategy(:atomic) do
8
+ # User.all.map(&:save) # Does nothing here
9
+ # Post.all.map(&:save) # And here
10
+ # # It imports all the changed users and posts right here
11
+ # # before block leaving with bulk ES API, kinda optimization
12
+ # end
13
+ #
14
+ class Atomic < Base
15
+ def initialize
16
+ @stash = {}
17
+ end
18
+
19
+ def update type, objects, options = {}
20
+ ActiveSupport::Deprecation.warn("`urgent: true` option is deprecated and is not effective inside `:atomic` strategy, use `Chewy.strategy(:urgent)` strategy instead") if options.key?(:urgent)
21
+
22
+ @stash[type] ||= []
23
+ @stash[type] |= type.adapter.identify(objects)
24
+ end
25
+
26
+ def leave
27
+ @stash.all? { |type, ids| type.import(ids) }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ module Chewy
2
+ class Strategy
3
+ # This strategy raises exception on every index update
4
+ # asking to choose some other strategy.
5
+ #
6
+ # Chewy.strategy(:base) do
7
+ # User.all.map(&:save) # Raises UndefinedUpdateStrategy exception
8
+ # end
9
+ #
10
+ class Base
11
+ def name
12
+ self.class.name.demodulize.underscore.to_sym
13
+ end
14
+ # This method called when some model tries to update index
15
+ #
16
+ def update type, objects, options = {}
17
+ raise UndefinedUpdateStrategy.new(type)
18
+ end
19
+
20
+ # This method called when strategy pops from the
21
+ # strategies stack
22
+ #
23
+ def leave
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module Chewy
2
+ class Strategy
3
+ # This strategy basically does nothing.
4
+ #
5
+ # Chewy.strategy(:bypass) do
6
+ # User.all.map(&:save) # Does nothing here
7
+ # # Does not update index all over the block.
8
+ # end
9
+ #
10
+ class Bypass < Base
11
+ def update type, objects, options = {}
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Chewy
2
+ class Strategy
3
+ # This strategy updates index on demand. Not the best
4
+ # strategy in case of optimization. If you need to update
5
+ # indexes with bulk API calls - use :atomic instead.
6
+ #
7
+ # Chewy.strategy(:urgent) do
8
+ # User.all.map(&:save) # Updates index on every `save` call
9
+ # end
10
+ #
11
+ class Urgent < Base
12
+ def update type, objects, options = {}
13
+ type.import(Array.wrap(objects))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -17,26 +17,40 @@ module Chewy
17
17
  include Actions
18
18
  include Import
19
19
 
20
- singleton_class.delegate :client, to: :index
20
+ singleton_class.delegate :index_name, :client, to: :index
21
21
 
22
+ # Chewy index current type blongs to. Defined inside `Chewy.create_type`
23
+ #
22
24
  def self.index
23
25
  raise NotImplementedError
24
26
  end
25
27
 
28
+ # Current type adapter. Defined inside `Chewy.create_type`, derived from
29
+ # `Chewy::Index.define_type` arguments.
30
+ #
26
31
  def self.adapter
27
32
  raise NotImplementedError
28
33
  end
29
34
 
35
+ # Returns type name string
36
+ #
30
37
  def self.type_name
31
38
  adapter.type_name
32
39
  end
33
40
 
34
- def self.search_index
35
- index
41
+ # Returns list of public class methods defined in current type
42
+ #
43
+ def self.scopes
44
+ public_methods - Chewy::Type.public_methods
36
45
  end
37
46
 
38
- def self.search_type
39
- type_name
47
+ def self.method_missing(method, *args, &block)
48
+ if index.scopes.include?(method)
49
+ define_singleton_method method do |*args, &block|
50
+ all.scoping { index.public_send(method, *args, &block) }
51
+ end
52
+ send(method, *args, &block)
53
+ end
40
54
  end
41
55
 
42
56
  def self.const_missing(name)
@@ -1,142 +1,53 @@
1
- require 'chewy/type/adapter/base'
1
+ require 'chewy/type/adapter/orm'
2
2
 
3
3
  module Chewy
4
4
  class Type
5
5
  module Adapter
6
- class ActiveRecord < Base
7
- def initialize *args
8
- @options = args.extract_options!
9
- subject = args.first
10
- if subject.is_a?(::ActiveRecord::Relation)
11
- @model = subject.klass
12
- @scope = subject
13
- else
14
- @model = subject
15
- end
16
- end
17
-
18
- def name
19
- @name ||= (options[:name].present? ? options[:name].to_s.camelize : model.model_name.to_s).demodulize
20
- end
21
-
22
- # Import method fo ActiveRecord takes import data and import options
23
- #
24
- # Import data types:
25
- #
26
- # * Nothing passed - imports all the model data
27
- # * ActiveRecord scope
28
- # * Objects collection
29
- # * Ids collection
30
- #
31
- # Import options:
32
- #
33
- # <tt>:batch_size</tt> - import batch size, 1000 objects by default
34
- #
35
- # Method handles destroyed objects as well. In case of objects AcriveRecord::Relation
36
- # or array passed, objects, responding with true to `destroyed?` method will be deleted
37
- # from index. In case of ids array passed - documents with missing records ids will be
38
- # deleted from index:
39
- #
40
- # users = User.all
41
- # users.each { |user| user.destroy if user.incative? }
42
- # UsersIndex::User.import users # inactive users will be deleted from index
43
- # # or
44
- # UsersIndex::User.import users.map(&:id) # deleted user ids will be deleted from index
45
- #
46
- # Also there is custom API method `delete_from_index?`. It it returns `true`
47
- # object will be deleted from index. Note that if this method is defined and
48
- # return `false` Chewy will still check `destroyed?` method. This is useful
49
- # for paranoid objects sdeleting implementation.
50
- #
51
- # class User
52
- # alias_method :delete_from_index?, :deleted_at?
53
- # end
54
- #
55
- # users = User.all
56
- # users.each { |user| user.deleted_at = Time.now }
57
- # UsersIndex::User.import users # paranoid deleted users will be deleted from index
58
- # # or
59
- # UsersIndex::User.import users.map(&:id) # user ids will be deleted from index
60
- #
61
- def import *args, &block
62
- import_options = args.extract_options!
63
- import_options[:batch_size] ||= BATCH_SIZE
64
- collection = args.none? ? model_all :
65
- (args.one? && args.first.is_a?(::ActiveRecord::Relation) ? args.first : args.flatten.compact)
6
+ class ActiveRecord < Orm
7
+ private
66
8
 
67
- if collection.is_a?(::ActiveRecord::Relation)
68
- result = true
69
- merged_scope(collection).find_in_batches(import_options.slice(:batch_size)) do |group|
70
- result &= block.call grouped_objects(group)
71
- end
72
- result
73
- else
74
- if collection.all? { |object| object.respond_to?(:id) }
75
- collection.each_slice(import_options[:batch_size]).map do |group|
76
- block.call grouped_objects(group)
77
- end.all?
78
- else
79
- import_ids(collection, import_options, &block)
80
- end
9
+ def cleanup_default_scope!
10
+ if Chewy.logger && (@default_scope.arel.orders.present? ||
11
+ @default_scope.arel.limit.present? || @default_scope.arel.offset.present?)
12
+ Chewy.logger.warn('Default type scope order, limit and offest are ignored and will be nullified')
81
13
  end
82
- end
83
14
 
84
- def load *args
85
- load_options = args.extract_options!
86
- objects = args.flatten
87
-
88
- additional_scope = load_options[load_options[:_type].type_name.to_sym].try(:[], :scope) || load_options[:scope]
89
-
90
- scope = scoped_model(objects.map(&:id))
91
- loaded_objects = if additional_scope.is_a?(Proc)
92
- scope.instance_exec(&additional_scope)
93
- elsif additional_scope.is_a?(::ActiveRecord::Relation)
94
- scope.merge(additional_scope)
95
- else
96
- scope
97
- end.index_by { |object| object.id.to_s }
98
-
99
- objects.map { |object| loaded_objects[object.id.to_s] }
15
+ @default_scope = @default_scope.reorder(nil).limit(nil).offset(nil)
100
16
  end
101
17
 
102
- private
18
+ def import_scope(scope, batch_size)
19
+ scope = scope.reorder(target.primary_key.to_sym).limit(batch_size)
103
20
 
104
- attr_reader :model, :scope, :options
21
+ ids = pluck_ids(scope)
22
+ result = true
105
23
 
106
- def import_ids(ids, import_options = {}, &block)
107
- ids.uniq!
108
-
109
- indexed = true
110
- merged_scope(scoped_model(ids)).find_in_batches(import_options.slice(:batch_size)) do |objects|
111
- ids -= objects.map(&:id)
112
- indexed &= block.call(grouped_objects(objects))
24
+ while ids.any?
25
+ result &= yield grouped_objects(default_scope_where_ids_in(ids))
26
+ break if ids.size < batch_size
27
+ ids = pluck_ids(scope.where(scope.table[target.primary_key].gt(ids.last)))
113
28
  end
114
29
 
115
- deleted = ids.each_slice(import_options[:batch_size]).map do |group|
116
- block.call(delete: group)
117
- end.all?
30
+ result
31
+ end
118
32
 
119
- indexed && deleted
33
+ def pluck_ids(scope)
34
+ scope.pluck(target.primary_key.to_sym)
120
35
  end
121
36
 
122
- def grouped_objects(objects)
123
- objects.group_by do |object|
124
- delete = object.delete_from_index? if object.respond_to?(:delete_from_index?)
125
- delete ||= object.destroyed?
126
- delete ? :delete : :index
127
- end
37
+ def scope_where_ids_in(scope, ids)
38
+ scope.where(target.primary_key => ids)
128
39
  end
129
40
 
130
- def merged_scope(target)
131
- scope ? scope.clone.merge(target) : target
41
+ def all_scope
42
+ target.where(nil)
132
43
  end
133
44
 
134
- def scoped_model(ids)
135
- model.where(Hash[model.primary_key.to_sym || :id, ids])
45
+ def relation_class
46
+ ::ActiveRecord::Relation
136
47
  end
137
48
 
138
- def model_all
139
- ::ActiveRecord::VERSION::MAJOR < 4 ? model.scoped : model.all
49
+ def object_class
50
+ ::ActiveRecord::Base
140
51
  end
141
52
  end
142
53
  end