chewy 0.8.3 → 0.8.4
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/.travis.yml +43 -11
- data/Appraisals +39 -12
- data/CHANGELOG.md +42 -0
- data/Gemfile +1 -1
- data/README.md +60 -7
- data/chewy.gemspec +12 -5
- data/gemfiles/{rails.4.0.mongoid.gemfile → rails.4.0.mongoid.4.0.0.gemfile} +1 -1
- data/gemfiles/{rails.4.0.mongoid.kaminari.gemfile → rails.4.0.mongoid.4.0.0.kaminari.gemfile} +1 -1
- data/gemfiles/{rails.4.0.mongoid.will_paginate.gemfile → rails.4.0.mongoid.4.0.0.will_paginate.gemfile} +1 -1
- data/gemfiles/rails.4.0.mongoid.5.1.0.gemfile +15 -0
- data/gemfiles/rails.4.0.mongoid.5.1.0.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.0.mongoid.5.1.0.will_paginate.gemfile +14 -0
- data/gemfiles/{rails.4.1.mongoid.gemfile → rails.4.1.mongoid.4.0.0.gemfile} +1 -1
- data/gemfiles/{rails.4.1.mongoid.kaminari.gemfile → rails.4.1.mongoid.4.0.0.kaminari.gemfile} +1 -1
- data/gemfiles/{rails.4.1.mongoid.will_paginate.gemfile → rails.4.1.mongoid.4.0.0.will_paginate.gemfile} +1 -1
- data/gemfiles/rails.4.1.mongoid.5.1.0.gemfile +15 -0
- data/gemfiles/rails.4.1.mongoid.5.1.0.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.1.mongoid.5.1.0.will_paginate.gemfile +14 -0
- data/gemfiles/{rails.4.2.mongoid.gemfile → rails.4.2.mongoid.4.0.0.gemfile} +1 -1
- data/gemfiles/{rails.4.2.mongoid.kaminari.gemfile → rails.4.2.mongoid.4.0.0.kaminari.gemfile} +1 -1
- data/gemfiles/{rails.4.2.mongoid.will_paginate.gemfile → rails.4.2.mongoid.4.0.0.will_paginate.gemfile} +1 -1
- data/gemfiles/rails.4.2.mongoid.5.1.0.gemfile +15 -0
- data/gemfiles/rails.4.2.mongoid.5.1.0.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.2.mongoid.5.1.0.will_paginate.gemfile +14 -0
- data/gemfiles/rails.5.0.0.beta3.activerecord.gemfile +16 -0
- data/gemfiles/rails.5.0.0.beta3.activerecord.kaminari.gemfile +16 -0
- data/gemfiles/rails.5.0.0.beta3.activerecord.will_paginate.gemfile +15 -0
- data/gemfiles/{sequel.4.28.gemfile → sequel.4.31.gemfile} +1 -1
- data/lib/chewy.rb +7 -1
- data/lib/chewy/errors.rb +6 -0
- data/lib/chewy/fields/base.rb +12 -8
- data/lib/chewy/fields/root.rb +1 -1
- data/lib/chewy/index.rb +17 -8
- data/lib/chewy/index/actions.rb +4 -4
- data/lib/chewy/query.rb +8 -13
- data/lib/chewy/query/compose.rb +2 -2
- data/lib/chewy/query/criteria.rb +2 -2
- data/lib/chewy/query/loading.rb +1 -1
- data/lib/chewy/query/nodes/bool.rb +1 -1
- data/lib/chewy/query/nodes/regexp.rb +2 -2
- data/lib/chewy/railtie.rb +15 -3
- data/lib/chewy/rake_helper.rb +5 -2
- data/lib/chewy/rspec/update_index.rb +17 -6
- data/lib/chewy/strategy.rb +7 -3
- data/lib/chewy/strategy/active_job.rb +2 -2
- data/lib/chewy/strategy/resque.rb +2 -2
- data/lib/chewy/strategy/sidekiq.rb +2 -2
- data/lib/chewy/type.rb +14 -0
- data/lib/chewy/type/adapter/active_record.rb +11 -1
- data/lib/chewy/type/adapter/orm.rb +13 -11
- data/lib/chewy/type/adapter/sequel.rb +10 -12
- data/lib/chewy/type/import.rb +53 -22
- data/lib/chewy/type/witchcraft.rb +208 -0
- data/lib/chewy/type/wrapper.rb +25 -7
- data/lib/chewy/version.rb +1 -1
- data/lib/tasks/chewy.rake +22 -14
- data/spec/chewy/fields/base_spec.rb +6 -2
- data/spec/chewy/fields/time_fields_spec.rb +4 -4
- data/spec/chewy/index/actions_spec.rb +32 -18
- data/spec/chewy/index_spec.rb +19 -0
- data/spec/chewy/query/pagination_spec.rb +1 -1
- data/spec/chewy/query_spec.rb +77 -21
- data/spec/chewy/rspec/update_index_spec.rb +75 -62
- data/spec/chewy/runtime_spec.rb +1 -1
- data/spec/chewy/strategy/active_job_spec.rb +6 -1
- data/spec/chewy/strategy/atomic_spec.rb +5 -5
- data/spec/chewy/strategy/resque_spec.rb +7 -2
- data/spec/chewy/strategy/sidekiq_spec.rb +6 -1
- data/spec/chewy/strategy_spec.rb +13 -1
- data/spec/chewy/type/actions_spec.rb +4 -1
- data/spec/chewy/type/import_spec.rb +71 -2
- data/spec/chewy/type/observe_spec.rb +9 -9
- data/spec/chewy/type/witchcraft_spec.rb +154 -0
- data/spec/chewy/type/wrapper_spec.rb +30 -5
- data/spec/chewy/type_spec.rb +10 -0
- data/spec/chewy_spec.rb +29 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/support/class_helpers.rb +15 -0
- data/spec/support/mongoid.rb +5 -0
- metadata +64 -21
data/lib/chewy/query/compose.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Chewy
|
|
|
30
30
|
def _queries_join queries, logic
|
|
31
31
|
queries = queries.compact
|
|
32
32
|
|
|
33
|
-
if queries.many? || (queries.
|
|
33
|
+
if queries.many? || (queries.present? && logic == :must_not)
|
|
34
34
|
case logic
|
|
35
35
|
when :dis_max
|
|
36
36
|
{ dis_max: { queries: queries } }
|
|
@@ -51,7 +51,7 @@ module Chewy
|
|
|
51
51
|
def _filters_join filters, logic
|
|
52
52
|
filters = filters.compact
|
|
53
53
|
|
|
54
|
-
if filters.many? || (filters.
|
|
54
|
+
if filters.many? || (filters.present? && logic == :must_not)
|
|
55
55
|
case logic
|
|
56
56
|
when :and, :or
|
|
57
57
|
{ logic => filters }
|
data/lib/chewy/query/criteria.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Chewy
|
|
|
32
32
|
|
|
33
33
|
STORAGES.each do |storage|
|
|
34
34
|
define_method "#{storage}?" do
|
|
35
|
-
send(storage).
|
|
35
|
+
send(storage).present?
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -69,7 +69,7 @@ module Chewy
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
[:filters, :queries, :post_filters].each do |storage|
|
|
72
|
-
class_eval <<-RUBY
|
|
72
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
73
73
|
def update_#{storage}(modifier)
|
|
74
74
|
@#{storage} = #{storage} + Array.wrap(modifier).reject(&:blank?)
|
|
75
75
|
end
|
data/lib/chewy/query/loading.rb
CHANGED
|
@@ -93,7 +93,7 @@ module Chewy
|
|
|
93
93
|
|
|
94
94
|
loaded_objects = Hash[_results.group_by(&:class).map do |type, objects|
|
|
95
95
|
next if except.include?(type.type_name)
|
|
96
|
-
next if only.
|
|
96
|
+
next if only.present? && !only.include?(type.type_name)
|
|
97
97
|
|
|
98
98
|
loaded = type.adapter.load(objects, options.merge(_type: type))
|
|
99
99
|
[type, loaded.index_by.with_index do |loaded, i|
|
|
@@ -20,7 +20,7 @@ module Chewy
|
|
|
20
20
|
bool = {
|
|
21
21
|
bool: Hash[METHODS.map do |method|
|
|
22
22
|
value = instance_variable_get("@#{method}")
|
|
23
|
-
[method.to_sym, value.map(&:__render__)] if value.
|
|
23
|
+
[method.to_sym, value.map(&:__render__)] if value.present?
|
|
24
24
|
end.compact]
|
|
25
25
|
}
|
|
26
26
|
bool[:bool][:_cache] = !!@options[:cache] if @options.key?(:cache)
|
|
@@ -8,8 +8,8 @@ module Chewy
|
|
|
8
8
|
@name = name.to_s
|
|
9
9
|
@regexp = regexp.respond_to?(:source) ? regexp.source : regexp.to_s
|
|
10
10
|
@options = args.extract_options!
|
|
11
|
-
if args.
|
|
12
|
-
@options[:flags] = FLAGS & (args.
|
|
11
|
+
if args.present? || @options[:flags].present?
|
|
12
|
+
@options[:flags] = FLAGS & (args.present? ? args.flatten : @options[:flags]).map(&:to_s).map(&:downcase)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
data/lib/chewy/railtie.rb
CHANGED
|
@@ -10,7 +10,8 @@ module Chewy
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def call(env)
|
|
13
|
-
|
|
13
|
+
# For Rails applications in `api_only` mode, the `assets` config isn't present
|
|
14
|
+
if Rails.application.config.respond_to?(:assets) && env['PATH_INFO'].start_with?(Rails.application.config.assets.prefix)
|
|
14
15
|
@app.call(env)
|
|
15
16
|
else
|
|
16
17
|
Chewy.strategy(Chewy.request_strategy) { @app.call(env) }
|
|
@@ -29,6 +30,12 @@ module Chewy
|
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
module Rails5MigrationStrategy
|
|
34
|
+
def migrate(*args)
|
|
35
|
+
Chewy.strategy(:bypass) { super }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
32
39
|
rake_tasks do
|
|
33
40
|
load 'tasks/chewy.rake'
|
|
34
41
|
end
|
|
@@ -49,8 +56,13 @@ module Chewy
|
|
|
49
56
|
|
|
50
57
|
initializer 'chewy.migration_strategy' do
|
|
51
58
|
ActiveSupport.on_load(:active_record) do
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
if Rails::VERSION::MAJOR >= 5
|
|
60
|
+
ActiveRecord::Migration.prepend(Rails5MigrationStrategy)
|
|
61
|
+
ActiveRecord::Migrator.prepend(Rails5MigrationStrategy) if defined? ActiveRecord::Migrator
|
|
62
|
+
else
|
|
63
|
+
ActiveRecord::Migration.send(:include, MigrationStrategy)
|
|
64
|
+
ActiveRecord::Migrator.send(:include, MigrationStrategy) if defined? ActiveRecord::Migrator
|
|
65
|
+
end
|
|
54
66
|
end
|
|
55
67
|
end
|
|
56
68
|
|
data/lib/chewy/rake_helper.rb
CHANGED
|
@@ -2,8 +2,8 @@ module Chewy
|
|
|
2
2
|
module RakeHelper
|
|
3
3
|
class << self
|
|
4
4
|
|
|
5
|
-
def
|
|
6
|
-
|
|
5
|
+
def subscribed_task_stats(&block)
|
|
6
|
+
callback = ->(name, start, finish, id, payload) do
|
|
7
7
|
duration = (finish - start).round(2)
|
|
8
8
|
puts " Imported #{payload[:type]} for #{duration}s, documents total: #{payload[:import].try(:[], :index).to_i}"
|
|
9
9
|
payload[:errors].each do |action, errors|
|
|
@@ -14,6 +14,9 @@ module Chewy
|
|
|
14
14
|
end
|
|
15
15
|
end if payload[:errors]
|
|
16
16
|
end
|
|
17
|
+
ActiveSupport::Notifications.subscribed(callback, 'import_objects.chewy') do
|
|
18
|
+
yield
|
|
19
|
+
end
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def eager_load_chewy!
|
|
@@ -100,7 +100,7 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
|
100
100
|
|
|
101
101
|
type = Chewy.derive_type(type_name)
|
|
102
102
|
|
|
103
|
-
instance_eval <<-RUBY
|
|
103
|
+
instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
104
104
|
#{agnostic_stub} do |bulk_options|
|
|
105
105
|
@updated += bulk_options[:body].map do |updated_document|
|
|
106
106
|
updated_document.deep_symbolize_keys
|
|
@@ -140,7 +140,7 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
|
140
140
|
(document[:expected_count] && document[:expected_count] == document[:real_count])
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
@updated.
|
|
143
|
+
@updated.present? && @missed_reindex.none? && @missed_delete.none? &&
|
|
144
144
|
@reindex.all? { |_, document| document[:match_count] && document[:match_attributes] } &&
|
|
145
145
|
@delete.all? { |_, document| document[:match_count] }
|
|
146
146
|
end
|
|
@@ -150,9 +150,20 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
|
150
150
|
|
|
151
151
|
if @updated.none?
|
|
152
152
|
output << "Expected index `#{type_name}` to be updated, but it was not\n"
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
elsif @missed_reindex.present? || @missed_delete.present?
|
|
154
|
+
message = "Expected index `#{type_name}` "
|
|
155
|
+
message << [
|
|
156
|
+
("to update documents #{@reindex.keys}" if @reindex.present?),
|
|
157
|
+
("to delete documents #{@delete.keys}" if @delete.present?)
|
|
158
|
+
].compact.join(' and ')
|
|
159
|
+
message << ' only, but '
|
|
160
|
+
message << [
|
|
161
|
+
("#{@missed_reindex} was updated" if @missed_reindex.present?),
|
|
162
|
+
("#{@missed_delete} was deleted" if @missed_delete.present?)
|
|
163
|
+
].compact.join(' and ')
|
|
164
|
+
message << ' also.'
|
|
165
|
+
|
|
166
|
+
output << message
|
|
156
167
|
end
|
|
157
168
|
|
|
158
169
|
output << @reindex.each.with_object('') do |(id, document), output|
|
|
@@ -184,7 +195,7 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
|
184
195
|
end
|
|
185
196
|
|
|
186
197
|
failure_message_when_negated do
|
|
187
|
-
if @updated.
|
|
198
|
+
if @updated.present?
|
|
188
199
|
"Expected index `#{type_name}` not to be updated, but it was with #{
|
|
189
200
|
@updated.map(&:values).flatten.group_by { |documents| documents[:_id] }.map do |id, documents|
|
|
190
201
|
"\n document id `#{id}` (#{documents.count} times)"
|
data/lib/chewy/strategy.rb
CHANGED
|
@@ -56,10 +56,10 @@ module Chewy
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def wrap name
|
|
59
|
-
push
|
|
59
|
+
stack = push(name)
|
|
60
60
|
yield
|
|
61
61
|
ensure
|
|
62
|
-
pop
|
|
62
|
+
pop if stack
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
private
|
|
@@ -72,7 +72,11 @@ module Chewy
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def resolve name
|
|
75
|
-
"Chewy::Strategy::#{name.to_s.camelize}".
|
|
75
|
+
"Chewy::Strategy::#{name.to_s.camelize}".safe_constantize or raise "Can't find update strategy `#{name}`"
|
|
76
|
+
rescue NameError => ex
|
|
77
|
+
# WORKAROUND: Strange behavior of `safe_constantize` with mongoid gem
|
|
78
|
+
raise "Can't find update strategy `#{name}`" if ex.name.to_s.demodulize == name.to_s.camelize
|
|
79
|
+
raise
|
|
76
80
|
end
|
|
77
81
|
end
|
|
78
82
|
end
|
data/lib/chewy/type.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'chewy/type/observe'
|
|
|
5
5
|
require 'chewy/type/actions'
|
|
6
6
|
require 'chewy/type/crutch'
|
|
7
7
|
require 'chewy/type/import'
|
|
8
|
+
require 'chewy/type/witchcraft'
|
|
8
9
|
require 'chewy/type/adapter/object'
|
|
9
10
|
require 'chewy/type/adapter/active_record'
|
|
10
11
|
require 'chewy/type/adapter/mongoid'
|
|
@@ -12,16 +13,22 @@ require 'chewy/type/adapter/sequel'
|
|
|
12
13
|
|
|
13
14
|
module Chewy
|
|
14
15
|
class Type
|
|
16
|
+
IMPORT_OPTIONS_KEYS = [:batch_size, :bulk_size, :refresh, :consistency, :replication]
|
|
17
|
+
|
|
15
18
|
include Search
|
|
16
19
|
include Mapping
|
|
17
20
|
include Wrapper
|
|
18
21
|
include Observe
|
|
19
22
|
include Actions
|
|
20
23
|
include Crutch
|
|
24
|
+
include Witchcraft
|
|
21
25
|
include Import
|
|
22
26
|
|
|
23
27
|
singleton_class.delegate :index_name, :client, to: :index
|
|
24
28
|
|
|
29
|
+
class_attribute :_default_import_options
|
|
30
|
+
self._default_import_options = {}
|
|
31
|
+
|
|
25
32
|
# Chewy index current type belongs to. Defined inside `Chewy.create_type`
|
|
26
33
|
#
|
|
27
34
|
def self.index
|
|
@@ -47,12 +54,19 @@ module Chewy
|
|
|
47
54
|
public_methods - Chewy::Type.public_methods
|
|
48
55
|
end
|
|
49
56
|
|
|
57
|
+
def self.default_import_options(params)
|
|
58
|
+
params.assert_valid_keys(IMPORT_OPTIONS_KEYS)
|
|
59
|
+
self._default_import_options = _default_import_options.merge(params)
|
|
60
|
+
end
|
|
61
|
+
|
|
50
62
|
def self.method_missing(method, *args, &block)
|
|
51
63
|
if index.scopes.include?(method)
|
|
52
64
|
define_singleton_method method do |*args, &block|
|
|
53
65
|
all.scoping { index.public_send(method, *args, &block) }
|
|
54
66
|
end
|
|
55
67
|
send(method, *args, &block)
|
|
68
|
+
else
|
|
69
|
+
super
|
|
56
70
|
end
|
|
57
71
|
end
|
|
58
72
|
|
|
@@ -28,7 +28,7 @@ module Chewy
|
|
|
28
28
|
ids = pluck_ids(scope)
|
|
29
29
|
result = true
|
|
30
30
|
|
|
31
|
-
while ids.
|
|
31
|
+
while ids.present?
|
|
32
32
|
result &= yield grouped_objects(default_scope_where_ids_in(ids))
|
|
33
33
|
break if ids.size < batch_size
|
|
34
34
|
ids = pluck_ids(scope.where(target_id.gt(ids.last)))
|
|
@@ -57,6 +57,16 @@ module Chewy
|
|
|
57
57
|
::ActiveRecord::Base
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
|
+
|
|
61
|
+
module Rails5
|
|
62
|
+
def pluck_ids(scope)
|
|
63
|
+
scope.except(:includes).distinct.pluck(target.primary_key.to_sym)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 5
|
|
68
|
+
Chewy::Type::Adapter::ActiveRecord.prepend(Rails5)
|
|
69
|
+
end
|
|
60
70
|
end
|
|
61
71
|
end
|
|
62
72
|
end
|
|
@@ -94,13 +94,7 @@ module Chewy
|
|
|
94
94
|
additional_scope = load_options[load_options[:_type].type_name.to_sym].try(:[], :scope) || load_options[:scope]
|
|
95
95
|
|
|
96
96
|
scope = all_scope_where_ids_in(objects.map(&primary_key))
|
|
97
|
-
loaded_objects =
|
|
98
|
-
scope.instance_exec(&additional_scope)
|
|
99
|
-
elsif additional_scope.is_a?(relation_class) && scope.respond_to?(:merge)
|
|
100
|
-
scope.merge(additional_scope)
|
|
101
|
-
else
|
|
102
|
-
scope
|
|
103
|
-
end.index_by { |object| object.public_send(primary_key).to_s }
|
|
97
|
+
loaded_objects = load_scope_objects(scope, additional_scope).index_by { |object| object.public_send(primary_key).to_s }
|
|
104
98
|
|
|
105
99
|
objects.map { |object| loaded_objects[object.public_send(primary_key).to_s] }
|
|
106
100
|
end
|
|
@@ -108,16 +102,14 @@ module Chewy
|
|
|
108
102
|
private
|
|
109
103
|
|
|
110
104
|
def import_objects(collection, batch_size)
|
|
111
|
-
hash = collection.
|
|
112
|
-
entity.is_a?(object_class) ? entity.public_send(primary_key) : entity
|
|
113
|
-
end
|
|
105
|
+
hash = Hash[identify(collection).zip(collection)]
|
|
114
106
|
|
|
115
107
|
indexed = hash.keys.each_slice(batch_size).map do |ids|
|
|
116
108
|
batch = default_scope_where_ids_in(ids)
|
|
117
109
|
if batch.empty?
|
|
118
110
|
true
|
|
119
111
|
else
|
|
120
|
-
batch.each { |
|
|
112
|
+
identify(batch).each { |id| hash.delete(id) }
|
|
121
113
|
yield grouped_objects(batch)
|
|
122
114
|
end
|
|
123
115
|
end.all?
|
|
@@ -148,6 +140,16 @@ module Chewy
|
|
|
148
140
|
def model_of_relation relation
|
|
149
141
|
relation.klass
|
|
150
142
|
end
|
|
143
|
+
|
|
144
|
+
def load_scope_objects(scope, additional_scope = nil)
|
|
145
|
+
if additional_scope.is_a?(Proc)
|
|
146
|
+
scope.instance_exec(&additional_scope)
|
|
147
|
+
elsif additional_scope.is_a?(relation_class) && scope.respond_to?(:merge)
|
|
148
|
+
scope.merge(additional_scope)
|
|
149
|
+
else
|
|
150
|
+
scope
|
|
151
|
+
end
|
|
152
|
+
end
|
|
151
153
|
end
|
|
152
154
|
end
|
|
153
155
|
end
|
|
@@ -13,16 +13,6 @@ module Chewy
|
|
|
13
13
|
target.is_a?(::Sequel::Dataset))
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def identify collection
|
|
17
|
-
if collection.is_a?(relation_class)
|
|
18
|
-
pluck_ids(collection)
|
|
19
|
-
else
|
|
20
|
-
Array.wrap(collection).map do |entity|
|
|
21
|
-
entity.is_a?(object_class) ? entity.public_send(primary_key) : entity
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
16
|
private
|
|
27
17
|
|
|
28
18
|
def cleanup_default_scope!
|
|
@@ -39,8 +29,8 @@ module Chewy
|
|
|
39
29
|
ids = pluck_ids(scope)
|
|
40
30
|
result = true
|
|
41
31
|
|
|
42
|
-
while ids.
|
|
43
|
-
result &= yield grouped_objects(default_scope_where_ids_in(ids))
|
|
32
|
+
while ids.present?
|
|
33
|
+
result &= yield grouped_objects(default_scope_where_ids_in(ids).all)
|
|
44
34
|
break if ids.size < batch_size
|
|
45
35
|
ids = pluck_ids(scope.where { |o| o.__send__(primary_key) > ids.last })
|
|
46
36
|
end
|
|
@@ -52,6 +42,10 @@ module Chewy
|
|
|
52
42
|
target.primary_key
|
|
53
43
|
end
|
|
54
44
|
|
|
45
|
+
def all_scope
|
|
46
|
+
target.dataset
|
|
47
|
+
end
|
|
48
|
+
|
|
55
49
|
def pluck_ids(scope)
|
|
56
50
|
scope.distinct.select_map(primary_key)
|
|
57
51
|
end
|
|
@@ -71,6 +65,10 @@ module Chewy
|
|
|
71
65
|
def object_class
|
|
72
66
|
::Sequel::Model
|
|
73
67
|
end
|
|
68
|
+
|
|
69
|
+
def load_scope_objects(*args)
|
|
70
|
+
super.all
|
|
71
|
+
end
|
|
74
72
|
end
|
|
75
73
|
end
|
|
76
74
|
end
|
data/lib/chewy/type/import.rb
CHANGED
|
@@ -3,6 +3,8 @@ module Chewy
|
|
|
3
3
|
module Import
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
|
+
BULK_OPTIONS = [:suffix, :bulk_size, :refresh, :consistency, :replication]
|
|
7
|
+
|
|
6
8
|
module ClassMethods
|
|
7
9
|
# Perform import operation for specified documents.
|
|
8
10
|
# Returns true or false depending on success.
|
|
@@ -11,15 +13,19 @@ module Chewy
|
|
|
11
13
|
# UsersIndex::User.import User.active # imports active users
|
|
12
14
|
# UsersIndex::User.import [1, 2, 3] # imports users with specified ids
|
|
13
15
|
# UsersIndex::User.import users # imports users collection
|
|
14
|
-
# UsersIndex::User.import refresh: false # to disable index refreshing after import
|
|
15
16
|
# UsersIndex::User.import suffix: Time.now.to_i # imports data to index with specified suffix if such is exists
|
|
17
|
+
# UsersIndex::User.import refresh: false # to disable index refreshing after import
|
|
16
18
|
# UsersIndex::User.import batch_size: 300 # import batch size
|
|
19
|
+
# UsersIndex::User.import bulk_size: 10.megabytes # import ElasticSearch bulk size in bytes
|
|
20
|
+
# UsersIndex::User.import consistency: :quorum # explicit write consistency setting for the operation (one, quorum, all)
|
|
21
|
+
# UsersIndex::User.import replication: :async # explicitly set the replication type (sync, async)
|
|
17
22
|
#
|
|
18
23
|
# See adapters documentation for more details.
|
|
19
24
|
#
|
|
20
25
|
def import *args
|
|
21
26
|
import_options = args.extract_options!
|
|
22
|
-
|
|
27
|
+
import_options.reverse_merge! _default_import_options
|
|
28
|
+
bulk_options = import_options.reject { |k, _| !BULK_OPTIONS.include?(k) }.reverse_merge!(refresh: true)
|
|
23
29
|
|
|
24
30
|
index.create!(bulk_options.slice(:suffix)) unless index.exists?
|
|
25
31
|
|
|
@@ -28,7 +34,7 @@ module Chewy
|
|
|
28
34
|
indexed_objects = build_root.parent_id && fetch_indexed_objects(action_objects.values.flatten)
|
|
29
35
|
body = bulk_body(action_objects, indexed_objects)
|
|
30
36
|
|
|
31
|
-
errors = bulk(bulk_options.merge(body: body)) if body.
|
|
37
|
+
errors = bulk(bulk_options.merge(body: body)) if body.present?
|
|
32
38
|
|
|
33
39
|
fill_payload_import payload, action_objects
|
|
34
40
|
fill_payload_errors payload, errors if errors.present?
|
|
@@ -39,15 +45,7 @@ module Chewy
|
|
|
39
45
|
|
|
40
46
|
# Perform import operation for specified documents.
|
|
41
47
|
# Raises Chewy::ImportFailed exception in case of import errors.
|
|
42
|
-
#
|
|
43
|
-
# UsersIndex::User.import! # imports default data set
|
|
44
|
-
# UsersIndex::User.import! User.active # imports active users
|
|
45
|
-
# UsersIndex::User.import! [1, 2, 3] # imports users with specified ids
|
|
46
|
-
# UsersIndex::User.import! users # imports users collection
|
|
47
|
-
# UsersIndex::User.import! refresh: false # to disable index refreshing after import
|
|
48
|
-
# UsersIndex::User.import! suffix: Time.now.to_i # imports data to index with specified suffix if such is exists
|
|
49
|
-
# UsersIndex::User.import! batch_size: 300 # import batch size
|
|
50
|
-
#
|
|
48
|
+
# Options are completely the same as for `import` method
|
|
51
49
|
# See adapters documentation for more details.
|
|
52
50
|
#
|
|
53
51
|
def import! *args
|
|
@@ -66,20 +64,46 @@ module Chewy
|
|
|
66
64
|
# Adds `:suffix` option to bulk import to index with specified suffix.
|
|
67
65
|
def bulk options = {}
|
|
68
66
|
suffix = options.delete(:suffix)
|
|
67
|
+
bulk_size = options.delete(:bulk_size)
|
|
68
|
+
body = options.delete(:body)
|
|
69
|
+
header = { index: index.build_index_name(suffix: suffix), type: type_name }
|
|
70
|
+
|
|
71
|
+
bodies = if bulk_size
|
|
72
|
+
bulk_size -= 1.kilobyte # 1 kilobyte for request header and newlines
|
|
73
|
+
raise ArgumentError.new('Import `:bulk_size` can\'t be less then 1 kilobyte') if bulk_size <= 0
|
|
74
|
+
|
|
75
|
+
body.each_with_object(['']) do |entry, result|
|
|
76
|
+
operation, meta = entry.to_a.first
|
|
77
|
+
data = meta.delete(:data)
|
|
78
|
+
entry = [{ operation => meta }, data].compact.map(&:to_json).join("\n")
|
|
79
|
+
if entry.bytesize > bulk_size
|
|
80
|
+
raise ArgumentError.new('Import `:bulk_size` seems to be less then entry size')
|
|
81
|
+
elsif result.last.bytesize + entry.bytesize > bulk_size
|
|
82
|
+
result.push(entry)
|
|
83
|
+
else
|
|
84
|
+
result[-1] = [result[-1], entry].delete_if(&:blank?).join("\n")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
[body]
|
|
89
|
+
end
|
|
69
90
|
|
|
70
|
-
|
|
91
|
+
items = bodies.map do |body|
|
|
92
|
+
result = client.bulk options.merge(header).merge(body: body)
|
|
93
|
+
result.try(:[], 'items') || []
|
|
94
|
+
end.flatten
|
|
71
95
|
Chewy.wait_for_status
|
|
72
96
|
|
|
73
|
-
extract_errors
|
|
97
|
+
extract_errors items
|
|
74
98
|
end
|
|
75
99
|
|
|
76
100
|
private
|
|
77
101
|
|
|
78
102
|
def bulk_body(action_objects, indexed_objects = nil)
|
|
79
|
-
action_objects.
|
|
103
|
+
action_objects.flat_map do |action, objects|
|
|
80
104
|
method = "#{action}_bulk_entry"
|
|
81
105
|
crutches = Chewy::Type::Crutch::Crutches.new self, objects
|
|
82
|
-
|
|
106
|
+
objects.flat_map { |object| send(method, object, indexed_objects, crutches) }
|
|
83
107
|
end
|
|
84
108
|
end
|
|
85
109
|
|
|
@@ -150,15 +174,21 @@ module Chewy
|
|
|
150
174
|
end
|
|
151
175
|
|
|
152
176
|
def object_data object, crutches = nil
|
|
153
|
-
|
|
177
|
+
if witchcraft?
|
|
178
|
+
cauldron.brew(object, crutches)
|
|
179
|
+
else
|
|
180
|
+
build_root.compose(object, crutches)[type_name.to_sym]
|
|
181
|
+
end
|
|
154
182
|
end
|
|
155
183
|
|
|
156
|
-
def extract_errors
|
|
157
|
-
|
|
184
|
+
def extract_errors items
|
|
185
|
+
items.each.with_object({}) do |item, memo|
|
|
158
186
|
action = item.keys.first.to_sym
|
|
159
187
|
data = item.values.first
|
|
160
|
-
|
|
161
|
-
|
|
188
|
+
if data['error']
|
|
189
|
+
(memo[action] ||= []).push(action: action, id: data['_id'], error: data['error'])
|
|
190
|
+
end
|
|
191
|
+
end.map do |action, items|
|
|
162
192
|
errors = items.group_by { |item| item[:error] }.map do |error, items|
|
|
163
193
|
{error => items.map { |item| item[:id] }}
|
|
164
194
|
end.reduce(&:merge)
|
|
@@ -181,7 +211,8 @@ module Chewy
|
|
|
181
211
|
break if result['hits']['hits'].empty?
|
|
182
212
|
|
|
183
213
|
result['hits']['hits'].map do |hit|
|
|
184
|
-
|
|
214
|
+
parent = hit.has_key?('_parent') ? hit['_parent'] : hit['fields']['_parent']
|
|
215
|
+
indexed_objects[hit['_id']] = { parent: parent }
|
|
185
216
|
end
|
|
186
217
|
end
|
|
187
218
|
|