chewy 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +35 -29
- data/Appraisals +37 -0
- data/CHANGELOG.md +115 -4
- data/Gemfile +2 -3
- data/README.md +135 -40
- data/chewy.gemspec +4 -3
- data/gemfiles/rails.3.2.activerecord.gemfile +13 -0
- data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.0.activerecord.gemfile +13 -0
- data/gemfiles/rails.4.0.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.0.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.0.mongoid.gemfile +13 -0
- data/gemfiles/rails.4.0.mongoid.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.0.mongoid.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.1.activerecord.gemfile +13 -0
- data/gemfiles/rails.4.1.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.1.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.1.mongoid.gemfile +13 -0
- data/gemfiles/rails.4.1.mongoid.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.1.mongoid.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.2.activerecord.gemfile +13 -0
- data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.2.mongoid.gemfile +13 -0
- data/gemfiles/rails.4.2.mongoid.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.2.mongoid.will_paginate.gemfile +14 -0
- data/lib/chewy.rb +65 -0
- data/lib/chewy/config.rb +44 -93
- data/lib/chewy/errors.rb +14 -5
- data/lib/chewy/fields/base.rb +8 -7
- data/lib/chewy/fields/root.rb +2 -2
- data/lib/chewy/index.rb +7 -9
- data/lib/chewy/log_subscriber.rb +34 -0
- data/lib/chewy/query.rb +41 -27
- data/lib/chewy/query/criteria.rb +28 -23
- data/lib/chewy/query/scoping.rb +20 -0
- data/lib/chewy/railtie.rb +51 -13
- data/lib/chewy/repository.rb +61 -0
- data/lib/chewy/rspec/update_index.rb +3 -6
- data/lib/chewy/search.rb +28 -7
- data/lib/chewy/strategy.rb +60 -0
- data/lib/chewy/strategy/atomic.rb +31 -0
- data/lib/chewy/strategy/base.rb +27 -0
- data/lib/chewy/strategy/bypass.rb +15 -0
- data/lib/chewy/strategy/urgent.rb +17 -0
- data/lib/chewy/type.rb +19 -5
- data/lib/chewy/type/adapter/active_record.rb +28 -117
- data/lib/chewy/type/adapter/base.rb +35 -0
- data/lib/chewy/type/adapter/mongoid.rb +23 -123
- data/lib/chewy/type/adapter/object.rb +41 -19
- data/lib/chewy/type/adapter/orm.rb +142 -0
- data/lib/chewy/type/import.rb +43 -16
- data/lib/chewy/type/observe.rb +8 -21
- data/lib/chewy/version.rb +1 -1
- data/lib/tasks/chewy.rake +8 -4
- data/spec/chewy/config_spec.rb +20 -97
- data/spec/chewy/fields/base_spec.rb +24 -11
- data/spec/chewy/fields/time_fields_spec.rb +27 -0
- data/spec/chewy/index/settings_spec.rb +2 -1
- data/spec/chewy/index_spec.rb +98 -79
- data/spec/chewy/query/criteria_spec.rb +14 -0
- data/spec/chewy/query_spec.rb +1 -1
- data/spec/chewy/repository_spec.rb +50 -0
- data/spec/chewy/search_spec.rb +100 -0
- data/spec/chewy/strategy_spec.rb +109 -0
- data/spec/chewy/type/adapter/active_record_spec.rb +110 -46
- data/spec/chewy/type/adapter/mongoid_spec.rb +123 -74
- data/spec/chewy/type/adapter/object_spec.rb +51 -34
- data/spec/chewy/type/import_spec.rb +21 -21
- data/spec/chewy/type/observe_spec.rb +26 -29
- data/spec/chewy/type_spec.rb +19 -0
- data/spec/chewy_spec.rb +19 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/support/active_record.rb +2 -1
- data/spec/support/mongoid.rb +29 -38
- metadata +85 -55
- data/gemfiles/Gemfile.rails-3.2.active_record +0 -6
- data/gemfiles/Gemfile.rails-3.2.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-3.2.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.0.active_record +0 -6
- data/gemfiles/Gemfile.rails-4.0.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.0.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.0.mongoid +0 -6
- data/gemfiles/Gemfile.rails-4.0.mongoid.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.0.mongoid.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.1.active_record +0 -6
- data/gemfiles/Gemfile.rails-4.1.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.1.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.1.mongoid +0 -6
- data/gemfiles/Gemfile.rails-4.1.mongoid.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.1.mongoid.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.2.active_record +0 -6
- data/gemfiles/Gemfile.rails-4.2.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.2.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.2.mongoid +0 -6
- data/gemfiles/Gemfile.rails-4.2.mongoid.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.2.mongoid.will_paginate +0 -7
- data/spec/chewy/index/search_spec.rb +0 -46
data/lib/chewy/railtie.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
9
|
-
end
|
36
|
+
console do |app|
|
37
|
+
Chewy.logger = ActiveRecord::Base.logger if defined?(ActiveRecord)
|
10
38
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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
|
111
|
-
|
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]
|
data/lib/chewy/search.rb
CHANGED
@@ -15,20 +15,41 @@ module Chewy
|
|
15
15
|
|
16
16
|
module ClassMethods
|
17
17
|
def all
|
18
|
-
|
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(
|
23
|
-
|
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
|
-
|
27
|
-
|
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
|
31
|
-
|
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
|
data/lib/chewy/type.rb
CHANGED
@@ -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
|
-
|
35
|
-
|
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.
|
39
|
-
|
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/
|
1
|
+
require 'chewy/type/adapter/orm'
|
2
2
|
|
3
3
|
module Chewy
|
4
4
|
class Type
|
5
5
|
module Adapter
|
6
|
-
class ActiveRecord <
|
7
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
18
|
+
def import_scope(scope, batch_size)
|
19
|
+
scope = scope.reorder(target.primary_key.to_sym).limit(batch_size)
|
103
20
|
|
104
|
-
|
21
|
+
ids = pluck_ids(scope)
|
22
|
+
result = true
|
105
23
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
116
|
-
|
117
|
-
end.all?
|
30
|
+
result
|
31
|
+
end
|
118
32
|
|
119
|
-
|
33
|
+
def pluck_ids(scope)
|
34
|
+
scope.pluck(target.primary_key.to_sym)
|
120
35
|
end
|
121
36
|
|
122
|
-
def
|
123
|
-
|
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
|
131
|
-
|
41
|
+
def all_scope
|
42
|
+
target.where(nil)
|
132
43
|
end
|
133
44
|
|
134
|
-
def
|
135
|
-
|
45
|
+
def relation_class
|
46
|
+
::ActiveRecord::Relation
|
136
47
|
end
|
137
48
|
|
138
|
-
def
|
139
|
-
::ActiveRecord::
|
49
|
+
def object_class
|
50
|
+
::ActiveRecord::Base
|
140
51
|
end
|
141
52
|
end
|
142
53
|
end
|