chewy 5.0.0 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +240 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
  6. data/.github/workflows/ruby.yml +94 -0
  7. data/Appraisals +1 -17
  8. data/CHANGELOG.md +308 -356
  9. data/CODE_OF_CONDUCT.md +14 -0
  10. data/CONTRIBUTING.md +63 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE.txt +1 -1
  13. data/README.md +71 -55
  14. data/chewy.gemspec +5 -5
  15. data/gemfiles/rails.5.2.activerecord.gemfile +4 -3
  16. data/gemfiles/{rails.5.0.activerecord.gemfile → rails.5.2.mongoid.6.4.gemfile} +4 -3
  17. data/gemfiles/{rails.5.0.mongoid.6.1.gemfile → rails.6.0.activerecord.gemfile} +4 -3
  18. data/gemfiles/{rails.5.1.activerecord.gemfile → rails.6.1.activerecord.gemfile} +6 -3
  19. data/lib/chewy.rb +1 -1
  20. data/lib/chewy/backports/duplicable.rb +1 -1
  21. data/lib/chewy/config.rb +2 -20
  22. data/lib/chewy/fields/base.rb +1 -7
  23. data/lib/chewy/fields/root.rb +2 -2
  24. data/lib/chewy/index.rb +2 -0
  25. data/lib/chewy/index/actions.rb +15 -5
  26. data/lib/chewy/index/aliases.rb +16 -5
  27. data/lib/chewy/multi_search.rb +62 -0
  28. data/lib/chewy/railtie.rb +1 -1
  29. data/lib/chewy/search.rb +2 -9
  30. data/lib/chewy/search/loader.rb +1 -1
  31. data/lib/chewy/search/pagination/will_paginate.rb +1 -1
  32. data/lib/chewy/search/parameters.rb +24 -6
  33. data/lib/chewy/search/parameters/allow_partial_search_results.rb +27 -0
  34. data/lib/chewy/search/parameters/indices.rb +123 -0
  35. data/lib/chewy/search/parameters/none.rb +1 -3
  36. data/lib/chewy/search/request.rb +101 -74
  37. data/lib/chewy/search/scrolling.rb +7 -6
  38. data/lib/chewy/stash.rb +1 -1
  39. data/lib/chewy/strategy/active_job.rb +1 -1
  40. data/lib/chewy/strategy/sidekiq.rb +1 -1
  41. data/lib/chewy/type.rb +4 -1
  42. data/lib/chewy/type/adapter/active_record.rb +1 -1
  43. data/lib/chewy/type/adapter/mongoid.rb +1 -1
  44. data/lib/chewy/type/adapter/orm.rb +7 -4
  45. data/lib/chewy/type/adapter/sequel.rb +1 -1
  46. data/lib/chewy/type/import.rb +14 -4
  47. data/lib/chewy/type/import/bulk_request.rb +4 -2
  48. data/lib/chewy/type/import/journal_builder.rb +1 -1
  49. data/lib/chewy/type/import/routine.rb +1 -1
  50. data/lib/chewy/type/mapping.rb +4 -4
  51. data/lib/chewy/type/observe.rb +3 -3
  52. data/lib/chewy/type/syncer.rb +4 -5
  53. data/lib/chewy/type/witchcraft.rb +4 -2
  54. data/lib/chewy/type/wrapper.rb +12 -2
  55. data/lib/chewy/version.rb +1 -1
  56. data/migration_guide.md +56 -0
  57. data/spec/chewy/config_spec.rb +1 -22
  58. data/spec/chewy/fields/base_spec.rb +11 -9
  59. data/spec/chewy/index/actions_spec.rb +120 -33
  60. data/spec/chewy/index/aliases_spec.rb +3 -3
  61. data/spec/chewy/index_spec.rb +16 -39
  62. data/spec/chewy/journal_spec.rb +22 -18
  63. data/spec/chewy/minitest/search_index_receiver_spec.rb +11 -9
  64. data/spec/chewy/multi_search_spec.rb +85 -0
  65. data/spec/chewy/rake_helper_spec.rb +102 -87
  66. data/spec/chewy/rspec/update_index_spec.rb +47 -46
  67. data/spec/chewy/runtime_spec.rb +2 -2
  68. data/spec/chewy/search/parameters/indices_spec.rb +190 -0
  69. data/spec/chewy/search/parameters/none_spec.rb +1 -1
  70. data/spec/chewy/search/parameters_spec.rb +21 -4
  71. data/spec/chewy/search/request_spec.rb +103 -70
  72. data/spec/chewy/search/response_spec.rb +27 -17
  73. data/spec/chewy/search/scrolling_spec.rb +27 -17
  74. data/spec/chewy/search_spec.rb +45 -41
  75. data/spec/chewy/stash_spec.rb +14 -12
  76. data/spec/chewy/strategy/active_job_spec.rb +15 -2
  77. data/spec/chewy/strategy/shoryuken_spec.rb +6 -2
  78. data/spec/chewy/strategy/sidekiq_spec.rb +6 -2
  79. data/spec/chewy/type/adapter/active_record_spec.rb +16 -4
  80. data/spec/chewy/type/import/bulk_builder_spec.rb +9 -94
  81. data/spec/chewy/type/import/journal_builder_spec.rb +9 -7
  82. data/spec/chewy/type/import_spec.rb +9 -0
  83. data/spec/chewy/type/mapping_spec.rb +3 -1
  84. data/spec/chewy/type/observe_spec.rb +4 -4
  85. data/spec/chewy/type/witchcraft_spec.rb +15 -0
  86. data/spec/chewy/type/wrapper_spec.rb +3 -1
  87. data/spec/chewy_spec.rb +0 -7
  88. data/spec/spec_helper.rb +4 -8
  89. data/spec/support/active_record.rb +21 -0
  90. metadata +32 -97
  91. data/.travis.yml +0 -45
  92. data/LEGACY_DSL.md +0 -497
  93. data/gemfiles/rails.4.0.activerecord.gemfile +0 -15
  94. data/gemfiles/rails.4.1.activerecord.gemfile +0 -15
  95. data/gemfiles/rails.4.2.activerecord.gemfile +0 -16
  96. data/gemfiles/rails.4.2.mongoid.5.2.gemfile +0 -16
  97. data/gemfiles/rails.5.1.mongoid.6.3.gemfile +0 -16
  98. data/lib/chewy/query.rb +0 -1137
  99. data/lib/chewy/query/compose.rb +0 -68
  100. data/lib/chewy/query/criteria.rb +0 -191
  101. data/lib/chewy/query/filters.rb +0 -244
  102. data/lib/chewy/query/loading.rb +0 -110
  103. data/lib/chewy/query/nodes/and.rb +0 -25
  104. data/lib/chewy/query/nodes/base.rb +0 -17
  105. data/lib/chewy/query/nodes/bool.rb +0 -34
  106. data/lib/chewy/query/nodes/equal.rb +0 -34
  107. data/lib/chewy/query/nodes/exists.rb +0 -20
  108. data/lib/chewy/query/nodes/expr.rb +0 -28
  109. data/lib/chewy/query/nodes/field.rb +0 -110
  110. data/lib/chewy/query/nodes/has_child.rb +0 -15
  111. data/lib/chewy/query/nodes/has_parent.rb +0 -15
  112. data/lib/chewy/query/nodes/has_relation.rb +0 -59
  113. data/lib/chewy/query/nodes/match_all.rb +0 -11
  114. data/lib/chewy/query/nodes/missing.rb +0 -20
  115. data/lib/chewy/query/nodes/not.rb +0 -25
  116. data/lib/chewy/query/nodes/or.rb +0 -25
  117. data/lib/chewy/query/nodes/prefix.rb +0 -19
  118. data/lib/chewy/query/nodes/query.rb +0 -20
  119. data/lib/chewy/query/nodes/range.rb +0 -63
  120. data/lib/chewy/query/nodes/raw.rb +0 -15
  121. data/lib/chewy/query/nodes/regexp.rb +0 -35
  122. data/lib/chewy/query/nodes/script.rb +0 -20
  123. data/lib/chewy/query/pagination.rb +0 -25
  124. data/spec/chewy/query/criteria_spec.rb +0 -700
  125. data/spec/chewy/query/filters_spec.rb +0 -201
  126. data/spec/chewy/query/loading_spec.rb +0 -124
  127. data/spec/chewy/query/nodes/and_spec.rb +0 -12
  128. data/spec/chewy/query/nodes/bool_spec.rb +0 -14
  129. data/spec/chewy/query/nodes/equal_spec.rb +0 -32
  130. data/spec/chewy/query/nodes/exists_spec.rb +0 -18
  131. data/spec/chewy/query/nodes/has_child_spec.rb +0 -59
  132. data/spec/chewy/query/nodes/has_parent_spec.rb +0 -59
  133. data/spec/chewy/query/nodes/match_all_spec.rb +0 -11
  134. data/spec/chewy/query/nodes/missing_spec.rb +0 -16
  135. data/spec/chewy/query/nodes/not_spec.rb +0 -14
  136. data/spec/chewy/query/nodes/or_spec.rb +0 -12
  137. data/spec/chewy/query/nodes/prefix_spec.rb +0 -16
  138. data/spec/chewy/query/nodes/query_spec.rb +0 -12
  139. data/spec/chewy/query/nodes/range_spec.rb +0 -32
  140. data/spec/chewy/query/nodes/raw_spec.rb +0 -11
  141. data/spec/chewy/query/nodes/regexp_spec.rb +0 -43
  142. data/spec/chewy/query/nodes/script_spec.rb +0 -15
  143. data/spec/chewy/query/pagination/kaminari_spec.rb +0 -5
  144. data/spec/chewy/query/pagination/will_paginate_spec.rb +0 -5
  145. data/spec/chewy/query/pagination_spec.rb +0 -39
  146. data/spec/chewy/query_spec.rb +0 -637
  147. data/spec/chewy/search/parameters/indices_boost_spec.rb +0 -83
@@ -22,7 +22,7 @@ module Chewy
22
22
  end
23
23
 
24
24
  def import_scope(scope, options)
25
- pluck_in_batches(scope, options.slice(:batch_size)).inject(true) do |result, ids|
25
+ pluck_in_batches(scope, **options.slice(:batch_size)).inject(true) do |result, ids|
26
26
  result & yield(grouped_objects(default_scope_where_ids_in(ids).all))
27
27
  end
28
28
  end
@@ -10,7 +10,7 @@ module Chewy
10
10
 
11
11
  IMPORT_WORKER = lambda do |type, options, total, ids, index|
12
12
  ::Process.setproctitle("chewy [#{type}]: import data (#{index + 1}/#{total})")
13
- routine = Routine.new(type, options)
13
+ routine = Routine.new(type, **options)
14
14
  type.adapter.import(*ids, routine.options) do |action_objects|
15
15
  routine.process(**action_objects)
16
16
  end
@@ -19,7 +19,7 @@ module Chewy
19
19
 
20
20
  LEFTOVERS_WORKER = lambda do |type, options, total, body, index|
21
21
  ::Process.setproctitle("chewy [#{type}]: import leftovers (#{index + 1}/#{total})")
22
- routine = Routine.new(type, options)
22
+ routine = Routine.new(type, **options)
23
23
  routine.perform_bulk(body)
24
24
  routine.errors
25
25
  end
@@ -67,6 +67,7 @@ module Chewy
67
67
  # @option options [String] suffix an index name suffix, used for zero-downtime reset mostly, no suffix by default
68
68
  # @option options [Integer] bulk_size bulk API chunk size in bytes; if passed, the request is performed several times for each chunk, empty by default
69
69
  # @option options [Integer] batch_size passed to the adapter import method, used to split imported objects in chunks, 1000 by default
70
+ # @option options [Boolean] direct_import skips object reloading in ORM adapter, `false` by default
70
71
  # @option options [true, false] journal enables imported objects journaling, false by default
71
72
  # @option options [Array<Symbol, String>] update_fields list of fields for the partial import, empty by default
72
73
  # @option options [true, false] update_failover enables full objects reimport in cases of partial update errors, `true` by default
@@ -126,8 +127,9 @@ module Chewy
126
127
  private
127
128
 
128
129
  def import_routine(*args)
129
- return if args.first.blank? && !args.first.nil?
130
- routine = Routine.new(self, args.extract_options!)
130
+ return if !args.first.nil? && empty_objects_or_scope?(args.first)
131
+
132
+ routine = Routine.new(self, **args.extract_options!)
131
133
  routine.create_indexes!
132
134
 
133
135
  if routine.parallel_options
@@ -137,6 +139,14 @@ module Chewy
137
139
  end
138
140
  end
139
141
 
142
+ def empty_objects_or_scope?(objects_or_scope)
143
+ if objects_or_scope.respond_to?(:empty?)
144
+ objects_or_scope.empty?
145
+ else
146
+ objects_or_scope.blank?
147
+ end
148
+ end
149
+
140
150
  def import_linear(objects, routine)
141
151
  ActiveSupport::Notifications.instrument 'import_objects.chewy', type: self do |payload|
142
152
  adapter.import(*objects, routine.options) do |action_objects|
@@ -54,15 +54,17 @@ module Chewy
54
54
 
55
55
  def request_bodies(body)
56
56
  if @bulk_size
57
+ serializer = ::Elasticsearch::API.serializer
57
58
  pieces = body.each_with_object(['']) do |piece, result|
58
59
  operation, meta = piece.to_a.first
59
60
  data = meta.delete(:data)
60
- piece = [{operation => meta}, data].compact.map(&:to_json).join("\n")
61
+ piece = serializer.dump(operation => meta)
62
+ piece << "\n" << serializer.dump(data) if data.present?
61
63
 
62
64
  if result.last.bytesize + piece.bytesize > @bulk_size
63
65
  result.push(piece)
64
66
  else
65
- result[-1] = [result[-1], piece].reject(&:blank?).join("\n")
67
+ result[-1].blank? ? (result[-1] = piece) : (result[-1] << "\n" << piece)
66
68
  end
67
69
  end
68
70
  pieces.each { |piece| piece << "\n" }
@@ -31,7 +31,7 @@ module Chewy
31
31
  index_name: @type.index.derivable_name,
32
32
  type_name: @type.type_name,
33
33
  action: action,
34
- references: identify(objects).map(&:to_json).map(&Base64.method(:encode64)),
34
+ references: identify(objects).map { |item| Base64.encode64(::Elasticsearch::API.serializer.dump(item)) },
35
35
  created_at: Time.now.utc
36
36
  }
37
37
  end
@@ -66,7 +66,7 @@ module Chewy
66
66
  def create_indexes!
67
67
  Chewy::Stash::Journal.create if @options[:journal]
68
68
  return if Chewy.configuration[:skip_index_creation_on_import]
69
- @type.index.create!(@bulk_options.slice(:suffix)) unless @type.index.exists?
69
+ @type.index.create!(**@bulk_options.slice(:suffix)) unless @type.index.exists?
70
70
  end
71
71
 
72
72
  # The main process method. Converts passed objects to thr bulk request body,
@@ -35,8 +35,8 @@ module Chewy
35
35
  # end
36
36
  #
37
37
  def root(**options)
38
- self.root_object ||= Chewy::Fields::Root.new(type_name, Chewy.default_root_options.merge(options))
39
- root_object.update_options!(options)
38
+ self.root_object ||= Chewy::Fields::Root.new(type_name, **Chewy.default_root_options.merge(options))
39
+ root_object.update_options!(**options)
40
40
  yield if block_given?
41
41
  root_object
42
42
  end
@@ -126,9 +126,9 @@ module Chewy
126
126
  #
127
127
  def field(*args, **options, &block)
128
128
  if args.size > 1
129
- args.map { |name| field(name, options) }
129
+ args.map { |name| field(name, **options) }
130
130
  else
131
- expand_nested(Chewy::Fields::Base.new(args.first, options), &block)
131
+ expand_nested(Chewy::Fields::Base.new(args.first, **options), &block)
132
132
  end
133
133
  end
134
134
 
@@ -63,10 +63,10 @@ module Chewy
63
63
  update_proc = Observe.update_proc(type_name, *args, &block)
64
64
 
65
65
  if Chewy.use_after_commit_callbacks
66
- after_commit(callback_options, &update_proc)
66
+ after_commit(**callback_options, &update_proc)
67
67
  else
68
- after_save(callback_options, &update_proc)
69
- after_destroy(callback_options, &update_proc)
68
+ after_save(**callback_options, &update_proc)
69
+ after_destroy(**callback_options, &update_proc)
70
70
  end
71
71
  end
72
72
  end
@@ -2,7 +2,7 @@ module Chewy
2
2
  class Type
3
3
  # This class is able to find missing and outdated documents in the ES
4
4
  # comparing ids from the data source and the ES index. Also, if `outdated_sync_field`
5
- # existss in the index definition, it performs comparison of this field
5
+ # exists in the index definition, it performs comparison of this field
6
6
  # values for each source object and corresponding ES document. Usually,
7
7
  # this field is `updated_at` and if its value in the source is not equal
8
8
  # to the value in the index - this means that this document outdated and
@@ -205,10 +205,9 @@ module Chewy
205
205
  return @outdated_sync_field_type if instance_variable_defined?(:@outdated_sync_field_type)
206
206
  return unless @type.outdated_sync_field
207
207
 
208
- mappings = @type.client.indices.get_mapping(
209
- index: @type.index_name,
210
- type: @type.type_name
211
- ).values.first.fetch('mappings', {})
208
+ args = {index: @type.index_name, type: @type.type_name}
209
+ args[:include_type_name] = true if Runtime.version >= '6.7.0'
210
+ mappings = @type.client.indices.get_mapping(**args).values.first.fetch('mappings', {})
212
211
 
213
212
  @outdated_sync_field_type = mappings
214
213
  .fetch(@type.type_name, {})
@@ -58,7 +58,7 @@ module Chewy
58
58
  private
59
59
 
60
60
  def alicorn
61
- @alicorn ||= class_eval <<-RUBY, __FILE__, __LINE__ + 1
61
+ @alicorn ||= singleton_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
62
62
  -> (locals, object0, crutches) do
63
63
  #{composed_values(@type.root, 0)}
64
64
  end
@@ -79,7 +79,9 @@ module Chewy
79
79
  if field.children.present? && !field.multi_field?
80
80
  <<-RUBY
81
81
  (result#{nesting} = #{fetcher}
82
- if result#{nesting}.respond_to?(:to_ary)
82
+ if result#{nesting}.nil?
83
+ nil
84
+ elsif result#{nesting}.respond_to?(:to_ary)
83
85
  result#{nesting}.map do |object#{nesting}|
84
86
  #{composed_values(field, nesting)}
85
87
  end
@@ -41,7 +41,7 @@ module Chewy
41
41
 
42
42
  %w[_id _type _index].each do |name|
43
43
  define_method name do
44
- data[name]
44
+ _data[name]
45
45
  end
46
46
  end
47
47
 
@@ -49,6 +49,8 @@ module Chewy
49
49
  m = method.to_s
50
50
  if (name = highlight_name(m))
51
51
  highlight(name)
52
+ elsif (name = highlight_names(m))
53
+ highlights(name)
52
54
  elsif @attributes.key?(m)
53
55
  @attributes[m]
54
56
  elsif attribute_defined?(m)
@@ -60,7 +62,7 @@ module Chewy
60
62
 
61
63
  def respond_to_missing?(method, include_private = false)
62
64
  m = method.to_s
63
- highlight_name(m) || @attributes.key?(m) || attribute_defined?(m) || super
65
+ highlight_name(m) || highlight_names(m) || @attributes.key?(m) || attribute_defined?(m) || super
64
66
  end
65
67
 
66
68
  private
@@ -69,6 +71,10 @@ module Chewy
69
71
  method.sub(/_highlight\z/, '') if method.end_with?('_highlight')
70
72
  end
71
73
 
74
+ def highlight_names(method)
75
+ method.sub(/_highlights\z/, '') if method.end_with?('_highlights')
76
+ end
77
+
72
78
  def attribute_defined?(attribute)
73
79
  self.class.root && self.class.root.children.find { |a| a.name.to_s == attribute }.present?
74
80
  end
@@ -77,6 +83,10 @@ module Chewy
77
83
  _data['highlight'][attribute].first if highlight?(attribute)
78
84
  end
79
85
 
86
+ def highlights(attribute)
87
+ _data['highlight'][attribute] if highlight?(attribute)
88
+ end
89
+
80
90
  def highlight?(attribute)
81
91
  _data.key?('highlight') && _data['highlight'].key?(attribute)
82
92
  end
data/lib/chewy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '5.0.0'.freeze
2
+ VERSION = '7.0.1'.freeze
3
3
  end
@@ -0,0 +1,56 @@
1
+ # Migration guide
2
+
3
+ This document outlines the steps you need to take when migrating between major versions of
4
+ Chewy and Elasticsearch. For simplicity's sake the guide will assume that you're using
5
+ Chewy alongside a matching Elasticsearch version.
6
+
7
+ ## Chewy 6/Elasticsearch 6 to Chewy 7/Elasticsearch 7
8
+
9
+ In order to upgrade Chewy 6/Elasticsearch 6 to Chewy 7/Elasticsearch 7 in the most seamless manner you have to:
10
+
11
+ * Upgrade to the latest 6.x stable releases, namely Chewy 6.0, Elasticsearch 6.8
12
+ * Study carefully [Breaking changes in 7.0](https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.htmll), make sure your application conforms.
13
+ * Run your test suite on Chewy 7.0 / Elasticsearch 7
14
+ * Run manual tests on Chewy 7.0 / Elasticsearch 7
15
+ * Upgrade to Chewy 7.0
16
+ * The “total hits” counter is an integer for ES versions < 7 and an object (hash) for the versions starting from 7.0.0. Elasticsearch added a special option, `rest_total_hits_as_int`, to ease the upgrade, that could be appended to any request and results in the old “total hits” format. Unfortunately, this option is not recognized by ES versions prior to 7.0.0, which means that we have to check the version to decide if we need this option.
17
+ Normally Chewy does memoization of the current ES version, but this might be inappropriate for the upgrade, as the version changes live.
18
+ To handle that we have 2 versions of Chewy for this stage of the upgrade: 7.0.0 and 7.0.1. Version 7.0.0 does the memoization and version 7.0.1 requests the current version on every search request.
19
+ * You can use the 7.0.0 version if it's fine for you to have an application restart immediately after ES cluster upgrade.
20
+ * If you're using the 7.0.1 version you might be interested in keeping the timeframe between this step and the last one as small as possible, as version 7.0.1 skips ES version memoization for search requests to help dynamically detect ES version. This leads to an extra version request on each search request, i.e. could affect the overall performance/latency of the search and a load of ES cluster.
21
+ * Perform a [rolling upgrade](https://www.elastic.co/guide/en/elasticsearch/reference//rolling-upgrades.html) of Elasticsearch
22
+ * Run your test suite on Chewy 7.1 / Elasticsearch 7
23
+ * Run manual tests on Chewy 7.1 / Elasticsearch 7
24
+ * Upgrade to Chewy 7.1
25
+ * Upgrade to Chewy 7.2:
26
+ * Remove all the the `Chewy::Type` class usages, e.g. remove `CitiesIndex::City` / `CitiesIndex.city`
27
+ * `CitiesIndex::City.import! ...` becomes `CitiesIndex.import! ...`
28
+ * Update indexes with simplified DSL:
29
+ * `define_type` block -> `index_scope` clause
30
+ * it can be omitted completely, if you don't need to specify the scope or options, e.g. `name`
31
+ * Remove type names from string representations:
32
+ * in `update_index` ActiveRecord helper and RSpec matcher, e.g.
33
+ * `update_index('cities#city')` -> `update_index('cities')`
34
+ * `update_index(UsersIndex::User)` -> `update_index(UsersIndex)`
35
+ * in rake tasks (e.g. `rake chewy:update[cities#city]` -> `rake chewy:update[cities]`)
36
+ * rake tasks output is also changed (e.g. `Imported CitiesIndex::City in 1s, stats: index 3` -> `Imported CitiesIndex in 1s, stats: index 3`)
37
+ * Use index name instead of type name in loader additional scope
38
+ * e.g. `CitiesIndex.filter(...).load(city: {scope: City.where(...)})` -> `CitiesIndex.filter(...).load(cities: {scope: City.where(...)})`
39
+
40
+ ## Chewy 5/Elasticsearch 5 to Chewy 6/Elasticsearch 6
41
+
42
+ In order to upgrade Chewy 5/Elasticsearch 5 to Chewy 6/Elasticsearch 6 in the most seamless manner you have to:
43
+
44
+ * Upgrade to the latest 5.x stable releases, namely Chewy 5.2, Elasticsearch 5.6
45
+ * [Migrate any multi-typed indexes into single-typed](https://www.elastic.co/guide/en/elasticsearch/reference/6.8/removal-of-types.html)
46
+ * Using [multi-index queries](https://github.com/toptal/chewy/pull/657) could be helpful
47
+ * Parent/Child [relationship is deprecated](https://www.elastic.co/guide/en/elasticsearch/reference/6.8/removal-of-types.html#parent-child-mapping-types) in favor of the [join field](https://www.elastic.co/guide/en/elasticsearch/reference/6.8/parent-join.html)
48
+ * Handle deprecation of `string` type & `not_analyzed` value for the `index` mapping parameter:
49
+ * replace fields with `{ type: 'string', index: 'not_analyzed'}` by `{type: 'keyword'}`
50
+ * replace fields with `{ type: 'string', index: 'analyzed'}` by `{type: 'text'}`
51
+ * `PathHierarchy` tokenizer' param `delimiter` now accepts only one argument, [others should be replaced by character filter ](https://discuss.elastic.co/t/multichar-delimiter-in-path-hierarchy-tokenizer/16203)
52
+ * Make sure you don't use any other of the [deprecated Elasticsearch 5 features](https://www.elastic.co/guide/en/elasticsearch/reference/6.8/breaking-changes-6.0.html)
53
+ * Run your test suite on Chewy 6 / Elasticsearch 6
54
+ * Run manual tests on Chewy 6 / Elasticsearch 6
55
+ * Upgrade to Chewy 6
56
+ * Perform a [rolling upgrade](https://www.elastic.co/guide/en/elasticsearch/reference/6.8/rolling-upgrades.html) of Elasticsearch
@@ -6,9 +6,6 @@ describe Chewy::Config do
6
6
  its(:logger) { should be_nil }
7
7
  its(:transport_logger) { should be_nil }
8
8
  its(:transport_logger) { should be_nil }
9
- its(:query_mode) { should == :must }
10
- its(:filter_mode) { should == :and }
11
- its(:post_filter_mode) { should be_nil }
12
9
  its(:root_strategy) { should == :base }
13
10
  its(:request_strategy) { should == :atomic }
14
11
  its(:use_after_commit_callbacks) { should == true }
@@ -54,24 +51,6 @@ describe Chewy::Config do
54
51
  end
55
52
  end
56
53
 
57
- describe '#search_class=' do
58
- specify do
59
- expect { subject.search_class = Chewy::Query }
60
- .to change { subject.search_class }
61
- .from(be < Chewy::Search::Request)
62
- .to(be < Chewy::Query)
63
- end
64
-
65
- context do
66
- before { hide_const('Kaminari') }
67
-
68
- specify do
69
- expect(subject.search_class.included_modules)
70
- .to include(Chewy::Search::Pagination::WillPaginate)
71
- end
72
- end
73
- end
74
-
75
54
  describe '#search_class' do
76
55
  context 'nothing is defined' do
77
56
  before do
@@ -141,7 +120,7 @@ describe Chewy::Config do
141
120
  context 'when Rails::VERSION constant is defined' do
142
121
  it 'looks for configuration in "config/chewy.yml"' do
143
122
  module Rails
144
- VERSION = '5.1.0'.freeze
123
+ VERSION = '5.1.1'.freeze
145
124
 
146
125
  def self.root
147
126
  Pathname.new(__dir__)
@@ -180,43 +180,45 @@ describe Chewy::Fields::Base do
180
180
  end
181
181
  end
182
182
 
183
+ # rubocop:disable Style/BracesAroundHashParameters
183
184
  specify do
184
- expect(EventsIndex::Event.root.compose(
185
- id: 1, category: {id: 2, licenses: {id: 3, name: 'Name'}}
186
- )).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
185
+ expect(EventsIndex::Event.root.compose({
186
+ id: 1, category: {id: 2, licenses: {id: 3, name: 'Name'}}
187
+ })).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
187
188
  end
188
189
 
189
190
  specify do
190
- expect(EventsIndex::Event.root.compose(id: 1, category: [
191
+ expect(EventsIndex::Event.root.compose({id: 1, category: [
191
192
  {id: 2, 'licenses' => {id: 3, name: 'Name1'}},
192
193
  {id: 4, licenses: nil}
193
- ])).to eq('id' => 1, 'category' => [
194
+ ]})).to eq('id' => 1, 'category' => [
194
195
  {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name1'}},
195
196
  {'id' => 4, 'licenses' => nil.as_json}
196
197
  ])
197
198
  end
198
199
 
199
200
  specify do
200
- expect(EventsIndex::Event.root.compose('id' => 1, category: {id: 2, licenses: [
201
+ expect(EventsIndex::Event.root.compose({'id' => 1, category: {id: 2, licenses: [
201
202
  {id: 3, name: 'Name1'}, {id: 4, name: 'Name2'}
202
- ]})).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => [
203
+ ]}})).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => [
203
204
  {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
204
205
  ]})
205
206
  end
206
207
 
207
208
  specify do
208
- expect(EventsIndex::Event.root.compose(id: 1, category: [
209
+ expect(EventsIndex::Event.root.compose({id: 1, category: [
209
210
  {id: 2, licenses: [
210
211
  {id: 3, 'name' => 'Name1'}, {id: 4, name: 'Name2'}
211
212
  ]},
212
213
  {id: 5, licenses: []}
213
- ])).to eq('id' => 1, 'category' => [
214
+ ]})).to eq('id' => 1, 'category' => [
214
215
  {'id' => 2, 'licenses' => [
215
216
  {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
216
217
  ]},
217
218
  {'id' => 5, 'licenses' => []}
218
219
  ])
219
220
  end
221
+ # rubocop:enable Style/BracesAroundHashParameters
220
222
 
221
223
  specify do
222
224
  expect(EventsIndex::Event.root.compose(
@@ -3,7 +3,10 @@ require 'spec_helper'
3
3
  describe Chewy::Index::Actions do
4
4
  before { Chewy.massacre }
5
5
 
6
- before { stub_index :dummies }
6
+ before do
7
+ stub_index :dummies
8
+ stub_index :dummies_suffixed
9
+ end
7
10
 
8
11
  describe '.exists?' do
9
12
  specify { expect(DummiesIndex.exists?).to eq(false) }
@@ -25,26 +28,40 @@ describe Chewy::Index::Actions do
25
28
  end
26
29
 
27
30
  context do
28
- before { DummiesIndex.create '2013' }
31
+ before do
32
+ DummiesIndex.create '2013'
33
+ DummiesSuffixedIndex.create 'should_not_appear'
34
+ end
35
+
29
36
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(true) }
30
37
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(true) }
31
- specify { expect(DummiesIndex.aliases).to eq([]) }
38
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
32
39
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
33
40
  specify { expect(DummiesIndex.create('2013')).to eq(false) }
34
41
  specify { expect(DummiesIndex.create('2014')['acknowledged']).to eq(true) }
35
42
 
36
43
  context do
37
44
  before { DummiesIndex.create '2014' }
45
+
38
46
  specify { expect(DummiesIndex.indexes).to match_array(%w[dummies_2013 dummies_2014]) }
39
47
  end
40
48
  end
41
49
 
42
50
  context do
43
- before { DummiesIndex.create '2013', alias: false }
51
+ before do
52
+ DummiesIndex.create '2013', alias: false
53
+ DummiesSuffixedIndex.create 'should_not_appear'
54
+ end
55
+
44
56
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
45
57
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(true) }
46
58
  specify { expect(DummiesIndex.aliases).to eq([]) }
47
59
  specify { expect(DummiesIndex.indexes).to eq([]) }
60
+ specify { expect(DummiesIndex.exists?).to eq(false) }
61
+ # Unfortunately, without alias we can't figure out that this dummies_2013 index is related to DummiesIndex
62
+ # it would be awesome to have the following specs passing
63
+ # specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
64
+ # specify { expect(DummiesIndex.exists?).to eq(true) }
48
65
  end
49
66
  end
50
67
 
@@ -53,36 +70,54 @@ describe Chewy::Index::Actions do
53
70
  specify { expect(DummiesIndex.create!('2013')['acknowledged']).to eq(true) }
54
71
 
55
72
  context do
56
- before { DummiesIndex.create }
73
+ before do
74
+ DummiesIndex.create
75
+ DummiesSuffixedIndex.create 'should_not_appear'
76
+ end
77
+
57
78
  specify do
58
- expect { DummiesIndex.create! }.to raise_error(Elasticsearch::Transport::Transport::Errors::BadRequest).with_message(/index_already_exists_exception.*dummies/)
79
+ expect { DummiesIndex.create! }.to raise_error(Elasticsearch::Transport::Transport::Errors::BadRequest).with_message(/already exists.*dummies/)
59
80
  end
60
81
  specify { expect { DummiesIndex.create!('2013') }.to raise_error(Elasticsearch::Transport::Transport::Errors::BadRequest).with_message(/Invalid alias name \[dummies\]/) }
61
82
  end
62
83
 
63
84
  context do
64
- before { DummiesIndex.create! '2013' }
85
+ before do
86
+ DummiesIndex.create! '2013'
87
+ DummiesSuffixedIndex.create! 'should_not_appear'
88
+ end
89
+
65
90
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(true) }
66
91
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(true) }
67
- specify { expect(DummiesIndex.aliases).to eq([]) }
92
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
68
93
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
69
94
  specify do
70
- expect { DummiesIndex.create!('2013') }.to raise_error(Elasticsearch::Transport::Transport::Errors::BadRequest).with_message(/index_already_exists_exception.*dummies_2013/)
95
+ expect { DummiesIndex.create!('2013') }.to raise_error(Elasticsearch::Transport::Transport::Errors::BadRequest).with_message(/already exists.*dummies_2013/)
71
96
  end
72
97
  specify { expect(DummiesIndex.create!('2014')['acknowledged']).to eq(true) }
73
98
 
74
99
  context do
75
100
  before { DummiesIndex.create! '2014' }
101
+
76
102
  specify { expect(DummiesIndex.indexes).to match_array(%w[dummies_2013 dummies_2014]) }
77
103
  end
78
104
  end
79
105
 
80
106
  context do
81
- before { DummiesIndex.create! '2013', alias: false }
107
+ before do
108
+ DummiesIndex.create! '2013', alias: false
109
+ DummiesSuffixedIndex.create! 'should_not_appear'
110
+ end
111
+
82
112
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
83
113
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(true) }
84
114
  specify { expect(DummiesIndex.aliases).to eq([]) }
85
115
  specify { expect(DummiesIndex.indexes).to eq([]) }
116
+ specify { expect(DummiesIndex.exists?).to eq(false) }
117
+ # Unfortunately, without alias we can't figure out that this dummies_2013 index is related to DummiesIndex
118
+ # it would be awesome to have the following specs passing
119
+ # specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
120
+ # specify { expect(DummiesIndex.exists?).to eq(true) }
86
121
  end
87
122
  end
88
123
 
@@ -91,27 +126,40 @@ describe Chewy::Index::Actions do
91
126
  specify { expect(DummiesIndex.delete('dummies_2013')).to eq(false) }
92
127
 
93
128
  context do
94
- before { DummiesIndex.create }
129
+ before do
130
+ DummiesIndex.create
131
+ DummiesSuffixedIndex.create 'should_not_appear'
132
+ end
133
+
95
134
  specify { expect(DummiesIndex.delete['acknowledged']).to eq(true) }
96
135
 
97
136
  context do
98
137
  before { DummiesIndex.delete }
99
138
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
139
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
100
140
  end
101
141
  end
102
142
 
103
143
  context do
104
- before { DummiesIndex.create '2013' }
144
+ before do
145
+ DummiesIndex.create '2013'
146
+ DummiesSuffixedIndex.create 'should_not_appear'
147
+ end
148
+
105
149
  specify { expect(DummiesIndex.delete('2013')['acknowledged']).to eq(true) }
106
150
 
107
151
  context do
108
152
  before { DummiesIndex.delete('2013') }
109
153
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
110
154
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(false) }
155
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
111
156
  end
112
157
 
113
158
  context do
114
- before { DummiesIndex.create '2014' }
159
+ before do
160
+ DummiesIndex.create '2014'
161
+ end
162
+
115
163
  specify { expect(DummiesIndex.delete['acknowledged']).to eq(true) }
116
164
 
117
165
  context do
@@ -119,6 +167,7 @@ describe Chewy::Index::Actions do
119
167
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
120
168
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(false) }
121
169
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2014')).to eq(false) }
170
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
122
171
  end
123
172
 
124
173
  context do
@@ -126,6 +175,7 @@ describe Chewy::Index::Actions do
126
175
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(true) }
127
176
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(true) }
128
177
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2014')).to eq(false) }
178
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
129
179
  end
130
180
  end
131
181
  end
@@ -136,27 +186,38 @@ describe Chewy::Index::Actions do
136
186
  specify { expect { DummiesIndex.delete!('2013') }.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound) }
137
187
 
138
188
  context do
139
- before { DummiesIndex.create }
189
+ before do
190
+ DummiesIndex.create
191
+ DummiesSuffixedIndex.create 'should_not_appear'
192
+ end
193
+
140
194
  specify { expect(DummiesIndex.delete!['acknowledged']).to eq(true) }
141
195
 
142
196
  context do
143
197
  before { DummiesIndex.delete! }
144
198
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
199
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
145
200
  end
146
201
  end
147
202
 
148
203
  context do
149
- before { DummiesIndex.create '2013' }
204
+ before do
205
+ DummiesIndex.create '2013'
206
+ DummiesSuffixedIndex.create 'should_not_appear'
207
+ end
208
+
150
209
  specify { expect(DummiesIndex.delete!('2013')['acknowledged']).to eq(true) }
151
210
 
152
211
  context do
153
212
  before { DummiesIndex.delete!('2013') }
154
213
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
155
214
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(false) }
215
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
156
216
  end
157
217
 
158
218
  context do
159
219
  before { DummiesIndex.create '2014' }
220
+
160
221
  specify { expect(DummiesIndex.delete!['acknowledged']).to eq(true) }
161
222
 
162
223
  context do
@@ -164,6 +225,7 @@ describe Chewy::Index::Actions do
164
225
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(false) }
165
226
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(false) }
166
227
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2014')).to eq(false) }
228
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
167
229
  end
168
230
 
169
231
  context do
@@ -171,6 +233,7 @@ describe Chewy::Index::Actions do
171
233
  specify { expect(Chewy.client.indices.exists(index: 'dummies')).to eq(true) }
172
234
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2013')).to eq(true) }
173
235
  specify { expect(Chewy.client.indices.exists(index: 'dummies_2014')).to eq(false) }
236
+ specify { expect(Chewy.client.indices.exists(index: 'dummies_suffixed')).to eq(true) }
174
237
  end
175
238
  end
176
239
  end
@@ -184,19 +247,19 @@ describe Chewy::Index::Actions do
184
247
  before { DummiesIndex.purge }
185
248
  specify { expect(DummiesIndex).to be_exists }
186
249
  specify { expect(DummiesIndex.aliases).to eq([]) }
187
- specify { expect(DummiesIndex.indexes).to eq([]) }
250
+ specify { expect(DummiesIndex.indexes).to eq(['dummies']) }
188
251
 
189
252
  context do
190
253
  before { DummiesIndex.purge }
191
254
  specify { expect(DummiesIndex).to be_exists }
192
255
  specify { expect(DummiesIndex.aliases).to eq([]) }
193
- specify { expect(DummiesIndex.indexes).to eq([]) }
256
+ specify { expect(DummiesIndex.indexes).to eq(['dummies']) }
194
257
  end
195
258
 
196
259
  context do
197
260
  before { DummiesIndex.purge('2013') }
198
261
  specify { expect(DummiesIndex).to be_exists }
199
- specify { expect(DummiesIndex.aliases).to eq([]) }
262
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
200
263
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
201
264
  end
202
265
  end
@@ -204,20 +267,20 @@ describe Chewy::Index::Actions do
204
267
  context do
205
268
  before { DummiesIndex.purge('2013') }
206
269
  specify { expect(DummiesIndex).to be_exists }
207
- specify { expect(DummiesIndex.aliases).to eq([]) }
270
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
208
271
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
209
272
 
210
273
  context do
211
274
  before { DummiesIndex.purge }
212
275
  specify { expect(DummiesIndex).to be_exists }
213
276
  specify { expect(DummiesIndex.aliases).to eq([]) }
214
- specify { expect(DummiesIndex.indexes).to eq([]) }
277
+ specify { expect(DummiesIndex.indexes).to eq(['dummies']) }
215
278
  end
216
279
 
217
280
  context do
218
281
  before { DummiesIndex.purge('2014') }
219
282
  specify { expect(DummiesIndex).to be_exists }
220
- specify { expect(DummiesIndex.aliases).to eq([]) }
283
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
221
284
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2014']) }
222
285
  end
223
286
  end
@@ -231,19 +294,19 @@ describe Chewy::Index::Actions do
231
294
  before { DummiesIndex.purge! }
232
295
  specify { expect(DummiesIndex).to be_exists }
233
296
  specify { expect(DummiesIndex.aliases).to eq([]) }
234
- specify { expect(DummiesIndex.indexes).to eq([]) }
297
+ specify { expect(DummiesIndex.indexes).to eq(['dummies']) }
235
298
 
236
299
  context do
237
300
  before { DummiesIndex.purge! }
238
301
  specify { expect(DummiesIndex).to be_exists }
239
302
  specify { expect(DummiesIndex.aliases).to eq([]) }
240
- specify { expect(DummiesIndex.indexes).to eq([]) }
303
+ specify { expect(DummiesIndex.indexes).to eq(['dummies']) }
241
304
  end
242
305
 
243
306
  context do
244
307
  before { DummiesIndex.purge!('2013') }
245
308
  specify { expect(DummiesIndex).to be_exists }
246
- specify { expect(DummiesIndex.aliases).to eq([]) }
309
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
247
310
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
248
311
  end
249
312
  end
@@ -251,20 +314,20 @@ describe Chewy::Index::Actions do
251
314
  context do
252
315
  before { DummiesIndex.purge!('2013') }
253
316
  specify { expect(DummiesIndex).to be_exists }
254
- specify { expect(DummiesIndex.aliases).to eq([]) }
317
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
255
318
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2013']) }
256
319
 
257
320
  context do
258
321
  before { DummiesIndex.purge! }
259
322
  specify { expect(DummiesIndex).to be_exists }
260
323
  specify { expect(DummiesIndex.aliases).to eq([]) }
261
- specify { expect(DummiesIndex.indexes).to eq([]) }
324
+ specify { expect(DummiesIndex.indexes).to eq(['dummies']) }
262
325
  end
263
326
 
264
327
  context do
265
328
  before { DummiesIndex.purge!('2014') }
266
329
  specify { expect(DummiesIndex).to be_exists }
267
- specify { expect(DummiesIndex.aliases).to eq([]) }
330
+ specify { expect(DummiesIndex.aliases).to eq(['dummies']) }
268
331
  specify { expect(DummiesIndex.indexes).to eq(['dummies_2014']) }
269
332
  end
270
333
  end
@@ -281,6 +344,18 @@ describe Chewy::Index::Actions do
281
344
 
282
345
  specify { expect(CitiesIndex.import).to eq(true) }
283
346
 
347
+ specify 'with an empty array' do
348
+ expect(CitiesIndex).not_to receive(:exists?)
349
+ expect(CitiesIndex).not_to receive(:create!)
350
+ expect(CitiesIndex.import([])).to eq(true)
351
+ end
352
+
353
+ specify 'with an empty relation' do
354
+ expect(CitiesIndex).not_to receive(:exists?)
355
+ expect(CitiesIndex).not_to receive(:create!)
356
+ expect(CitiesIndex.import(City.where('1 = 2'))).to eq(true)
357
+ end
358
+
284
359
  context do
285
360
  before do
286
361
  stub_index(:cities) do
@@ -305,6 +380,18 @@ describe Chewy::Index::Actions do
305
380
 
306
381
  specify { expect(CitiesIndex.import!).to eq(true) }
307
382
 
383
+ specify 'with an empty array' do
384
+ expect(CitiesIndex).not_to receive(:exists?)
385
+ expect(CitiesIndex).not_to receive(:create!)
386
+ expect(CitiesIndex.import!([])).to eq(true)
387
+ end
388
+
389
+ specify 'with an empty relation' do
390
+ expect(CitiesIndex).not_to receive(:exists?)
391
+ expect(CitiesIndex).not_to receive(:create!)
392
+ expect(CitiesIndex.import!(City.where('1 = 2'))).to eq(true)
393
+ end
394
+
308
395
  context do
309
396
  before do
310
397
  stub_index(:cities) do
@@ -337,13 +424,13 @@ describe Chewy::Index::Actions do
337
424
 
338
425
  specify { expect(CitiesIndex.all).to have(1).item }
339
426
  specify { expect(CitiesIndex.aliases).to eq([]) }
340
- specify { expect(CitiesIndex.indexes).to eq([]) }
427
+ specify { expect(CitiesIndex.indexes).to eq(['cities']) }
341
428
 
342
429
  context do
343
430
  before { CitiesIndex.reset!('2013') }
344
431
 
345
432
  specify { expect(CitiesIndex.all).to have(1).item }
346
- specify { expect(CitiesIndex.aliases).to eq([]) }
433
+ specify { expect(CitiesIndex.aliases).to eq(['cities']) }
347
434
  specify { expect(CitiesIndex.indexes).to eq(['cities_2013']) }
348
435
  end
349
436
 
@@ -352,7 +439,7 @@ describe Chewy::Index::Actions do
352
439
 
353
440
  specify { expect(CitiesIndex.all).to have(1).item }
354
441
  specify { expect(CitiesIndex.aliases).to eq([]) }
355
- specify { expect(CitiesIndex.indexes).to eq([]) }
442
+ specify { expect(CitiesIndex.indexes).to eq(['cities']) }
356
443
  end
357
444
  end
358
445
 
@@ -360,14 +447,14 @@ describe Chewy::Index::Actions do
360
447
  before { CitiesIndex.reset!('2013') }
361
448
 
362
449
  specify { expect(CitiesIndex.all).to have(1).item }
363
- specify { expect(CitiesIndex.aliases).to eq([]) }
450
+ specify { expect(CitiesIndex.aliases).to eq(['cities']) }
364
451
  specify { expect(CitiesIndex.indexes).to eq(['cities_2013']) }
365
452
 
366
453
  context do
367
454
  before { CitiesIndex.reset!('2014') }
368
455
 
369
456
  specify { expect(CitiesIndex.all).to have(1).item }
370
- specify { expect(CitiesIndex.aliases).to eq([]) }
457
+ specify { expect(CitiesIndex.aliases).to eq(['cities']) }
371
458
  specify { expect(CitiesIndex.indexes).to eq(['cities_2014']) }
372
459
  specify { expect(Chewy.client.indices.exists(index: 'cities_2013')).to eq(false) }
373
460
  end
@@ -377,7 +464,7 @@ describe Chewy::Index::Actions do
377
464
 
378
465
  specify { expect(CitiesIndex.all).to have(1).item }
379
466
  specify { expect(CitiesIndex.aliases).to eq([]) }
380
- specify { expect(CitiesIndex.indexes).to eq([]) }
467
+ specify { expect(CitiesIndex.indexes).to eq(['cities']) }
381
468
  specify { expect(Chewy.client.indices.exists(index: 'cities_2013')).to eq(false) }
382
469
  end
383
470
  end