chewy 0.8.4 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +34 -0
- data/.rubocop_todo.yml +44 -0
- data/.travis.yml +20 -60
- data/Appraisals +15 -40
- data/CHANGELOG.md +42 -0
- data/Gemfile +1 -0
- data/Guardfile +5 -5
- data/README.md +155 -6
- data/Rakefile +11 -1
- data/chewy.gemspec +5 -7
- data/gemfiles/rails.3.2.activerecord.gemfile +1 -0
- data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +1 -0
- data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +1 -0
- data/gemfiles/rails.4.2.activerecord.gemfile +1 -0
- data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +1 -0
- data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +1 -0
- data/gemfiles/{rails.4.2.mongoid.4.0.0.gemfile → rails.4.2.mongoid.4.0.gemfile} +2 -1
- data/gemfiles/{rails.4.2.mongoid.4.0.0.kaminari.gemfile → rails.4.2.mongoid.4.0.kaminari.gemfile} +2 -1
- data/gemfiles/{rails.4.2.mongoid.4.0.0.will_paginate.gemfile → rails.4.2.mongoid.4.0.will_paginate.gemfile} +2 -1
- data/gemfiles/{rails.4.2.mongoid.5.1.0.gemfile → rails.4.2.mongoid.5.1.gemfile} +2 -1
- data/gemfiles/{rails.4.2.mongoid.5.1.0.kaminari.gemfile → rails.4.2.mongoid.5.1.kaminari.gemfile} +2 -1
- data/gemfiles/{rails.4.2.mongoid.5.1.0.will_paginate.gemfile → rails.4.2.mongoid.5.1.will_paginate.gemfile} +2 -1
- data/gemfiles/{rails.4.0.activerecord.gemfile → rails.5.0.activerecord.gemfile} +4 -2
- data/gemfiles/{rails.4.0.mongoid.4.0.0.kaminari.gemfile → rails.5.0.activerecord.kaminari.gemfile} +4 -2
- data/gemfiles/{rails.4.0.mongoid.4.0.0.will_paginate.gemfile → rails.5.0.activerecord.will_paginate.gemfile} +4 -2
- data/gemfiles/{sequel.4.31.gemfile → sequel.4.38.gemfile} +3 -2
- data/lib/chewy.rb +24 -16
- data/lib/chewy/backports/deep_dup.rb +1 -1
- data/lib/chewy/backports/duplicable.rb +1 -0
- data/lib/chewy/config.rb +13 -7
- data/lib/chewy/errors.rb +4 -4
- data/lib/chewy/fields/base.rb +19 -14
- data/lib/chewy/fields/root.rb +11 -9
- data/lib/chewy/index.rb +38 -25
- data/lib/chewy/index/actions.rb +17 -17
- data/lib/chewy/index/settings.rb +3 -4
- data/lib/chewy/journal.rb +107 -0
- data/lib/chewy/journal/apply.rb +31 -0
- data/lib/chewy/journal/clean.rb +24 -0
- data/lib/chewy/journal/entry.rb +83 -0
- data/lib/chewy/journal/query.rb +87 -0
- data/lib/chewy/log_subscriber.rb +8 -8
- data/lib/chewy/minitest.rb +1 -0
- data/lib/chewy/minitest/helpers.rb +77 -0
- data/lib/chewy/minitest/search_index_receiver.rb +80 -0
- data/lib/chewy/query.rb +116 -60
- data/lib/chewy/query/compose.rb +5 -6
- data/lib/chewy/query/criteria.rb +26 -16
- data/lib/chewy/query/filters.rb +9 -9
- data/lib/chewy/query/loading.rb +2 -2
- data/lib/chewy/query/nodes/and.rb +3 -3
- data/lib/chewy/query/nodes/base.rb +1 -1
- data/lib/chewy/query/nodes/bool.rb +6 -4
- data/lib/chewy/query/nodes/equal.rb +6 -6
- data/lib/chewy/query/nodes/exists.rb +2 -2
- data/lib/chewy/query/nodes/expr.rb +2 -2
- data/lib/chewy/query/nodes/field.rb +35 -31
- data/lib/chewy/query/nodes/has_child.rb +1 -0
- data/lib/chewy/query/nodes/has_parent.rb +1 -0
- data/lib/chewy/query/nodes/has_relation.rb +11 -13
- data/lib/chewy/query/nodes/match_all.rb +1 -1
- data/lib/chewy/query/nodes/missing.rb +2 -2
- data/lib/chewy/query/nodes/not.rb +3 -3
- data/lib/chewy/query/nodes/or.rb +3 -3
- data/lib/chewy/query/nodes/prefix.rb +4 -3
- data/lib/chewy/query/nodes/query.rb +3 -3
- data/lib/chewy/query/nodes/range.rb +11 -11
- data/lib/chewy/query/nodes/raw.rb +1 -1
- data/lib/chewy/query/nodes/regexp.rb +15 -11
- data/lib/chewy/query/nodes/script.rb +6 -6
- data/lib/chewy/query/pagination/will_paginate.rb +2 -2
- data/lib/chewy/railtie.rb +3 -3
- data/lib/chewy/rake_helper.rb +51 -30
- data/lib/chewy/repository.rb +2 -2
- data/lib/chewy/rspec.rb +1 -1
- data/lib/chewy/rspec/update_index.rb +46 -47
- data/lib/chewy/runtime/version.rb +4 -4
- data/lib/chewy/search.rb +7 -5
- data/lib/chewy/strategy.rb +10 -8
- data/lib/chewy/strategy/atomic.rb +2 -2
- data/lib/chewy/strategy/base.rb +4 -4
- data/lib/chewy/strategy/bypass.rb +1 -2
- data/lib/chewy/strategy/sidekiq.rb +2 -0
- data/lib/chewy/strategy/urgent.rb +1 -1
- data/lib/chewy/type.rb +51 -45
- data/lib/chewy/type/adapter/active_record.rb +23 -12
- data/lib/chewy/type/adapter/base.rb +4 -4
- data/lib/chewy/type/adapter/mongoid.rb +6 -6
- data/lib/chewy/type/adapter/object.rb +15 -12
- data/lib/chewy/type/adapter/orm.rb +24 -15
- data/lib/chewy/type/adapter/sequel.rb +11 -7
- data/lib/chewy/type/crutch.rb +4 -3
- data/lib/chewy/type/import.rb +51 -32
- data/lib/chewy/type/mapping.rb +17 -17
- data/lib/chewy/type/observe.rb +9 -7
- data/lib/chewy/type/witchcraft.rb +62 -23
- data/lib/chewy/type/wrapper.rb +20 -14
- data/lib/chewy/version.rb +1 -1
- data/lib/generators/chewy/install_generator.rb +3 -3
- data/lib/tasks/chewy.rake +28 -23
- data/spec/chewy/config_spec.rb +33 -12
- data/spec/chewy/fields/base_spec.rb +143 -154
- data/spec/chewy/fields/root_spec.rb +22 -20
- data/spec/chewy/fields/time_fields_spec.rb +11 -9
- data/spec/chewy/index/actions_spec.rb +27 -4
- data/spec/chewy/index/aliases_spec.rb +2 -2
- data/spec/chewy/index/settings_spec.rb +72 -50
- data/spec/chewy/index_spec.rb +95 -43
- data/spec/chewy/journal/apply_spec.rb +120 -0
- data/spec/chewy/journal/entry_spec.rb +237 -0
- data/spec/chewy/journal_spec.rb +173 -0
- data/spec/chewy/minitest/helpers_spec.rb +90 -0
- data/spec/chewy/minitest/search_index_receiver_spec.rb +120 -0
- data/spec/chewy/query/criteria_spec.rb +504 -237
- data/spec/chewy/query/filters_spec.rb +94 -66
- data/spec/chewy/query/loading_spec.rb +76 -40
- data/spec/chewy/query/nodes/and_spec.rb +3 -7
- data/spec/chewy/query/nodes/bool_spec.rb +5 -13
- data/spec/chewy/query/nodes/equal_spec.rb +20 -20
- data/spec/chewy/query/nodes/exists_spec.rb +7 -7
- data/spec/chewy/query/nodes/has_child_spec.rb +42 -23
- data/spec/chewy/query/nodes/has_parent_spec.rb +42 -23
- data/spec/chewy/query/nodes/match_all_spec.rb +2 -2
- data/spec/chewy/query/nodes/missing_spec.rb +6 -5
- data/spec/chewy/query/nodes/not_spec.rb +3 -7
- data/spec/chewy/query/nodes/or_spec.rb +3 -7
- data/spec/chewy/query/nodes/prefix_spec.rb +6 -6
- data/spec/chewy/query/nodes/query_spec.rb +3 -3
- data/spec/chewy/query/nodes/range_spec.rb +19 -19
- data/spec/chewy/query/nodes/raw_spec.rb +2 -2
- data/spec/chewy/query/nodes/regexp_spec.rb +31 -19
- data/spec/chewy/query/nodes/script_spec.rb +5 -5
- data/spec/chewy/query/pagination/kaminari_spec.rb +2 -2
- data/spec/chewy/query/pagination/will_paginage_spec.rb +6 -7
- data/spec/chewy/query/pagination_spec.rb +2 -3
- data/spec/chewy/query_spec.rb +208 -145
- data/spec/chewy/repository_spec.rb +8 -8
- data/spec/chewy/rspec/update_index_spec.rb +180 -111
- data/spec/chewy/search_spec.rb +8 -8
- data/spec/chewy/strategy/active_job_spec.rb +2 -2
- data/spec/chewy/strategy/atomic_spec.rb +4 -1
- data/spec/chewy/strategy/resque_spec.rb +2 -2
- data/spec/chewy/strategy/sidekiq_spec.rb +2 -2
- data/spec/chewy/type/actions_spec.rb +1 -1
- data/spec/chewy/type/adapter/active_record_spec.rb +255 -149
- data/spec/chewy/type/adapter/mongoid_spec.rb +169 -108
- data/spec/chewy/type/adapter/object_spec.rb +56 -40
- data/spec/chewy/type/adapter/sequel_spec.rb +248 -163
- data/spec/chewy/type/import_spec.rb +78 -47
- data/spec/chewy/type/mapping_spec.rb +6 -6
- data/spec/chewy/type/observe_spec.rb +20 -14
- data/spec/chewy/type/witchcraft_spec.rb +89 -43
- data/spec/chewy/type_spec.rb +4 -3
- data/spec/chewy_spec.rb +10 -8
- data/spec/spec_helper.rb +3 -0
- data/spec/support/active_record.rb +1 -1
- data/spec/support/class_helpers.rb +10 -11
- data/spec/support/mongoid.rb +2 -2
- data/spec/support/sequel.rb +1 -1
- metadata +65 -35
- data/gemfiles/rails.4.0.activerecord.kaminari.gemfile +0 -14
- data/gemfiles/rails.4.0.activerecord.will_paginate.gemfile +0 -14
- data/gemfiles/rails.4.0.mongoid.4.0.0.gemfile +0 -15
- data/gemfiles/rails.4.0.mongoid.5.1.0.gemfile +0 -15
- data/gemfiles/rails.4.0.mongoid.5.1.0.kaminari.gemfile +0 -14
- data/gemfiles/rails.4.0.mongoid.5.1.0.will_paginate.gemfile +0 -14
- data/gemfiles/rails.4.1.activerecord.gemfile +0 -15
- data/gemfiles/rails.4.1.activerecord.kaminari.gemfile +0 -14
- data/gemfiles/rails.4.1.activerecord.will_paginate.gemfile +0 -14
- data/gemfiles/rails.4.1.mongoid.4.0.0.gemfile +0 -15
- data/gemfiles/rails.4.1.mongoid.4.0.0.kaminari.gemfile +0 -14
- data/gemfiles/rails.4.1.mongoid.4.0.0.will_paginate.gemfile +0 -14
- data/gemfiles/rails.4.1.mongoid.5.1.0.gemfile +0 -15
- data/gemfiles/rails.4.1.mongoid.5.1.0.kaminari.gemfile +0 -14
- data/gemfiles/rails.4.1.mongoid.5.1.0.will_paginate.gemfile +0 -14
- data/gemfiles/rails.5.0.0.beta3.activerecord.gemfile +0 -16
- data/gemfiles/rails.5.0.0.beta3.activerecord.kaminari.gemfile +0 -16
- data/gemfiles/rails.5.0.0.beta3.activerecord.will_paginate.gemfile +0 -15
@@ -0,0 +1,31 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Journal
|
3
|
+
module Apply
|
4
|
+
# Applies all changes that were done since some moment
|
5
|
+
#
|
6
|
+
# @param time [Integer] timestamp from which changes will be applied
|
7
|
+
# @param options [Hash]
|
8
|
+
# @option options [Integer] :retries maximum number of attempts to make journal "empty". By default is set to 10
|
9
|
+
# @option options [Boolean] :once shows whether we should try until the journal is clean. If set to true, :retries is ignored
|
10
|
+
# @option options [Array<Chewy::Index>] :only filters the resulting set of records by index name
|
11
|
+
def since(time, options = {})
|
12
|
+
previous_entries = []
|
13
|
+
retries = options[:retries] || 10
|
14
|
+
stage = 0
|
15
|
+
while stage < retries
|
16
|
+
stage += 1
|
17
|
+
previous_entries.select { |entry| entry.created_at.to_i >= time }
|
18
|
+
entries = Entry.group(Entry.since(time, options[:only]))
|
19
|
+
Entry.subtract(entries, previous_entries)
|
20
|
+
break if entries.length.zero?
|
21
|
+
ActiveSupport::Notifications.instrument 'apply_journal.chewy', stage: stage
|
22
|
+
entries.each { |entry| entry.index.import(entry.object_ids, journal: false) }
|
23
|
+
break if options[:once]
|
24
|
+
time = Entry.recent_timestamp(entries)
|
25
|
+
previous_entries = entries
|
26
|
+
end
|
27
|
+
end
|
28
|
+
module_function :since
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Journal
|
3
|
+
module Clean
|
4
|
+
DELETE_BATCH_SIZE = 10_000
|
5
|
+
|
6
|
+
def until(time)
|
7
|
+
query = Query.new(time, :lte, nil, false).to_h
|
8
|
+
search_query = query.merge(fields: ['_id'], size: DELETE_BATCH_SIZE)
|
9
|
+
index_name = Journal.index_name
|
10
|
+
|
11
|
+
count = Chewy.client.count(index: index_name, body: query)['count']
|
12
|
+
|
13
|
+
(count.to_f / DELETE_BATCH_SIZE).ceil.times do
|
14
|
+
ids = Chewy.client.search(index: index_name, body: search_query)['hits']['hits'].map { |doc| doc['_id'] }
|
15
|
+
Chewy.client.bulk(body: ids.map { |id| { delete: { _index: index_name, _type: Journal.type_name, _id: id } } }, refresh: true)
|
16
|
+
end
|
17
|
+
|
18
|
+
Chewy.wait_for_status
|
19
|
+
count
|
20
|
+
end
|
21
|
+
module_function :until
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Journal
|
3
|
+
# Describes a journal entry and provides necessary assisting methods
|
4
|
+
class Entry
|
5
|
+
ATTRIBUTES = %w(index_name type_name action object_ids created_at).freeze
|
6
|
+
|
7
|
+
attr_accessor(*ATTRIBUTES)
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
attributes.slice(*ATTRIBUTES).each do |attr, value|
|
11
|
+
public_send("#{attr}=", value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Loads all entries since some time
|
16
|
+
# @param time [Integer] a timestamp from which we load a journal
|
17
|
+
# @param indices [Array<Chewy::Index>] journal records related to these indices will be loaded only
|
18
|
+
def self.since(time, indices = [])
|
19
|
+
query = Query.new(time, :gte, indices).to_h
|
20
|
+
parameters = { index: Journal.index_name, type: Journal.type_name, body: query }
|
21
|
+
size = Chewy.client.search(search_type: 'count', **parameters)['hits']['total']
|
22
|
+
if size > 0
|
23
|
+
Chewy.client
|
24
|
+
.search(size: size, sort: 'created_at', **parameters)['hits']['hits']
|
25
|
+
.map { |r| new(r['_source']) }
|
26
|
+
else
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Groups a list of entries by full type name to decrease
|
32
|
+
# a number of calls to Elasticsearch during journal apply
|
33
|
+
# @param entries [Array<Chewy::Journal::Entry>]
|
34
|
+
def self.group(entries)
|
35
|
+
entries.group_by(&:full_type_name)
|
36
|
+
.map { |_, grouped_entries| grouped_entries.reduce(:merge) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Allows to filter one list of entries from another
|
40
|
+
# If any records with the same full type name are found then their object_ids will be subtracted
|
41
|
+
# @param from [Array<Chewy::Journal::Entry>] from which list we subtract another
|
42
|
+
# @param what [Array<Chewy::Journal::Entry>] what we subtract
|
43
|
+
def self.subtract(from, what)
|
44
|
+
return from if what.empty?
|
45
|
+
from.each do |from_entry|
|
46
|
+
what.each do |what_entry|
|
47
|
+
from_entry.object_ids -= what_entry.object_ids if from_entry == what_entry
|
48
|
+
end
|
49
|
+
end
|
50
|
+
from.delete_if(&:empty?)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get the most recent timestamp from a list of entries
|
54
|
+
# @param entries [Array<Chewy::Journal::Entry>]
|
55
|
+
def self.recent_timestamp(entries)
|
56
|
+
entries.map { |entry| entry.created_at.to_i }.max
|
57
|
+
end
|
58
|
+
|
59
|
+
def index
|
60
|
+
@index ||= Chewy.derive_type(full_type_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def full_type_name
|
64
|
+
"#{index_name}##{type_name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def merge(other)
|
68
|
+
return self if other.nil? || full_type_name != other.full_type_name
|
69
|
+
self.object_ids |= other.object_ids
|
70
|
+
self.created_at = [created_at, other.created_at].compact.max
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def ==(other)
|
75
|
+
full_type_name == other.try(:full_type_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def empty?
|
79
|
+
!object_ids || object_ids.empty?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Journal
|
3
|
+
class Query
|
4
|
+
# @param time [Integer]
|
5
|
+
# @param comparator [Symbol, String] lt, lte, gt, gte
|
6
|
+
# @param indices [Array<Chewy::Index>] which indices should only be selected in the resulting set
|
7
|
+
# @param use_filter [Boolean] should we use filter or query
|
8
|
+
def initialize(time, comparator, indices, use_filter = true)
|
9
|
+
@time = time
|
10
|
+
@comparator = comparator
|
11
|
+
@indices = indices || []
|
12
|
+
@use_filter = use_filter
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Hash] ElasicSearch query
|
16
|
+
def to_h
|
17
|
+
@query ||= { query: { filtered: filtered } }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def filtered
|
23
|
+
if @use_filter
|
24
|
+
using_filter_query
|
25
|
+
else
|
26
|
+
using_query_query
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def using_filter_query
|
31
|
+
if @indices.any?
|
32
|
+
{
|
33
|
+
filter: {
|
34
|
+
bool: {
|
35
|
+
must: [
|
36
|
+
range_filter,
|
37
|
+
bool: {
|
38
|
+
should: @indices.collect { |i| index_filter(i) }
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
else
|
45
|
+
{
|
46
|
+
filter: range_filter
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def using_query_query
|
52
|
+
if @indices.any?
|
53
|
+
{
|
54
|
+
query: range_filter,
|
55
|
+
filter: {
|
56
|
+
bool: {
|
57
|
+
should: @indices.collect { |i| index_filter(i) }
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
else
|
62
|
+
{
|
63
|
+
query: range_filter
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def range_filter
|
69
|
+
{
|
70
|
+
range: {
|
71
|
+
created_at: {
|
72
|
+
@comparator => @time.to_i
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def index_filter(index)
|
79
|
+
{
|
80
|
+
term: {
|
81
|
+
index_name: index.derivable_index_name
|
82
|
+
}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/chewy/log_subscriber.rb
CHANGED
@@ -16,17 +16,17 @@ module Chewy
|
|
16
16
|
render_action('Delete by Query', event) { |payload| payload[:request] }
|
17
17
|
end
|
18
18
|
|
19
|
-
def render_action
|
19
|
+
def render_action(action, event)
|
20
20
|
payload = event.payload
|
21
|
-
description =
|
21
|
+
description = yield(payload)
|
22
22
|
|
23
|
-
if description.
|
24
|
-
subject = payload[:type].presence || payload[:index]
|
25
|
-
action = "#{subject} #{action} (#{event.duration.round(1)}ms)"
|
26
|
-
action = color(action, GREEN, true)
|
23
|
+
return if description.blank?
|
27
24
|
|
28
|
-
|
29
|
-
|
25
|
+
subject = payload[:type].presence || payload[:index]
|
26
|
+
action = "#{subject} #{action} (#{event.duration.round(1)}ms)"
|
27
|
+
action = color(action, GREEN, true)
|
28
|
+
|
29
|
+
debug(" #{action} #{description}")
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'chewy/minitest/helpers'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'search_index_receiver'
|
2
|
+
|
3
|
+
module Chewy
|
4
|
+
module Minitest
|
5
|
+
module Helpers
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# Assert that an index *changes* during a block.
|
9
|
+
# @param (Chewy::Type) index the index / type to watch, eg EntitiesIndex::Entity.
|
10
|
+
# @param (Symbol) strategy the Chewy strategy to use around the block. See Chewy docs.
|
11
|
+
# @param (boolean) assert the index changes
|
12
|
+
# @param (boolean) bypass_actual_index
|
13
|
+
# True to preempt the http call to Elastic, false otherwise.
|
14
|
+
# Should be set to true unless actually testing search functionality.
|
15
|
+
#
|
16
|
+
# @return (SearchIndexReceiver) for optional further assertions on the nature of the index changes.
|
17
|
+
def assert_indexes(index, strategy: :atomic, bypass_actual_index: true)
|
18
|
+
type = Chewy.derive_type index
|
19
|
+
receiver = SearchIndexReceiver.new
|
20
|
+
|
21
|
+
bulk_method = type.method :bulk
|
22
|
+
# Manually mocking #bulk because we need to properly capture `self`
|
23
|
+
bulk_mock = lambda do |*bulk_args|
|
24
|
+
receiver.catch bulk_args, self
|
25
|
+
|
26
|
+
bulk_method.call(*bulk_args) unless bypass_actual_index
|
27
|
+
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
|
31
|
+
type.define_singleton_method :bulk, bulk_mock
|
32
|
+
|
33
|
+
Chewy.strategy(strategy) do
|
34
|
+
yield
|
35
|
+
end
|
36
|
+
|
37
|
+
type.define_singleton_method :bulk, bulk_method
|
38
|
+
|
39
|
+
assert_includes receiver.updated_indexes, index, "Expected #{index} to be updated but it wasn't"
|
40
|
+
|
41
|
+
receiver
|
42
|
+
end
|
43
|
+
|
44
|
+
# Run indexing for the database changes during the block provided.
|
45
|
+
# By default, indexing is run at the end of the block.
|
46
|
+
# @param (Symbol) strategy the Chewy index update strategy see Chewy docs.
|
47
|
+
def run_indexing(strategy: :atomic)
|
48
|
+
Chewy.strategy strategy do
|
49
|
+
yield
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
# Declare that all tests in this file require real indexing, always.
|
55
|
+
# In my completely unscientific experiments, this roughly doubled test runtime.
|
56
|
+
# Use with trepidation.
|
57
|
+
def index_everything!
|
58
|
+
setup do
|
59
|
+
Chewy.strategy :urgent
|
60
|
+
end
|
61
|
+
|
62
|
+
teardown do
|
63
|
+
Chewy.strategy.pop
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
included do
|
69
|
+
teardown do
|
70
|
+
# always destroy indexes between tests
|
71
|
+
# Prevent croll pollution of test cases due to indexing
|
72
|
+
Chewy.massacre
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Test helper class to provide minitest hooks for Chewy::Index testing.
|
2
|
+
#
|
3
|
+
# @note Intended to be used in conjunction with a test helper which mocks over the #bulk
|
4
|
+
# method on a Chewy::Type class. (See SearchTestHelper)
|
5
|
+
#
|
6
|
+
# The class will capture the data from the *param on the Chewy::Type#bulk method and
|
7
|
+
# aggregate the data for test analysis.
|
8
|
+
class SearchIndexReceiver
|
9
|
+
def initialize
|
10
|
+
@mutations = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param bulk_params the bulk_params that should be sent to the Chewy::Type#bulk method.
|
14
|
+
# @param (Chewy::Type) type the Index::Type executing this query.
|
15
|
+
def catch(bulk_params, type)
|
16
|
+
Array.wrap(bulk_params).map { |y| y[:body] }.flatten.each do |update|
|
17
|
+
if update[:delete]
|
18
|
+
mutation_for(type).deletes << update[:delete][:_id]
|
19
|
+
elsif update[:index]
|
20
|
+
mutation_for(type).indexes << update[:index]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param index return only index requests to the specified Chewy::Type index.
|
26
|
+
# @return the index changes captured by the mock.
|
27
|
+
def indexes_for(index = nil)
|
28
|
+
if index
|
29
|
+
mutation_for(index).indexes
|
30
|
+
else
|
31
|
+
Hash[
|
32
|
+
@mutations.map { |a, b| [a, b.indexes] }
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
alias_method :indexes, :indexes_for
|
37
|
+
|
38
|
+
# @param index return only delete requests to the specified Chewy::Type index.
|
39
|
+
# @return the index deletes captured by the mock.
|
40
|
+
def deletes_for(index = nil)
|
41
|
+
if index
|
42
|
+
mutation_for(index).deletes
|
43
|
+
else
|
44
|
+
Hash[
|
45
|
+
@mutations.map { |a, b| [a, b.deletes] }
|
46
|
+
]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias_method :deletes, :deletes_for
|
50
|
+
|
51
|
+
# Check to see if a given object has been indexed.
|
52
|
+
# @param (#id) obj the object to look for.
|
53
|
+
# @param Chewy::Type what type the object should be indexed as.
|
54
|
+
# @return bool if the object was indexed.
|
55
|
+
def indexed?(obj, type)
|
56
|
+
indexes_for(type).map { |i| i[:_id] }.include? obj.id
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check to see if a given object has been deleted.
|
60
|
+
# @param (#id) obj the object to look for.
|
61
|
+
# @param Chewy::Type what type the object should have been deleted from.
|
62
|
+
# @return bool if the object was deleted.
|
63
|
+
def deleted?(obj, type)
|
64
|
+
deletes_for(type).include? obj.id
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return a list of Chewy::Type indexes changed.
|
68
|
+
def updated_indexes
|
69
|
+
@mutations.keys
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Get the mutation object for a given type.
|
75
|
+
# @param (Chewy::Type) type the index type to fetch.
|
76
|
+
# @return (#indexes, #deletes) an object with a list of indexes and a list of deletes.
|
77
|
+
def mutation_for(type)
|
78
|
+
@mutations[type] ||= OpenStruct.new(indexes: [], deletes: [])
|
79
|
+
end
|
80
|
+
end
|
data/lib/chewy/query.rb
CHANGED
@@ -23,7 +23,7 @@ module Chewy
|
|
23
23
|
|
24
24
|
attr_reader :_indexes, :_types, :options, :criteria
|
25
25
|
|
26
|
-
def initialize
|
26
|
+
def initialize(*indexes_or_types_and_options)
|
27
27
|
@options = indexes_or_types_and_options.extract_options!
|
28
28
|
@_types = indexes_or_types_and_options.select { |klass| klass < Chewy::Type }
|
29
29
|
@_indexes = indexes_or_types_and_options.select { |klass| klass < Chewy::Index }
|
@@ -39,12 +39,8 @@ module Chewy
|
|
39
39
|
# UsersIndex.filter(term: {name: 'Johny'}) == UsersIndex.filter(term: {name: 'Johny'}).to_a # => true
|
40
40
|
# UsersIndex.filter(term: {name: 'Johny'}) == UsersIndex.filter(term: {name: 'Winnie'}) # => false
|
41
41
|
#
|
42
|
-
def ==
|
43
|
-
super ||
|
44
|
-
other.criteria == criteria
|
45
|
-
else
|
46
|
-
to_a == other
|
47
|
-
end
|
42
|
+
def ==(other)
|
43
|
+
super || other.is_a?(self.class) ? other.criteria == criteria : other == to_a
|
48
44
|
end
|
49
45
|
|
50
46
|
# Adds <tt>explain</tt> parameter to search request.
|
@@ -59,7 +55,7 @@ module Chewy
|
|
59
55
|
#
|
60
56
|
# UsersIndex::User.filter(term: {name: 'Johny'}).explain.first._explanation # => {...}
|
61
57
|
#
|
62
|
-
def explain
|
58
|
+
def explain(value = nil)
|
63
59
|
chain { criteria.update_request_options explain: (value.nil? ? true : value) }
|
64
60
|
end
|
65
61
|
|
@@ -73,7 +69,7 @@ module Chewy
|
|
73
69
|
# script: "doc['coordinates'].distanceInMiles(lat, lon)"
|
74
70
|
# }
|
75
71
|
# )
|
76
|
-
def script_fields
|
72
|
+
def script_fields(value)
|
77
73
|
chain { criteria.update_script_fields(value) }
|
78
74
|
end
|
79
75
|
|
@@ -143,7 +139,7 @@ module Chewy
|
|
143
139
|
# Chewy.query_mode = :dis_max
|
144
140
|
# Chewy.query_mode = '50%'
|
145
141
|
#
|
146
|
-
def query_mode
|
142
|
+
def query_mode(value)
|
147
143
|
chain { criteria.update_options query_mode: value }
|
148
144
|
end
|
149
145
|
|
@@ -215,7 +211,7 @@ module Chewy
|
|
215
211
|
# Chewy.filter_mode = :should
|
216
212
|
# Chewy.filter_mode = '50%'
|
217
213
|
#
|
218
|
-
def filter_mode
|
214
|
+
def filter_mode(value)
|
219
215
|
chain { criteria.update_options filter_mode: value }
|
220
216
|
end
|
221
217
|
|
@@ -227,7 +223,7 @@ module Chewy
|
|
227
223
|
# UsersIndex.post_filter{ name == 'Johny' }.post_filter{ age <= 42 }.post_filter_mode(:should)
|
228
224
|
# UsersIndex.post_filter{ name == 'Johny' }.post_filter{ age <= 42 }.post_filter_mode('50%')
|
229
225
|
#
|
230
|
-
def post_filter_mode
|
226
|
+
def post_filter_mode(value)
|
231
227
|
chain { criteria.update_options post_filter_mode: value }
|
232
228
|
end
|
233
229
|
|
@@ -276,7 +272,7 @@ module Chewy
|
|
276
272
|
# Use the timeout because it is important to your SLA, not because you want
|
277
273
|
# to abort the execution of long running queries.
|
278
274
|
#
|
279
|
-
def timeout
|
275
|
+
def timeout(value)
|
280
276
|
chain { criteria.update_request_options timeout: value }
|
281
277
|
end
|
282
278
|
|
@@ -289,8 +285,8 @@ module Chewy
|
|
289
285
|
# size: 100
|
290
286
|
# }}
|
291
287
|
#
|
292
|
-
def limit
|
293
|
-
chain { criteria.update_request_options size: Integer(value) }
|
288
|
+
def limit(value = nil, &block)
|
289
|
+
chain { criteria.update_request_options size: block || Integer(value) }
|
294
290
|
end
|
295
291
|
|
296
292
|
# Sets elasticsearch <tt>from</tt> search request param
|
@@ -301,15 +297,15 @@ module Chewy
|
|
301
297
|
# from: 300
|
302
298
|
# }}
|
303
299
|
#
|
304
|
-
def offset
|
305
|
-
chain { criteria.update_request_options from: Integer(value) }
|
300
|
+
def offset(value = nil, &block)
|
301
|
+
chain { criteria.update_request_options from: block || Integer(value) }
|
306
302
|
end
|
307
303
|
|
308
304
|
# Elasticsearch highlight query option support
|
309
305
|
#
|
310
306
|
# UsersIndex.query(...).highlight(fields: { ... })
|
311
307
|
#
|
312
|
-
def highlight
|
308
|
+
def highlight(value)
|
313
309
|
chain { criteria.update_request_options highlight: value }
|
314
310
|
end
|
315
311
|
|
@@ -317,7 +313,7 @@ module Chewy
|
|
317
313
|
#
|
318
314
|
# UsersIndex.query(...).rescore(query: { ... })
|
319
315
|
#
|
320
|
-
def rescore
|
316
|
+
def rescore(value)
|
321
317
|
chain { criteria.update_request_options rescore: value }
|
322
318
|
end
|
323
319
|
|
@@ -325,10 +321,18 @@ module Chewy
|
|
325
321
|
#
|
326
322
|
# UsersIndex.query(...).min_score(0.5)
|
327
323
|
#
|
328
|
-
def min_score
|
324
|
+
def min_score(value)
|
329
325
|
chain { criteria.update_request_options min_score: value }
|
330
326
|
end
|
331
327
|
|
328
|
+
# Elasticsearch track_scores option support
|
329
|
+
#
|
330
|
+
# UsersIndex.query(...).track_scores(true)
|
331
|
+
#
|
332
|
+
def track_scores(value)
|
333
|
+
chain { criteria.update_request_options track_scores: value }
|
334
|
+
end
|
335
|
+
|
332
336
|
# Adds facets section to the search request.
|
333
337
|
# All the chained facets a merged and added to the
|
334
338
|
# search request
|
@@ -342,7 +346,7 @@ module Chewy
|
|
342
346
|
# If called parameterless - returns result facets from ES performing request.
|
343
347
|
# Returns empty hash if no facets was requested or resulted.
|
344
348
|
#
|
345
|
-
def facets
|
349
|
+
def facets(params = nil)
|
346
350
|
raise RemovedFeature, 'removed in elasticsearch 2.0' if Runtime.version >= '2.0'
|
347
351
|
if params
|
348
352
|
chain { criteria.update_facets params }
|
@@ -395,6 +399,28 @@ module Chewy
|
|
395
399
|
chain { criteria.update_scores scoring }
|
396
400
|
end
|
397
401
|
|
402
|
+
# Add a weight scoring function to the search. All scores are
|
403
|
+
# added to the search request and combinded according to
|
404
|
+
# <tt>boost_mode</tt> and <tt>score_mode</tt>
|
405
|
+
#
|
406
|
+
# This probably only makes sense if you specify a filter
|
407
|
+
# for the weight as well.
|
408
|
+
#
|
409
|
+
# UsersIndex.weight(23, filter: { term: { foo: :bar} })
|
410
|
+
# # => {body:
|
411
|
+
# query: {
|
412
|
+
# function_score: {
|
413
|
+
# query: { ...},
|
414
|
+
# functions: [{
|
415
|
+
# weight: 23,
|
416
|
+
# filter: { term: { foo: :bar } }
|
417
|
+
# }]
|
418
|
+
# } } }
|
419
|
+
def weight(factor, options = {})
|
420
|
+
scoring = options.merge(weight: factor.to_i)
|
421
|
+
chain { criteria.update_scores scoring }
|
422
|
+
end
|
423
|
+
|
398
424
|
# Adds a random score to the search request. All scores are
|
399
425
|
# added to the search request and combinded according to
|
400
426
|
# <tt>boost_mode</tt> and <tt>score_mode</tt>
|
@@ -484,11 +510,20 @@ module Chewy
|
|
484
510
|
def decay(function, field, options = {})
|
485
511
|
field_options = options.extract!(:origin, :scale, :offset, :decay).delete_if { |_, v| v.nil? }
|
486
512
|
scoring = options.merge(function => {
|
487
|
-
|
488
|
-
|
513
|
+
field => field_options
|
514
|
+
})
|
489
515
|
chain { criteria.update_scores scoring }
|
490
516
|
end
|
491
517
|
|
518
|
+
# Sets <tt>preference</tt> for request.
|
519
|
+
# For instance, one can use <tt>preference=_primary</tt> to execute only on the primary shards.
|
520
|
+
#
|
521
|
+
# scope = UsersIndex.preference(:_primary)
|
522
|
+
#
|
523
|
+
def preference(value)
|
524
|
+
chain { criteria.update_search_options preference: value }
|
525
|
+
end
|
526
|
+
|
492
527
|
# Sets elasticsearch <tt>aggregations</tt> search request param
|
493
528
|
#
|
494
529
|
# UsersIndex.filter{ name == 'Johny' }.aggregations(category_id: {terms: {field: 'category_ids'}})
|
@@ -501,7 +536,7 @@ module Chewy
|
|
501
536
|
# }
|
502
537
|
# }}
|
503
538
|
#
|
504
|
-
def aggregations
|
539
|
+
def aggregations(params = nil)
|
505
540
|
@_named_aggs ||= _build_named_aggs
|
506
541
|
@_fully_qualified_named_aggs ||= _build_fqn_aggs
|
507
542
|
if params
|
@@ -512,7 +547,7 @@ module Chewy
|
|
512
547
|
_response['aggregations'] || {}
|
513
548
|
end
|
514
549
|
end
|
515
|
-
|
550
|
+
alias_method :aggs, :aggregations
|
516
551
|
|
517
552
|
# In this simplest of implementations each named aggregation must be uniquely named
|
518
553
|
def _build_named_aggs
|
@@ -562,7 +597,7 @@ module Chewy
|
|
562
597
|
# }
|
563
598
|
# }}
|
564
599
|
#
|
565
|
-
def suggest
|
600
|
+
def suggest(params = nil)
|
566
601
|
if params
|
567
602
|
chain { criteria.update_suggest params }
|
568
603
|
else
|
@@ -595,7 +630,7 @@ module Chewy
|
|
595
630
|
# } }
|
596
631
|
# }}
|
597
632
|
#
|
598
|
-
def strategy
|
633
|
+
def strategy(value = nil)
|
599
634
|
chain { criteria.update_options strategy: value }
|
600
635
|
end
|
601
636
|
|
@@ -621,7 +656,7 @@ module Chewy
|
|
621
656
|
# query: {text: {name: 'Johny'}}
|
622
657
|
# }}
|
623
658
|
#
|
624
|
-
def query
|
659
|
+
def query(params)
|
625
660
|
chain { criteria.update_queries params }
|
626
661
|
end
|
627
662
|
|
@@ -653,7 +688,7 @@ module Chewy
|
|
653
688
|
# filter: {term: {name: 'Johny'}}
|
654
689
|
# }}}}
|
655
690
|
#
|
656
|
-
def filter
|
691
|
+
def filter(params = nil, &block)
|
657
692
|
params = Filters.new(&block).__render__ if block
|
658
693
|
chain { criteria.update_filters params }
|
659
694
|
end
|
@@ -684,7 +719,7 @@ module Chewy
|
|
684
719
|
# post_filter: {term: {name: 'Johny'}}
|
685
720
|
# }}
|
686
721
|
#
|
687
|
-
def post_filter
|
722
|
+
def post_filter(params = nil, &block)
|
688
723
|
params = Filters.new(&block).__render__ if block
|
689
724
|
chain { criteria.update_post_filters params }
|
690
725
|
end
|
@@ -722,7 +757,7 @@ module Chewy
|
|
722
757
|
#
|
723
758
|
# Default value for <tt>:boost_mode</tt> might be changed
|
724
759
|
# with <tt>Chewy.score_mode</tt> config option.
|
725
|
-
def boost_mode
|
760
|
+
def boost_mode(value)
|
726
761
|
chain { criteria.update_options boost_mode: value }
|
727
762
|
end
|
728
763
|
|
@@ -762,7 +797,7 @@ module Chewy
|
|
762
797
|
#
|
763
798
|
# Chewy.score_mode = :first
|
764
799
|
#
|
765
|
-
def score_mode
|
800
|
+
def score_mode(value)
|
766
801
|
chain { criteria.update_options score_mode: value }
|
767
802
|
end
|
768
803
|
|
@@ -774,7 +809,7 @@ module Chewy
|
|
774
809
|
# sort: ['first_name', 'last_name', {age: 'desc'}, {price: {order: 'asc', mode: 'avg'}}]
|
775
810
|
# }}
|
776
811
|
#
|
777
|
-
def order
|
812
|
+
def order(*params)
|
778
813
|
chain { criteria.update_sort params }
|
779
814
|
end
|
780
815
|
|
@@ -786,7 +821,7 @@ module Chewy
|
|
786
821
|
# sort: [{price: {order: 'asc', mode: 'avg'}}]
|
787
822
|
# }}
|
788
823
|
#
|
789
|
-
def reorder
|
824
|
+
def reorder(*params)
|
790
825
|
chain { criteria.update_sort params, purge: true }
|
791
826
|
end
|
792
827
|
|
@@ -798,7 +833,7 @@ module Chewy
|
|
798
833
|
# fields: ['first_name', 'last_name', 'age']
|
799
834
|
# }}
|
800
835
|
#
|
801
|
-
def only
|
836
|
+
def only(*params)
|
802
837
|
chain { criteria.update_fields params }
|
803
838
|
end
|
804
839
|
|
@@ -810,7 +845,7 @@ module Chewy
|
|
810
845
|
# fields: ['age']
|
811
846
|
# }}
|
812
847
|
#
|
813
|
-
def only!
|
848
|
+
def only!(*params)
|
814
849
|
chain { criteria.update_fields params, purge: true }
|
815
850
|
end
|
816
851
|
|
@@ -846,7 +881,7 @@ module Chewy
|
|
846
881
|
# ]}
|
847
882
|
# }}}}
|
848
883
|
#
|
849
|
-
def types
|
884
|
+
def types(*params)
|
850
885
|
chain { criteria.update_types params }
|
851
886
|
end
|
852
887
|
|
@@ -858,7 +893,7 @@ module Chewy
|
|
858
893
|
# filter: {type: {value: 'manager'}}
|
859
894
|
# }}}}
|
860
895
|
#
|
861
|
-
def types!
|
896
|
+
def types!(*params)
|
862
897
|
chain { criteria.update_types params, purge: true }
|
863
898
|
end
|
864
899
|
|
@@ -872,8 +907,8 @@ module Chewy
|
|
872
907
|
# scope = UsersIndex.aggs(max_age: { max: { field: 'age' } }).search_type(:count)
|
873
908
|
# max_age = scope.aggs['max_age']['value']
|
874
909
|
#
|
875
|
-
def search_type
|
876
|
-
chain {
|
910
|
+
def search_type(value)
|
911
|
+
chain { criteria.update_search_options search_type: value }
|
877
912
|
end
|
878
913
|
|
879
914
|
# Merges two queries.
|
@@ -885,7 +920,7 @@ module Chewy
|
|
885
920
|
#
|
886
921
|
# scope1.merge(scope2) == scope3 # => true
|
887
922
|
#
|
888
|
-
def merge
|
923
|
+
def merge(other)
|
889
924
|
chain { criteria.merge!(other.criteria) }
|
890
925
|
end
|
891
926
|
|
@@ -898,15 +933,15 @@ module Chewy
|
|
898
933
|
#
|
899
934
|
def delete_all
|
900
935
|
if Runtime.version > '2.0'
|
901
|
-
plugins = Chewy.client.nodes.info(plugins: true)[
|
902
|
-
raise PluginMissing,
|
936
|
+
plugins = Chewy.client.nodes.info(plugins: true)['nodes'].values.map { |item| item['plugins'] }.flatten
|
937
|
+
raise PluginMissing, 'install delete-by-query plugin' unless plugins.find { |item| item['name'] == 'delete-by-query' }
|
903
938
|
end
|
904
939
|
request = chain { criteria.update_options simple: true }.send(:_request)
|
905
940
|
ActiveSupport::Notifications.instrument 'delete_query.chewy',
|
906
941
|
request: request, indexes: _indexes, types: _types,
|
907
942
|
index: _indexes.one? ? _indexes.first : _indexes,
|
908
943
|
type: _types.one? ? _types.first : _types do
|
909
|
-
|
944
|
+
Chewy.client.delete_by_query(request)
|
910
945
|
end
|
911
946
|
end
|
912
947
|
|
@@ -924,13 +959,31 @@ module Chewy
|
|
924
959
|
# UsersIndex::User.find([8, 13]) # array of objects with ids in [8, 13]
|
925
960
|
# UsersIndex::User.find([42]) # array of the object with id == 42
|
926
961
|
#
|
927
|
-
def find
|
962
|
+
def find(*ids)
|
928
963
|
results = chain { criteria.update_options simple: true }.filter { _id == ids.flatten }.to_a
|
929
964
|
|
930
|
-
raise Chewy::DocumentNotFound
|
965
|
+
raise Chewy::DocumentNotFound, "Could not find documents for ids #{ids.flatten}" if results.empty?
|
931
966
|
ids.one? && !ids.first.is_a?(Array) ? results.first : results
|
932
967
|
end
|
933
968
|
|
969
|
+
# Returns true if there are at least one document that matches the query
|
970
|
+
#
|
971
|
+
# PlacesIndex.query(...).filter(...).exists?
|
972
|
+
#
|
973
|
+
def exists?
|
974
|
+
search_type(:count).total > 0
|
975
|
+
end
|
976
|
+
|
977
|
+
# Sets limit to be equal to total documents count
|
978
|
+
#
|
979
|
+
# PlacesIndex.query(...).filter(...).unlimited
|
980
|
+
#
|
981
|
+
|
982
|
+
def unlimited
|
983
|
+
count_query = search_type(:count)
|
984
|
+
offset(0).limit { count_query.total }
|
985
|
+
end
|
986
|
+
|
934
987
|
# Returns request total time elapsed as reported by elasticsearch
|
935
988
|
#
|
936
989
|
# UsersIndex.query(...).filter(...).took
|
@@ -955,14 +1008,14 @@ module Chewy
|
|
955
1008
|
|
956
1009
|
protected
|
957
1010
|
|
958
|
-
def initialize_clone
|
1011
|
+
def initialize_clone(other)
|
959
1012
|
@criteria = other.criteria.clone
|
960
1013
|
reset
|
961
1014
|
end
|
962
1015
|
|
963
1016
|
private
|
964
1017
|
|
965
|
-
def chain
|
1018
|
+
def chain(&block)
|
966
1019
|
clone.tap { |q| q.instance_exec(&block) }
|
967
1020
|
end
|
968
1021
|
|
@@ -973,8 +1026,8 @@ module Chewy
|
|
973
1026
|
def _request
|
974
1027
|
@_request ||= begin
|
975
1028
|
request = criteria.request_body
|
976
|
-
request
|
977
|
-
request
|
1029
|
+
request[:index] = _indexes.map(&:index_name)
|
1030
|
+
request[:type] = _types.map(&:type_name)
|
978
1031
|
request
|
979
1032
|
end
|
980
1033
|
end
|
@@ -984,12 +1037,12 @@ module Chewy
|
|
984
1037
|
request: _request, indexes: _indexes, types: _types,
|
985
1038
|
index: _indexes.one? ? _indexes.first : _indexes,
|
986
1039
|
type: _types.one? ? _types.first : _types do
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
1040
|
+
begin
|
1041
|
+
Chewy.client.search(_request)
|
1042
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
|
1043
|
+
raise e if e.message !~ /IndexMissingException/ && e.message !~ /index_not_found_exception/
|
1044
|
+
{}
|
1045
|
+
end
|
993
1046
|
end
|
994
1047
|
end
|
995
1048
|
|
@@ -1000,7 +1053,7 @@ module Chewy
|
|
1000
1053
|
.merge!(_score: hit['_score'])
|
1001
1054
|
.merge!(_explanation: hit['_explanation'])
|
1002
1055
|
|
1003
|
-
wrapper = _derive_index(hit['_index']).
|
1056
|
+
wrapper = _derive_index(hit['_index']).type(hit['_type']).new(attributes)
|
1004
1057
|
wrapper._data = hit
|
1005
1058
|
wrapper
|
1006
1059
|
end
|
@@ -1009,12 +1062,15 @@ module Chewy
|
|
1009
1062
|
def _collection
|
1010
1063
|
@_collection ||= begin
|
1011
1064
|
_load_objects! if criteria.options[:preload]
|
1012
|
-
criteria.options[:preload] && criteria.options[:loaded_objects]
|
1013
|
-
_results.map(&:_object)
|
1065
|
+
if criteria.options[:preload] && criteria.options[:loaded_objects]
|
1066
|
+
_results.map(&:_object)
|
1067
|
+
else
|
1068
|
+
_results
|
1069
|
+
end
|
1014
1070
|
end
|
1015
1071
|
end
|
1016
1072
|
|
1017
|
-
def _derive_index
|
1073
|
+
def _derive_index(index_name)
|
1018
1074
|
(@derive_index ||= {})[index_name] ||= _indexes_hash[index_name] ||
|
1019
1075
|
_indexes_hash[_indexes_hash.keys.sort_by(&:length).reverse.detect { |name| index_name.start_with?(name) }]
|
1020
1076
|
end
|