chewy 0.7.0 → 0.8.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 -0
- data/.rspec +0 -1
- data/.travis.yml +2 -2
- data/Appraisals +6 -2
- data/CHANGELOG.md +29 -1
- data/Gemfile +4 -0
- data/README.md +137 -19
- data/chewy.gemspec +1 -0
- data/gemfiles/rails.3.2.activerecord.gemfile +2 -0
- data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +1 -1
- data/gemfiles/rails.4.0.activerecord.gemfile +2 -0
- data/gemfiles/rails.4.0.activerecord.kaminari.gemfile +1 -1
- data/gemfiles/rails.4.0.mongoid.gemfile +2 -0
- data/gemfiles/rails.4.0.mongoid.kaminari.gemfile +1 -1
- data/gemfiles/rails.4.1.activerecord.gemfile +2 -0
- data/gemfiles/rails.4.1.activerecord.kaminari.gemfile +1 -1
- data/gemfiles/rails.4.1.mongoid.gemfile +2 -0
- data/gemfiles/rails.4.1.mongoid.kaminari.gemfile +1 -1
- data/gemfiles/rails.4.2.activerecord.gemfile +2 -0
- data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +1 -1
- data/gemfiles/rails.4.2.mongoid.gemfile +2 -0
- data/gemfiles/rails.4.2.mongoid.kaminari.gemfile +1 -1
- data/lib/chewy.rb +1 -2
- data/lib/chewy/config.rb +3 -3
- data/lib/chewy/fields/base.rb +27 -30
- data/lib/chewy/fields/root.rb +9 -19
- data/lib/chewy/query.rb +34 -1
- data/lib/chewy/railtie.rb +1 -0
- data/lib/chewy/rspec/update_index.rb +16 -6
- data/lib/chewy/strategy.rb +12 -0
- data/lib/chewy/strategy/atomic.rb +1 -1
- data/lib/chewy/strategy/resque.rb +26 -0
- data/lib/chewy/strategy/sidekiq.rb +26 -0
- data/lib/chewy/strategy/urgent.rb +1 -1
- data/lib/chewy/type.rb +2 -0
- data/lib/chewy/type/adapter/active_record.rb +7 -3
- data/lib/chewy/type/adapter/mongoid.rb +5 -0
- data/lib/chewy/type/adapter/orm.rb +1 -1
- data/lib/chewy/type/crutch.rb +31 -0
- data/lib/chewy/type/import.rb +7 -6
- data/lib/chewy/type/mapping.rb +7 -3
- data/lib/chewy/type/observe.rb +24 -35
- data/lib/chewy/version.rb +1 -1
- data/spec/chewy/fields/base_spec.rb +26 -19
- data/spec/chewy/query_spec.rb +13 -0
- data/spec/chewy/runtime_spec.rb +1 -1
- data/spec/chewy/strategy/resque_spec.rb +35 -0
- data/spec/chewy/strategy/sidekiq_spec.rb +35 -0
- data/spec/chewy/type/adapter/mongoid_spec.rb +18 -9
- data/spec/chewy/type/mapping_spec.rb +14 -9
- data/spec/chewy/type/observe_spec.rb +22 -7
- data/spec/spec_helper.rb +1 -0
- metadata +23 -2
data/lib/chewy.rb
CHANGED
@@ -21,6 +21,7 @@ require 'chewy/index'
|
|
21
21
|
require 'chewy/type'
|
22
22
|
require 'chewy/fields/base'
|
23
23
|
require 'chewy/fields/root'
|
24
|
+
require 'chewy/railtie' if defined?(::Rails)
|
24
25
|
|
25
26
|
begin
|
26
27
|
require 'kaminari'
|
@@ -35,8 +36,6 @@ begin
|
|
35
36
|
rescue LoadError
|
36
37
|
end
|
37
38
|
|
38
|
-
require 'chewy/railtie' if defined?(::Rails)
|
39
|
-
|
40
39
|
ActiveSupport.on_load(:active_record) do
|
41
40
|
extend Chewy::Type::Observe::ActiveRecordMethods
|
42
41
|
|
data/lib/chewy/config.rb
CHANGED
@@ -25,9 +25,9 @@ module Chewy
|
|
25
25
|
#
|
26
26
|
:root_strategy,
|
27
27
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
28
|
+
# Default request strategy middleware, used in e.g
|
29
|
+
# Rails controllers. See Chewy::Railtie::RequestStrategy
|
30
|
+
# for more info.
|
31
31
|
#
|
32
32
|
:request_strategy,
|
33
33
|
|
data/lib/chewy/fields/base.rb
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
module Chewy
|
2
2
|
module Fields
|
3
3
|
class Base
|
4
|
-
attr_reader :name, :options, :value
|
4
|
+
attr_reader :name, :options, :value, :children
|
5
|
+
attr_accessor :parent
|
5
6
|
|
6
7
|
def initialize(name, options = {})
|
7
|
-
@name, @options
|
8
|
+
@name, @options = name.to_sym, options.deep_symbolize_keys
|
8
9
|
@value = @options.delete(:value)
|
10
|
+
@children = []
|
9
11
|
end
|
10
12
|
|
11
13
|
def multi_field?
|
12
|
-
|
14
|
+
children.any? && !object_field?
|
13
15
|
end
|
14
16
|
|
15
17
|
def object_field?
|
16
|
-
(
|
18
|
+
(children.any? && options[:type].blank?) || ['object', 'nested'].include?(options[:type].to_s)
|
17
19
|
end
|
18
20
|
|
19
|
-
def
|
20
|
-
|
21
|
+
def mappings_hash
|
22
|
+
mapping = children.any? ? {
|
23
|
+
(multi_field? ? :fields : :properties) => children.map(&:mappings_hash).inject(:merge)
|
24
|
+
} : {}
|
25
|
+
mapping.reverse_merge!(options)
|
26
|
+
mapping.reverse_merge!(type: (children.any? ? 'object' : 'string'))
|
27
|
+
{name => mapping}
|
21
28
|
end
|
22
29
|
|
23
30
|
def compose(object, *parent_objects)
|
31
|
+
objects = ([object] + parent_objects.flatten).uniq
|
32
|
+
|
24
33
|
result = if value && value.is_a?(Proc)
|
25
|
-
value.arity
|
26
|
-
|
34
|
+
if value.arity == 0
|
35
|
+
object.instance_exec(&value)
|
36
|
+
elsif value.arity < 0
|
37
|
+
value.call(*object)
|
38
|
+
else
|
39
|
+
value.call(*objects.first(value.arity))
|
40
|
+
end
|
27
41
|
elsif object.is_a?(Hash)
|
28
42
|
object[name] || object[name.to_s]
|
29
43
|
else
|
@@ -31,35 +45,18 @@ module Chewy
|
|
31
45
|
end
|
32
46
|
|
33
47
|
result = if result.respond_to?(:to_ary)
|
34
|
-
result.to_ary.map { |result|
|
48
|
+
result.to_ary.map { |result| compose_children(result, *objects) }
|
35
49
|
else
|
36
|
-
|
37
|
-
end if
|
50
|
+
compose_children(result, *objects)
|
51
|
+
end if children.any? && !multi_field?
|
38
52
|
|
39
53
|
{name => result.as_json(root: false)}
|
40
54
|
end
|
41
55
|
|
42
|
-
def nested(field = nil)
|
43
|
-
if field
|
44
|
-
@nested[field.name] = field
|
45
|
-
else
|
46
|
-
@nested
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def mappings_hash
|
51
|
-
mapping = nested.any? ? {
|
52
|
-
(multi_field? ? :fields : :properties) => nested.values.map(&:mappings_hash).inject(:merge)
|
53
|
-
} : {}
|
54
|
-
mapping.reverse_merge!(options)
|
55
|
-
mapping.reverse_merge!(type: (nested.any? ? 'object' : 'string')) unless root_field?
|
56
|
-
{name => mapping}
|
57
|
-
end
|
58
|
-
|
59
56
|
private
|
60
57
|
|
61
|
-
def
|
62
|
-
|
58
|
+
def compose_children(value, *parent_objects)
|
59
|
+
children.map { |field| field.compose(value, *parent_objects) if value }.compact.inject(:merge)
|
63
60
|
end
|
64
61
|
end
|
65
62
|
end
|
data/lib/chewy/fields/root.rb
CHANGED
@@ -6,30 +6,20 @@ module Chewy
|
|
6
6
|
attr_reader :parent
|
7
7
|
attr_reader :parent_id
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
@parent = options.delete(:parent) || options.delete(:_parent)
|
12
|
-
@parent_id = options.delete(:parent_id)
|
13
|
-
options.reverse_merge!(value: ->(_) { _ })
|
14
|
-
super(name, options)
|
15
|
-
options.delete(:type)
|
16
|
-
@dynamic_templates = []
|
17
|
-
end
|
18
|
-
|
19
|
-
def multi_field?
|
20
|
-
false
|
21
|
-
end
|
9
|
+
def initialize(*args)
|
10
|
+
super(*args)
|
22
11
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
@id = @options.delete(:id) || options.delete(:_id)
|
13
|
+
@parent = @options.delete(:parent) || options.delete(:_parent)
|
14
|
+
@parent_id = @options.delete(:parent_id)
|
15
|
+
@value ||= ->(_) { _ }
|
16
|
+
@dynamic_templates = []
|
17
|
+
@options.delete(:type)
|
29
18
|
end
|
30
19
|
|
31
20
|
def mappings_hash
|
32
21
|
mappings = super
|
22
|
+
mappings[name].delete(:type)
|
33
23
|
|
34
24
|
if dynamic_templates.any?
|
35
25
|
mappings[name][:dynamic_templates] ||= []
|
data/lib/chewy/query.rb
CHANGED
@@ -73,6 +73,20 @@ module Chewy
|
|
73
73
|
chain { criteria.update_request_options explain: (value.nil? ? true : value) }
|
74
74
|
end
|
75
75
|
|
76
|
+
# Adds <tt>script_fields</tt> parameter to search request.
|
77
|
+
# UsersIndex.script_fields(
|
78
|
+
# distance: {
|
79
|
+
# params: {
|
80
|
+
# lat: 37.569976,
|
81
|
+
# lon: -122.351591
|
82
|
+
# },
|
83
|
+
# script: "doc['coordinates'].distanceInMiles(lat, lon)"
|
84
|
+
# }
|
85
|
+
# )
|
86
|
+
def script_fields value
|
87
|
+
chain { criteria.update_script_fields(value) }
|
88
|
+
end
|
89
|
+
|
76
90
|
# Sets query compilation mode for search request.
|
77
91
|
# Not used if only one filter for search is specified.
|
78
92
|
# Possible values:
|
@@ -818,6 +832,20 @@ module Chewy
|
|
818
832
|
chain { criteria.update_types params, purge: true }
|
819
833
|
end
|
820
834
|
|
835
|
+
# Sets <tt>search_type</tt> for request.
|
836
|
+
# For instance, one can use <tt>search_type=count</tt> to fetch only total count of records or to fetch only aggregations without fetching records.
|
837
|
+
#
|
838
|
+
# scope = UsersIndex.search_type(:count)
|
839
|
+
# scope.count == 0 # no records actually fetched
|
840
|
+
# scope.total == 10 # but we know a total count of them
|
841
|
+
#
|
842
|
+
# scope = UsersIndex.aggs(max_age: { max: { field: 'age' } }).search_type(:count)
|
843
|
+
# max_age = scope.aggs['max_age']['value']
|
844
|
+
#
|
845
|
+
def search_type val
|
846
|
+
chain { options.merge!(search_type: val) }
|
847
|
+
end
|
848
|
+
|
821
849
|
# Merges two queries.
|
822
850
|
# Merges all the values in criteria with the same rules as values added manually.
|
823
851
|
#
|
@@ -909,7 +937,12 @@ module Chewy
|
|
909
937
|
end
|
910
938
|
|
911
939
|
def _request
|
912
|
-
@_request ||=
|
940
|
+
@_request ||= begin
|
941
|
+
request = criteria.request_body
|
942
|
+
request.merge!(index: _indexes.map(&:index_name), type: _types.map(&:type_name))
|
943
|
+
request.merge!(search_type: options[:search_type]) if options[:search_type]
|
944
|
+
request
|
945
|
+
end
|
913
946
|
end
|
914
947
|
|
915
948
|
def _response
|
data/lib/chewy/railtie.rb
CHANGED
@@ -50,6 +50,7 @@ module Chewy
|
|
50
50
|
initializer 'chewy.migration_strategy' do
|
51
51
|
ActiveSupport.on_load(:active_record) do
|
52
52
|
ActiveRecord::Migration.send(:include, MigrationStrategy)
|
53
|
+
ActiveRecord::Migrator.send(:include, MigrationStrategy) if defined? ActiveRecord::Migrator
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
@@ -100,12 +100,14 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
100
100
|
|
101
101
|
type = Chewy.derive_type(type_name)
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
updated_document
|
103
|
+
instance_eval <<-RUBY
|
104
|
+
#{agnostic_stub} do |bulk_options|
|
105
|
+
@updated += bulk_options[:body].map do |updated_document|
|
106
|
+
updated_document.deep_symbolize_keys
|
107
|
+
end
|
108
|
+
{}
|
106
109
|
end
|
107
|
-
|
108
|
-
end
|
110
|
+
RUBY
|
109
111
|
|
110
112
|
ActiveSupport::Deprecation.warn('`atomic: false` option is removed and not effective anymore, use `strategy: :atomic` option instead') if options.key?(:atomic)
|
111
113
|
Chewy.strategy(options[:strategy] || :atomic) { block.call }
|
@@ -191,6 +193,14 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
191
193
|
end
|
192
194
|
end
|
193
195
|
|
196
|
+
def agnostic_stub
|
197
|
+
if defined? Mocha
|
198
|
+
"type.stubs(:bulk).with"
|
199
|
+
else
|
200
|
+
"allow(type).to receive(:bulk)"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
194
204
|
def extract_documents *args
|
195
205
|
options = args.extract_options!
|
196
206
|
|
@@ -231,4 +241,4 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
|
|
231
241
|
end
|
232
242
|
difference.none?
|
233
243
|
end
|
234
|
-
end
|
244
|
+
end
|
data/lib/chewy/strategy.rb
CHANGED
@@ -3,6 +3,18 @@ require 'chewy/strategy/bypass'
|
|
3
3
|
require 'chewy/strategy/urgent'
|
4
4
|
require 'chewy/strategy/atomic'
|
5
5
|
|
6
|
+
begin
|
7
|
+
require 'resque'
|
8
|
+
require 'chewy/strategy/resque'
|
9
|
+
rescue LoadError
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'sidekiq'
|
14
|
+
require 'chewy/strategy/sidekiq'
|
15
|
+
rescue LoadError
|
16
|
+
end
|
17
|
+
|
6
18
|
module Chewy
|
7
19
|
# This class represents strategies stack with `:base`
|
8
20
|
# Strategy on top of it. This causes raising exceptions
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Strategy
|
3
|
+
# The strategy works the same way as atomic, but performs
|
4
|
+
# async index update driven by resque
|
5
|
+
#
|
6
|
+
# Chewy.strategy(:resque) do
|
7
|
+
# User.all.map(&:save) # Does nothing here
|
8
|
+
# Post.all.map(&:save) # And here
|
9
|
+
# # It imports all the changed users and posts right here
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
class Resque < Atomic
|
13
|
+
class Worker
|
14
|
+
@queue = :chewy
|
15
|
+
|
16
|
+
def self.perform(type, ids)
|
17
|
+
type.constantize.import!(ids)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def leave
|
22
|
+
@stash.all? { |type, ids| ::Resque.enqueue(Chewy::Strategy::Resque::Worker, type.name, ids) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Strategy
|
3
|
+
# The strategy works the same way as atomic, but performs
|
4
|
+
# async index update driven by sidekiq
|
5
|
+
#
|
6
|
+
# Chewy.strategy(:sidekiq) do
|
7
|
+
# User.all.map(&:save) # Does nothing here
|
8
|
+
# Post.all.map(&:save) # And here
|
9
|
+
# # It imports all the changed users and posts right here
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
class Sidekiq < Atomic
|
13
|
+
class Worker
|
14
|
+
include ::Sidekiq::Worker
|
15
|
+
|
16
|
+
def perform(type, ids)
|
17
|
+
type.constantize.import!(ids)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def leave
|
22
|
+
@stash.all? { |type, ids| Chewy::Strategy::Sidekiq::Worker.perform_async(type.name, ids) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/chewy/type.rb
CHANGED
@@ -3,6 +3,7 @@ require 'chewy/type/mapping'
|
|
3
3
|
require 'chewy/type/wrapper'
|
4
4
|
require 'chewy/type/observe'
|
5
5
|
require 'chewy/type/actions'
|
6
|
+
require 'chewy/type/crutch'
|
6
7
|
require 'chewy/type/import'
|
7
8
|
require 'chewy/type/adapter/object'
|
8
9
|
require 'chewy/type/adapter/active_record'
|
@@ -15,6 +16,7 @@ module Chewy
|
|
15
16
|
include Wrapper
|
16
17
|
include Observe
|
17
18
|
include Actions
|
19
|
+
include Crutch
|
18
20
|
include Import
|
19
21
|
|
20
22
|
singleton_class.delegate :index_name, :client, to: :index
|
@@ -16,7 +16,7 @@ module Chewy
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def import_scope(scope, batch_size)
|
19
|
-
scope = scope.reorder(
|
19
|
+
scope = scope.reorder(target_id.asc).limit(batch_size)
|
20
20
|
|
21
21
|
ids = pluck_ids(scope)
|
22
22
|
result = true
|
@@ -24,7 +24,7 @@ module Chewy
|
|
24
24
|
while ids.any?
|
25
25
|
result &= yield grouped_objects(default_scope_where_ids_in(ids))
|
26
26
|
break if ids.size < batch_size
|
27
|
-
ids = pluck_ids(scope.where(
|
27
|
+
ids = pluck_ids(scope.where(target_id.gt(ids.last)))
|
28
28
|
end
|
29
29
|
|
30
30
|
result
|
@@ -35,13 +35,17 @@ module Chewy
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def scope_where_ids_in(scope, ids)
|
38
|
-
scope.where(
|
38
|
+
scope.where(target_id.in(Array.wrap(ids)))
|
39
39
|
end
|
40
40
|
|
41
41
|
def all_scope
|
42
42
|
target.where(nil)
|
43
43
|
end
|
44
44
|
|
45
|
+
def target_id
|
46
|
+
target.arel_table[target.primary_key]
|
47
|
+
end
|
48
|
+
|
45
49
|
def relation_class
|
46
50
|
::ActiveRecord::Relation
|
47
51
|
end
|