chewy 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|