chewy 0.7.0 → 0.8.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 -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
|