chewy 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|