chewy 7.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +58 -0
  3. data/.rubocop.yml +13 -8
  4. data/.rubocop_todo.yml +110 -22
  5. data/CHANGELOG.md +53 -0
  6. data/Gemfile +0 -7
  7. data/Guardfile +3 -1
  8. data/README.md +282 -245
  9. data/chewy.gemspec +3 -5
  10. data/gemfiles/rails.5.2.activerecord.gemfile +8 -14
  11. data/gemfiles/rails.6.0.activerecord.gemfile +8 -14
  12. data/gemfiles/rails.6.1.activerecord.gemfile +8 -14
  13. data/lib/chewy.rb +21 -75
  14. data/lib/chewy/config.rb +40 -40
  15. data/lib/chewy/errors.rb +0 -12
  16. data/lib/chewy/fields/base.rb +11 -1
  17. data/lib/chewy/fields/root.rb +3 -4
  18. data/lib/chewy/index.rb +46 -87
  19. data/lib/chewy/index/actions.rb +51 -32
  20. data/lib/chewy/{type → index}/adapter/active_record.rb +12 -3
  21. data/lib/chewy/{type → index}/adapter/base.rb +2 -3
  22. data/lib/chewy/{type → index}/adapter/object.rb +27 -31
  23. data/lib/chewy/{type → index}/adapter/orm.rb +11 -14
  24. data/lib/chewy/{type → index}/crutch.rb +5 -5
  25. data/lib/chewy/{type → index}/import.rb +36 -27
  26. data/lib/chewy/{type → index}/import/bulk_builder.rb +15 -13
  27. data/lib/chewy/{type → index}/import/bulk_request.rb +6 -6
  28. data/lib/chewy/{type → index}/import/journal_builder.rb +10 -10
  29. data/lib/chewy/{type → index}/import/routine.rb +15 -14
  30. data/lib/chewy/{type → index}/mapping.rb +26 -31
  31. data/lib/chewy/{type → index}/observe.rb +9 -19
  32. data/lib/chewy/index/specification.rb +1 -0
  33. data/lib/chewy/{type → index}/syncer.rb +60 -57
  34. data/lib/chewy/{type → index}/witchcraft.rb +11 -7
  35. data/lib/chewy/{type → index}/wrapper.rb +2 -2
  36. data/lib/chewy/journal.rb +8 -8
  37. data/lib/chewy/minitest/helpers.rb +9 -13
  38. data/lib/chewy/minitest/search_index_receiver.rb +22 -26
  39. data/lib/chewy/railtie.rb +4 -2
  40. data/lib/chewy/rake_helper.rb +82 -107
  41. data/lib/chewy/rspec/update_index.rb +47 -43
  42. data/lib/chewy/search.rb +4 -17
  43. data/lib/chewy/search/loader.rb +18 -30
  44. data/lib/chewy/search/parameters.rb +4 -2
  45. data/lib/chewy/search/parameters/concerns/query_storage.rb +2 -2
  46. data/lib/chewy/search/parameters/source.rb +5 -1
  47. data/lib/chewy/search/query_proxy.rb +9 -2
  48. data/lib/chewy/search/request.rb +82 -86
  49. data/lib/chewy/search/response.rb +4 -4
  50. data/lib/chewy/search/scoping.rb +6 -7
  51. data/lib/chewy/search/scrolling.rb +11 -11
  52. data/lib/chewy/stash.rb +14 -22
  53. data/lib/chewy/strategy.rb +3 -19
  54. data/lib/chewy/strategy/sidekiq.rb +1 -0
  55. data/lib/chewy/version.rb +1 -1
  56. data/lib/generators/chewy/install_generator.rb +1 -1
  57. data/lib/tasks/chewy.rake +10 -22
  58. data/migration_guide.md +14 -0
  59. data/spec/chewy/config_spec.rb +14 -39
  60. data/spec/chewy/fields/base_spec.rb +412 -148
  61. data/spec/chewy/fields/root_spec.rb +16 -24
  62. data/spec/chewy/fields/time_fields_spec.rb +5 -5
  63. data/spec/chewy/index/actions_spec.rb +270 -24
  64. data/spec/chewy/{type → index}/adapter/active_record_spec.rb +68 -40
  65. data/spec/chewy/{type → index}/adapter/object_spec.rb +21 -6
  66. data/spec/chewy/{type → index}/import/bulk_builder_spec.rb +23 -31
  67. data/spec/chewy/{type → index}/import/bulk_request_spec.rb +5 -6
  68. data/spec/chewy/{type → index}/import/journal_builder_spec.rb +9 -15
  69. data/spec/chewy/{type → index}/import/routine_spec.rb +16 -16
  70. data/spec/chewy/{type → index}/import_spec.rb +102 -98
  71. data/spec/chewy/{type → index}/mapping_spec.rb +46 -54
  72. data/spec/chewy/index/observe_spec.rb +116 -0
  73. data/spec/chewy/index/settings_spec.rb +3 -1
  74. data/spec/chewy/index/specification_spec.rb +7 -17
  75. data/spec/chewy/{type → index}/syncer_spec.rb +14 -19
  76. data/spec/chewy/{type → index}/witchcraft_spec.rb +20 -22
  77. data/spec/chewy/index/wrapper_spec.rb +100 -0
  78. data/spec/chewy/index_spec.rb +59 -102
  79. data/spec/chewy/journal_spec.rb +9 -22
  80. data/spec/chewy/minitest/helpers_spec.rb +13 -15
  81. data/spec/chewy/minitest/search_index_receiver_spec.rb +22 -26
  82. data/spec/chewy/multi_search_spec.rb +4 -5
  83. data/spec/chewy/rake_helper_spec.rb +145 -55
  84. data/spec/chewy/rspec/update_index_spec.rb +74 -71
  85. data/spec/chewy/search/loader_spec.rb +19 -37
  86. data/spec/chewy/search/pagination/kaminari_examples.rb +3 -5
  87. data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
  88. data/spec/chewy/search/parameters/indices_spec.rb +2 -8
  89. data/spec/chewy/search/parameters/order_spec.rb +1 -1
  90. data/spec/chewy/search/parameters/query_storage_examples.rb +67 -21
  91. data/spec/chewy/search/parameters/search_after_spec.rb +4 -1
  92. data/spec/chewy/search/parameters/source_spec.rb +8 -2
  93. data/spec/chewy/search/parameters_spec.rb +12 -3
  94. data/spec/chewy/search/query_proxy_spec.rb +68 -17
  95. data/spec/chewy/search/request_spec.rb +222 -74
  96. data/spec/chewy/search/response_spec.rb +12 -12
  97. data/spec/chewy/search/scrolling_spec.rb +7 -9
  98. data/spec/chewy/search_spec.rb +32 -35
  99. data/spec/chewy/stash_spec.rb +9 -21
  100. data/spec/chewy/strategy/active_job_spec.rb +8 -8
  101. data/spec/chewy/strategy/atomic_spec.rb +9 -10
  102. data/spec/chewy/strategy/sidekiq_spec.rb +8 -8
  103. data/spec/chewy/strategy_spec.rb +19 -15
  104. data/spec/chewy_spec.rb +14 -100
  105. data/spec/spec_helper.rb +2 -21
  106. data/spec/support/active_record.rb +15 -5
  107. metadata +44 -103
  108. data/.circleci/config.yml +0 -214
  109. data/Appraisals +0 -81
  110. data/gemfiles/rails.5.2.mongoid.6.4.gemfile +0 -17
  111. data/gemfiles/sequel.4.45.gemfile +0 -11
  112. data/lib/chewy/search/pagination/will_paginate.rb +0 -43
  113. data/lib/chewy/strategy/resque.rb +0 -27
  114. data/lib/chewy/strategy/shoryuken.rb +0 -40
  115. data/lib/chewy/type.rb +0 -120
  116. data/lib/chewy/type/actions.rb +0 -43
  117. data/lib/chewy/type/adapter/mongoid.rb +0 -67
  118. data/lib/chewy/type/adapter/sequel.rb +0 -93
  119. data/lib/sequel/plugins/chewy_observe.rb +0 -63
  120. data/spec/chewy/search/pagination/will_paginate_examples.rb +0 -63
  121. data/spec/chewy/search/pagination/will_paginate_spec.rb +0 -23
  122. data/spec/chewy/strategy/resque_spec.rb +0 -46
  123. data/spec/chewy/strategy/shoryuken_spec.rb +0 -70
  124. data/spec/chewy/type/actions_spec.rb +0 -50
  125. data/spec/chewy/type/adapter/mongoid_spec.rb +0 -372
  126. data/spec/chewy/type/adapter/sequel_spec.rb +0 -472
  127. data/spec/chewy/type/observe_spec.rb +0 -137
  128. data/spec/chewy/type/wrapper_spec.rb +0 -100
  129. data/spec/chewy/type_spec.rb +0 -55
  130. data/spec/support/mongoid.rb +0 -93
  131. data/spec/support/sequel.rb +0 -80
@@ -64,12 +64,12 @@ module Chewy
64
64
  end
65
65
  alias_method :aggregations, :aggs
66
66
 
67
- # {Chewy::Type} wrappers collection instantiated on top of hits.
67
+ # {Chewy::Index} wrappers collection instantiated on top of hits.
68
68
  #
69
- # @return [Array<Chewy::Type>]
69
+ # @return [Array<Chewy::Index>]
70
70
  def wrappers
71
71
  @wrappers ||= hits.map do |hit|
72
- @loader.derive_type(hit['_index'], hit['_type']).build(hit)
72
+ @loader.derive_index(hit['_index']).build(hit)
73
73
  end
74
74
  end
75
75
 
@@ -102,7 +102,7 @@ module Chewy
102
102
  # end
103
103
  # @see #wrappers
104
104
  # @see #objects
105
- # @return [{Chewy::Type => Object}] a hash with wrappers as keys and ORM/ODM objects as values
105
+ # @return [{Chewy::Index => Object}] a hash with wrappers as keys and ORM/ODM objects as values
106
106
  def object_hash
107
107
  @object_hash ||= wrappers.zip(objects).to_h
108
108
  end
@@ -9,17 +9,16 @@ module Chewy
9
9
  # query(match: {name: name})
10
10
  # end
11
11
  #
12
- # define_type :user do
13
- # def self.by_age(age)
14
- # filter(term: {age: age})
15
- # end
12
+ #
13
+ # def self.by_age(age)
14
+ # filter(term: {age: age})
16
15
  # end
17
16
  # end
18
17
  #
19
18
  # UsersIndex.limit(10).by_name('Martin')
20
19
  # # => <UsersIndex::Query {..., :body=>{:size=>10, :query=>{:match=>{:name=>"Martin"}}}}>
21
- # UsersIndex::User.limit(10).by_name('Martin').by_age(42)
22
- # # => <UsersIndex::User::Query {..., :body=>{:size=>10, :query=>{:bool=>{
20
+ # UsersIndex.limit(10).by_name('Martin').by_age(42)
21
+ # # => <UsersIndex::Query {..., :body=>{:size=>10, :query=>{:bool=>{
23
22
  # # :must=>{:match=>{:name=>"Martin"}},
24
23
  # # :filter=>{:term=>{:age=>42}}}}}}>
25
24
  module Scoping
@@ -28,7 +27,7 @@ module Chewy
28
27
  module ClassMethods
29
28
  # The scopes stack.
30
29
  #
31
- # @return [Array<Chewy::Search::Reques>] array of scopes
30
+ # @return [Array<Chewy::Search::Request>] array of scopes
32
31
  def scopes
33
32
  Thread.current[:chewy_scopes] ||= []
34
33
  end
@@ -40,6 +40,7 @@ module Chewy
40
40
  yield(hits) if hits.present?
41
41
  scroll_id = result['_scroll_id']
42
42
  break if fetched >= total
43
+
43
44
  result = perform_scroll(scroll: scroll, scroll_id: scroll_id)
44
45
  end
45
46
  ensure
@@ -61,17 +62,17 @@ module Chewy
61
62
  # @example
62
63
  # PlaceIndex.scroll_hits.map { |hit| hit['_id'] }
63
64
  # @return [Enumerator] a standard ruby Enumerator
64
- def scroll_hits(**options)
65
+ def scroll_hits(**options, &block)
65
66
  return enum_for(:scroll_hits, **options) unless block_given?
66
67
 
67
68
  scroll_batches(**options).each do |batch|
68
- batch.each { |hit| yield hit }
69
+ batch.each(&block)
69
70
  end
70
71
  end
71
72
 
72
73
  # @!method scroll_wrappers(batch_size: 1000, scroll: '1m')
73
74
  # Iterates through the documents of the scope in batches. Yields
74
- # each hit wrapped with {Chewy::Type}.
75
+ # each hit wrapped with {Chewy::Index}.
75
76
  #
76
77
  # @param batch_size [Integer] batch size obviously, replaces `size` query parameter
77
78
  # @param scroll [String] cursor expiration time
@@ -79,7 +80,7 @@ module Chewy
79
80
  # @overload scroll_wrappers(batch_size: 1000, scroll: '1m')
80
81
  # @example
81
82
  # PlaceIndex.scroll_wrappers { |object| p object.id }
82
- # @yieldparam object [Chewy::Type] block is executed for each hit object
83
+ # @yieldparam object [Chewy::Index] block is executed for each hit object
83
84
  #
84
85
  # @overload scroll_wrappers(batch_size: 1000, scroll: '1m')
85
86
  # @example
@@ -89,7 +90,7 @@ module Chewy
89
90
  return enum_for(:scroll_wrappers, **options) unless block_given?
90
91
 
91
92
  scroll_hits(**options).each do |hit|
92
- yield loader.derive_type(hit['_index'], hit['_type']).build(hit)
93
+ yield loader.derive_index(hit['_index']).build(hit)
93
94
  end
94
95
  end
95
96
 
@@ -113,12 +114,12 @@ module Chewy
113
114
  # @example
114
115
  # PlaceIndex.scroll_objects.map { |record| record.id }
115
116
  # @return [Enumerator] a standard ruby Enumerator
116
- def scroll_objects(**options)
117
+ def scroll_objects(**options, &block)
117
118
  return enum_for(:scroll_objects, **options) unless block_given?
118
119
 
119
120
  except(:source, :stored_fields, :script_fields, :docvalue_fields)
120
121
  .source(false).scroll_batches(**options).each do |batch|
121
- loader.load(batch).each { |object| yield object }
122
+ loader.load(batch).each(&block)
122
123
  end
123
124
  end
124
125
  alias_method :scroll_records, :scroll_objects
@@ -127,10 +128,9 @@ module Chewy
127
128
  private
128
129
 
129
130
  def perform_scroll(body)
130
- ActiveSupport::Notifications.instrument 'search_query.chewy',
131
- notification_payload(request: body) do
132
- Chewy.client.scroll(body)
133
- end
131
+ ActiveSupport::Notifications.instrument 'search_query.chewy', notification_payload(request: body) do
132
+ Chewy.client.scroll(body)
133
+ end
134
134
  end
135
135
  end
136
136
  end
data/lib/chewy/stash.rb CHANGED
@@ -9,11 +9,9 @@ module Chewy
9
9
  class Specification < Chewy::Index
10
10
  index_name 'chewy_specifications'
11
11
 
12
- define_type :specification do
13
- default_import_options journal: false
12
+ default_import_options journal: false
14
13
 
15
- field :specification, type: 'binary'
16
- end
14
+ field :specification, type: 'binary'
17
15
  end
18
16
 
19
17
  class Journal < Chewy::Index
@@ -43,32 +41,26 @@ module Chewy
43
41
  # @param indices [Chewy::Index, Array<Chewy::Index>]
44
42
  def self.for(*something)
45
43
  something = something.flatten.compact
46
- types = something.flat_map { |s| Chewy.derive_types(s) }
47
- return none if something.present? && types.blank?
44
+ indexes = something.flat_map { |s| Chewy.derive_name(s) }
45
+ return none if something.present? && indexes.blank?
46
+
48
47
  scope = all
49
- types.map(&:index).uniq.each do |index|
48
+ indexes.each do |index|
50
49
  scope = scope.or(filter(term: {index_name: index.derivable_name}))
51
50
  end
52
51
  scope
53
52
  end
54
53
 
55
- define_type :journal do
56
- default_import_options journal: false
54
+ default_import_options journal: false
57
55
 
58
- field :index_name, type: 'keyword'
59
- field :type_name, type: 'keyword'
60
- field :action, type: 'keyword'
61
- field :references, type: 'binary'
62
- field :created_at, type: 'date'
63
-
64
- def type
65
- @type ||= Chewy.derive_type("#{index_name}##{type_name}")
66
- end
56
+ field :index_name, type: 'keyword'
57
+ field :action, type: 'keyword'
58
+ field :references, type: 'binary'
59
+ field :created_at, type: 'date'
67
60
 
68
- def references
69
- @references ||= Array.wrap(@attributes['references']).map do |item|
70
- JSON.load(Base64.decode64(item)) # rubocop:disable Security/JSONLoad
71
- end
61
+ def references
62
+ @references ||= Array.wrap(@attributes['references']).map do |item|
63
+ JSON.load(Base64.decode64(item)) # rubocop:disable Security/JSONLoad
72
64
  end
73
65
  end
74
66
  end
@@ -3,13 +3,6 @@ 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
- nil
11
- end
12
-
13
6
  begin
14
7
  require 'sidekiq'
15
8
  require 'chewy/strategy/sidekiq'
@@ -17,13 +10,6 @@ rescue LoadError
17
10
  nil
18
11
  end
19
12
 
20
- begin
21
- require 'shoryuken'
22
- require 'chewy/strategy/shoryuken'
23
- rescue LoadError
24
- nil
25
- end
26
-
27
13
  begin
28
14
  require 'active_job'
29
15
  require 'chewy/strategy/active_job'
@@ -60,6 +46,7 @@ module Chewy
60
46
 
61
47
  def pop
62
48
  raise "Can't pop root strategy" if @stack.one?
49
+
63
50
  result = @stack.pop.tap(&:leave)
64
51
  debug "[#{@stack.size}] -> #{result.name}, now #{current.name}" if @stack.size > 1
65
52
  result
@@ -75,17 +62,14 @@ module Chewy
75
62
  private
76
63
 
77
64
  def debug(string)
78
- return unless Chewy.logger && Chewy.logger.debug?
65
+ return unless Chewy.logger&.debug?
66
+
79
67
  line = caller.detect { |l| l !~ %r{lib/chewy/strategy.rb:|lib/chewy.rb:} }
80
68
  Chewy.logger.debug(["Chewy strategies stack: #{string}", line.sub(/:in\s.+$/, '')].join(' @ '))
81
69
  end
82
70
 
83
71
  def resolve(name)
84
72
  "Chewy::Strategy::#{name.to_s.camelize}".safe_constantize or raise "Can't find update strategy `#{name}`"
85
- rescue NameError => ex
86
- # WORKAROUND: Strange behavior of `safe_constantize` with mongoid gem
87
- raise "Can't find update strategy `#{name}`" if ex.name.to_s.demodulize == name.to_s.camelize
88
- raise
89
73
  end
90
74
  end
91
75
  end
@@ -22,6 +22,7 @@ module Chewy
22
22
  def leave
23
23
  @stash.each do |type, ids|
24
24
  next if ids.empty?
25
+
25
26
  ::Sidekiq::Client.push(
26
27
  'queue' => sidekiq_queue,
27
28
  'class' => Chewy::Strategy::Sidekiq::Worker,
data/lib/chewy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '7.1.0'.freeze
2
+ VERSION = '7.2.0'.freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  module Chewy
2
2
  module Generators
3
3
  class InstallGenerator < Rails::Generators::Base
4
- source_root File.expand_path('../../templates', __FILE__)
4
+ source_root File.expand_path('../templates', __dir__)
5
5
 
6
6
  def copy_configuration
7
7
  template 'chewy.yml', 'config/chewy.yml'
data/lib/tasks/chewy.rake CHANGED
@@ -47,6 +47,16 @@ namespace :chewy do
47
47
  Chewy::RakeHelper.sync(except: processed)
48
48
  end
49
49
 
50
+ desc 'Reindex data from source index to destination index'
51
+ task :reindex, %i[source dest] => :environment do |_task, args|
52
+ Chewy::RakeHelper.reindex(source: args[:source], dest: args[:dest])
53
+ end
54
+
55
+ desc 'Update mapping of exising index with body hash'
56
+ task :update_mapping, %i[index_name] => :environment do |_task, args|
57
+ Chewy::RakeHelper.update_mapping(name: args[:name])
58
+ end
59
+
50
60
  namespace :parallel do
51
61
  desc 'Parallel version of `rake chewy:reset`'
52
62
  task reset: :environment do |_task, args|
@@ -87,26 +97,4 @@ namespace :chewy do
87
97
  Chewy::RakeHelper.journal_clean(**parse_journal_args(args.extras))
88
98
  end
89
99
  end
90
-
91
- task apply_changes_from: :environment do |_task, args|
92
- ActiveSupport::Deprecation.warn '`rake chewy:apply_changes_from` is deprecated and will be removed soon, use `rake chewy:journal:apply` instead'
93
-
94
- Chewy::RakeHelper.subscribed_task_stats do
95
- params = args.extras
96
-
97
- if params.empty?
98
- puts 'Please specify a timestamp like chewy:apply_changes_from[1469528705]'
99
- else
100
- timestamp, retries = params
101
- time = Time.at(timestamp.to_i)
102
- Chewy::Journal.new.apply(time, retries: (retries.to_i if retries))
103
- end
104
- end
105
- end
106
-
107
- task clean_journal: :environment do |_task, args|
108
- ActiveSupport::Deprecation.warn '`rake chewy:clean_journal` is deprecated and will be removed soon, use `rake chewy:journal:clean` instead'
109
-
110
- Chewy::Journal.new.clean(args.extras.first)
111
- end
112
100
  end
data/migration_guide.md CHANGED
@@ -17,6 +17,20 @@ In order to upgrade Chewy 6/Elasticsearch 6 to Chewy 7/Elasticsearch 7 in the mo
17
17
  * Run your test suite on Chewy 7.1 / Elasticsearch 7
18
18
  * Run manual tests on Chewy 7.1 / Elasticsearch 7
19
19
  * Upgrade to Chewy 7.1
20
+ * Upgrade to Chewy 7.2:
21
+ * Remove all the the `Chewy::Type` class usages, e.g. remove `CitiesIndex::City` / `CitiesIndex.city`
22
+ * `CitiesIndex::City.import! ...` becomes `CitiesIndex.import! ...`
23
+ * Update indexes with simplified DSL:
24
+ * `define_type` block -> `index_scope` clause
25
+ * it can be omitted completely, if you don't need to specify the scope or options, e.g. `name`
26
+ * Remove type names from string representations:
27
+ * in `update_index` ActiveRecord helper and RSpec matcher, e.g.
28
+ * `update_index('cities#city')` -> `update_index('cities')`
29
+ * `update_index(UsersIndex::User)` -> `update_index(UsersIndex)`
30
+ * in rake tasks (e.g. `rake chewy:update[cities#city]` -> `rake chewy:update[cities]`)
31
+ * rake tasks output is also changed (e.g. `Imported CitiesIndex::City in 1s, stats: index 3` -> `Imported CitiesIndex in 1s, stats: index 3`)
32
+ * Use index name instead of type name in loader additional scope
33
+ * e.g. `CitiesIndex.filter(...).load(city: {scope: City.where(...)})` -> `CitiesIndex.filter(...).load(cities: {scope: City.where(...)})`
20
34
 
21
35
  ## Chewy 5/Elasticsearch 5 to Chewy 6/Elasticsearch 6
22
36
 
@@ -8,6 +8,7 @@ describe Chewy::Config do
8
8
  its(:transport_logger) { should be_nil }
9
9
  its(:root_strategy) { should == :base }
10
10
  its(:request_strategy) { should == :atomic }
11
+ its(:console_strategy) { should == :urgent }
11
12
  its(:use_after_commit_callbacks) { should == true }
12
13
  its(:indices_path) { should == 'app/chewy' }
13
14
  its(:reset_disable_refresh_interval) { should == false }
@@ -55,58 +56,19 @@ describe Chewy::Config do
55
56
  context 'nothing is defined' do
56
57
  before do
57
58
  hide_const('Kaminari')
58
- hide_const('WillPaginate')
59
59
  end
60
60
 
61
61
  specify do
62
62
  expect(subject.search_class.included_modules)
63
63
  .not_to include(Chewy::Search::Pagination::Kaminari)
64
64
  end
65
-
66
- specify do
67
- expect(subject.search_class.included_modules)
68
- .not_to include(Chewy::Search::Pagination::WillPaginate)
69
- end
70
65
  end
71
66
 
72
67
  context 'kaminari' do
73
- before { hide_const('WillPaginate') }
74
-
75
- specify do
76
- expect(subject.search_class.included_modules)
77
- .to include(Chewy::Search::Pagination::Kaminari)
78
- end
79
-
80
- specify do
81
- expect(subject.search_class.included_modules)
82
- .not_to include(Chewy::Search::Pagination::WillPaginate)
83
- end
84
- end
85
-
86
- context 'will_paginate' do
87
- before { hide_const('Kaminari') }
88
-
89
- specify do
90
- expect(subject.search_class.included_modules)
91
- .not_to include(Chewy::Search::Pagination::Kaminari)
92
- end
93
-
94
- specify do
95
- expect(subject.search_class.included_modules)
96
- .to include(Chewy::Search::Pagination::WillPaginate)
97
- end
98
- end
99
-
100
- context 'both are defined' do
101
68
  specify do
102
69
  expect(subject.search_class.included_modules)
103
70
  .to include(Chewy::Search::Pagination::Kaminari)
104
71
  end
105
-
106
- specify do
107
- expect(subject.search_class.included_modules)
108
- .not_to include(Chewy::Search::Pagination::WillPaginate)
109
- end
110
72
  end
111
73
  end
112
74
 
@@ -133,4 +95,17 @@ describe Chewy::Config do
133
95
  end
134
96
  end
135
97
  end
98
+
99
+ describe '.console_strategy' do
100
+ context 'sets .console_strategy' do
101
+ let(:default_strategy) { subject.console_strategy }
102
+ let(:new_strategy) { :atomic }
103
+ after { subject.console_strategy = default_strategy }
104
+
105
+ specify do
106
+ expect { subject.console_strategy = new_strategy }
107
+ .to change { subject.console_strategy }.to(new_strategy)
108
+ end
109
+ end
110
+ end
136
111
  end
@@ -10,8 +10,12 @@ describe Chewy::Fields::Base do
10
10
  specify { expect(field.compose(double(value: 'hello'))).to eq(name: 'hello') }
11
11
  specify { expect(field.compose(double(value: %w[hello world]))).to eq(name: %w[hello world]) }
12
12
 
13
- specify { expect(described_class.new(:name, value: :last_name).compose(double(last_name: 'hello'))).to eq(name: 'hello') }
14
- specify { expect(described_class.new(:name, value: :last_name).compose('last_name' => 'hello')).to eq(name: 'hello') }
13
+ specify do
14
+ expect(described_class.new(:name, value: :last_name).compose(double(last_name: 'hello'))).to eq(name: 'hello')
15
+ end
16
+ specify do
17
+ expect(described_class.new(:name, value: :last_name).compose('last_name' => 'hello')).to eq(name: 'hello')
18
+ end
15
19
  specify { expect(described_class.new(:name).compose(double(name: 'hello'))).to eq(name: 'hello') }
16
20
  specify { expect(described_class.new(:false_value).compose(false_value: false)).to eq(false_value: false) }
17
21
  specify { expect(described_class.new(:true_value).compose(true_value: true)).to eq(true_value: true) }
@@ -40,9 +44,24 @@ describe Chewy::Fields::Base do
40
44
  end
41
45
 
42
46
  context 'parent objects' do
43
- let!(:country) { described_class.new(:name, value: ->(country, crutches) { country.cities.map { |city| double(districts: city.districts, name: crutches.city_name) } }) }
44
- let!(:city) { described_class.new(:name, value: ->(city, country, crutches) { city.districts.map { |district| [district, country.name, crutches.suffix] } }) }
45
- let!(:district) { described_class.new(:name, value: ->(district, city, country, crutches) { [district, city.name, country.name, crutches] }) }
47
+ let!(:country) do
48
+ described_class.new(:name, value: lambda { |country, crutches|
49
+ country.cities.map do |city|
50
+ double(districts: city.districts, name: crutches.city_name)
51
+ end
52
+ })
53
+ end
54
+ let!(:city) do
55
+ described_class.new(:name, value: lambda { |city, country, crutches|
56
+ city.districts.map do |district|
57
+ [district, country.name, crutches.suffix]
58
+ end
59
+ })
60
+ end
61
+ let(:district_value) { ->(district, city, country, crutches) { [district, city.name, country.name, crutches] } }
62
+ let!(:district) do
63
+ described_class.new(:name, value: district_value)
64
+ end
46
65
  let(:crutches) { double(suffix: 'suffix', city_name: 'Bangkok') }
47
66
 
48
67
  before do
@@ -122,14 +141,12 @@ describe Chewy::Fields::Base do
122
141
  context 'default field type' do
123
142
  before do
124
143
  stub_index(:events) do
125
- define_type :event do
144
+ field :id
145
+ field :category do
126
146
  field :id
127
- field :category do
147
+ field :licenses do
128
148
  field :id
129
- field :licenses do
130
- field :id
131
- field :created_at, type: 'time'
132
- end
149
+ field :created_at, type: 'time'
133
150
  end
134
151
  end
135
152
  end
@@ -143,18 +160,20 @@ describe Chewy::Fields::Base do
143
160
  end
144
161
 
145
162
  specify do
146
- expect(EventsIndex::Event.mappings_hash).to eq(
147
- properties: {
148
- id: {type: 'integer'},
149
- category: {
150
- type: 'object',
151
- properties: {
152
- id: {type: 'integer'},
153
- licenses: {
154
- type: 'object',
155
- properties: {
156
- id: {type: 'integer'},
157
- created_at: {type: 'time'}
163
+ expect(EventsIndex.mappings_hash).to eq(
164
+ mappings: {
165
+ properties: {
166
+ id: {type: 'integer'},
167
+ category: {
168
+ type: 'object',
169
+ properties: {
170
+ id: {type: 'integer'},
171
+ licenses: {
172
+ type: 'object',
173
+ properties: {
174
+ id: {type: 'integer'},
175
+ created_at: {type: 'time'}
176
+ }
158
177
  }
159
178
  }
160
179
  }
@@ -167,153 +186,191 @@ describe Chewy::Fields::Base do
167
186
  context 'objects, hashes and arrays' do
168
187
  before do
169
188
  stub_index(:events) do
170
- define_type :event do
189
+ field :id
190
+ field :category do
171
191
  field :id
172
- field :category do
192
+ field :licenses do
173
193
  field :id
174
- field :licenses do
175
- field :id
176
- field :name
177
- end
194
+ field :name
178
195
  end
179
196
  end
180
197
  end
181
198
  end
182
199
 
183
- # rubocop:disable Style/BracesAroundHashParameters
184
200
  specify do
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'}})
201
+ expect(
202
+ EventsIndex.root.compose({id: 1, category: {id: 2, licenses: {id: 3, name: 'Name'}}})
203
+ ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
188
204
  end
189
205
 
190
206
  specify do
191
- expect(EventsIndex::Event.root.compose({id: 1, category: [
192
- {id: 2, 'licenses' => {id: 3, name: 'Name1'}},
193
- {id: 4, licenses: nil}
194
- ]})).to eq('id' => 1, 'category' => [
207
+ expect(
208
+ EventsIndex.root.compose({id: 1, category: [
209
+ {id: 2, 'licenses' => {id: 3, name: 'Name1'}},
210
+ {id: 4, licenses: nil}
211
+ ]})
212
+ ).to eq('id' => 1, 'category' => [
195
213
  {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name1'}},
196
214
  {'id' => 4, 'licenses' => nil.as_json}
197
215
  ])
198
216
  end
199
217
 
200
218
  specify do
201
- expect(EventsIndex::Event.root.compose({'id' => 1, category: {id: 2, licenses: [
202
- {id: 3, name: 'Name1'}, {id: 4, name: 'Name2'}
203
- ]}})).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => [
204
- {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
205
- ]})
219
+ expect(
220
+ EventsIndex.root.compose({
221
+ 'id' => 1,
222
+ category: {
223
+ id: 2, licenses: [
224
+ {id: 3, name: 'Name1'},
225
+ {id: 4, name: 'Name2'}
226
+ ]
227
+ }
228
+ })
229
+ ).to eq(
230
+ 'id' => 1,
231
+ 'category' => {
232
+ 'id' => 2,
233
+ 'licenses' => [
234
+ {'id' => 3, 'name' => 'Name1'},
235
+ {'id' => 4, 'name' => 'Name2'}
236
+ ]
237
+ }
238
+ )
206
239
  end
207
240
 
208
241
  specify do
209
- expect(EventsIndex::Event.root.compose({id: 1, category: [
210
- {id: 2, licenses: [
211
- {id: 3, 'name' => 'Name1'}, {id: 4, name: 'Name2'}
212
- ]},
213
- {id: 5, licenses: []}
214
- ]})).to eq('id' => 1, 'category' => [
215
- {'id' => 2, 'licenses' => [
216
- {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
217
- ]},
218
- {'id' => 5, 'licenses' => []}
219
- ])
242
+ expect(
243
+ EventsIndex.root.compose({id: 1, category: [
244
+ {id: 2, licenses: [
245
+ {id: 3, 'name' => 'Name1'}, {id: 4, name: 'Name2'}
246
+ ]},
247
+ {id: 5, licenses: []}
248
+ ]})
249
+ ).to eq(
250
+ 'id' => 1,
251
+ 'category' => [
252
+ {'id' => 2, 'licenses' => [
253
+ {'id' => 3, 'name' => 'Name1'},
254
+ {'id' => 4, 'name' => 'Name2'}
255
+ ]},
256
+ {'id' => 5, 'licenses' => []}
257
+ ]
258
+ )
220
259
  end
221
- # rubocop:enable Style/BracesAroundHashParameters
222
-
223
260
  specify do
224
- expect(EventsIndex::Event.root.compose(
225
- double(id: 1, category: double(id: 2, licenses: double(id: 3, name: 'Name')))
226
- )).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
261
+ expect(
262
+ EventsIndex.root.compose(double(id: 1, category: double(id: 2, licenses: double(id: 3, name: 'Name'))))
263
+ ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
227
264
  end
228
265
 
229
266
  specify do
230
- expect(EventsIndex::Event.root.compose(double(id: 1, category: [
231
- double(id: 2, licenses: double(id: 3, name: 'Name1')),
232
- double(id: 4, licenses: nil)
233
- ]))).to eq('id' => 1, 'category' => [
267
+ expect(
268
+ EventsIndex.root.compose(double(id: 1, category: [
269
+ double(id: 2, licenses: double(id: 3, name: 'Name1')),
270
+ double(id: 4, licenses: nil)
271
+ ]))
272
+ ).to eq('id' => 1, 'category' => [
234
273
  {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name1'}},
235
274
  {'id' => 4, 'licenses' => nil.as_json}
236
275
  ])
237
276
  end
238
277
 
239
278
  specify do
240
- expect(EventsIndex::Event.root.compose(double(id: 1, category: double(id: 2, licenses: [
241
- double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2')
242
- ])))).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => [
279
+ expect(
280
+ EventsIndex.root.compose(double(id: 1, category: double(id: 2, licenses: [
281
+ double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2')
282
+ ])))
283
+ ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => [
243
284
  {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
244
285
  ]})
245
286
  end
246
287
 
247
288
  specify do
248
- expect(EventsIndex::Event.root.compose(double(id: 1, category: [
249
- double(id: 2, licenses: [
250
- double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2')
251
- ]),
252
- double(id: 5, licenses: [])
253
- ]))).to eq('id' => 1, 'category' => [
254
- {'id' => 2, 'licenses' => [
255
- {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
256
- ]},
257
- {'id' => 5, 'licenses' => []}
258
- ])
289
+ expect(
290
+ EventsIndex.root.compose(double(id: 1, category: [
291
+ double(id: 2, licenses: [
292
+ double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2')
293
+ ]),
294
+ double(id: 5, licenses: [])
295
+ ]))
296
+ ).to eq(
297
+ 'id' => 1, 'category' => [
298
+ {'id' => 2, 'licenses' => [
299
+ {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
300
+ ]},
301
+ {'id' => 5, 'licenses' => []}
302
+ ]
303
+ )
259
304
  end
260
305
  end
261
306
 
262
307
  context 'custom methods' do
263
308
  before do
264
309
  stub_index(:events) do
265
- define_type :event do
310
+ field :id, type: 'integer'
311
+ field :category, value: -> { categories } do
266
312
  field :id, type: 'integer'
267
- field :category, value: -> { categories } do
313
+ field :licenses, value: -> { license } do
268
314
  field :id, type: 'integer'
269
- field :licenses, value: -> { license } do
270
- field :id, type: 'integer'
271
- field :name
272
- end
315
+ field :name
273
316
  end
274
317
  end
275
318
  end
276
319
  end
277
320
 
278
321
  specify do
279
- expect(EventsIndex::Event.root.compose(
280
- double(id: 1, categories: double(id: 2, license: double(id: 3, name: 'Name')))
281
- )).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
322
+ expect(
323
+ EventsIndex.root.compose(
324
+ double(
325
+ id: 1, categories: double(
326
+ id: 2, license: double(
327
+ id: 3, name: 'Name'
328
+ )
329
+ )
330
+ )
331
+ )
332
+ ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}})
282
333
  end
283
334
  end
284
335
 
285
336
  context 'objects and multi_fields' do
286
337
  before do
287
338
  stub_index(:events) do
288
- define_type :event do
289
- field :id, type: 'integer'
290
- field :name, type: 'integer' do
291
- field :raw, analyzer: 'my_own'
292
- end
293
- field :category, type: 'object'
339
+ field :id, type: 'integer'
340
+ field :name, type: 'integer' do
341
+ field :raw, analyzer: 'my_own'
294
342
  end
343
+ field :category, type: 'object'
295
344
  end
296
345
  end
297
346
 
298
347
  specify do
299
- expect(EventsIndex::Event.mappings_hash).to eq(
300
- properties: {
301
- id: {type: 'integer'},
302
- name: {
303
- type: 'integer',
304
- fields: {
305
- raw: {analyzer: 'my_own', type: Chewy.default_field_type}
306
- }
307
- },
308
- category: {type: 'object'}
348
+ expect(EventsIndex.mappings_hash).to eq(
349
+ mappings: {
350
+ properties: {
351
+ id: {type: 'integer'},
352
+ name: {
353
+ type: 'integer',
354
+ fields: {
355
+ raw: {analyzer: 'my_own', type: Chewy.default_field_type}
356
+ }
357
+ },
358
+ category: {type: 'object'}
359
+ }
309
360
  }
310
361
  )
311
362
  end
312
363
 
313
364
  specify do
314
- expect(EventsIndex::Event.root.compose(
315
- double(id: 1, name: 'Jonny', category: double(id: 2, as_json: {'name' => 'Borogoves'}))
316
- )).to eq(
365
+ expect(
366
+ EventsIndex.root.compose(
367
+ double(
368
+ id: 1, name: 'Jonny', category: double(
369
+ id: 2, as_json: {'name' => 'Borogoves'}
370
+ )
371
+ )
372
+ )
373
+ ).to eq(
317
374
  'id' => 1,
318
375
  'name' => 'Jonny',
319
376
  'category' => {'name' => 'Borogoves'}
@@ -321,12 +378,14 @@ describe Chewy::Fields::Base do
321
378
  end
322
379
 
323
380
  specify do
324
- expect(EventsIndex::Event.root.compose(
325
- double(id: 1, name: 'Jonny', category: [
326
- double(id: 2, as_json: {'name' => 'Borogoves1'}),
327
- double(id: 3, as_json: {'name' => 'Borogoves2'})
328
- ])
329
- )).to eq(
381
+ expect(
382
+ EventsIndex.root.compose(
383
+ double(id: 1, name: 'Jonny', category: [
384
+ double(id: 2, as_json: {'name' => 'Borogoves1'}),
385
+ double(id: 3, as_json: {'name' => 'Borogoves2'})
386
+ ])
387
+ )
388
+ ).to eq(
330
389
  'id' => 1,
331
390
  'name' => 'Jonny',
332
391
  'category' => [
@@ -337,56 +396,262 @@ describe Chewy::Fields::Base do
337
396
  end
338
397
  end
339
398
 
340
- context 'objects and scopes', :orm do
399
+ context 'ignore_blank option for field method', :orm do
341
400
  before do
401
+ stub_model(:location)
342
402
  stub_model(:city)
343
403
  stub_model(:country)
344
404
 
345
- case adapter
346
- when :active_record
347
- City.belongs_to :country
348
- if ActiveRecord::VERSION::MAJOR >= 4
349
- Country.has_many :cities, -> { order :id }
350
- else
351
- Country.has_many :cities, order: :id
352
- end
353
- when :mongoid
354
- if Mongoid::VERSION.start_with?('6')
355
- City.belongs_to :country, optional: true
356
- else
357
- City.belongs_to :country
358
- end
359
- Country.has_many :cities, order: :id.asc
360
- when :sequel
361
- City.many_to_one :country
362
- Country.one_to_many :cities, order: :id
363
- end
405
+ City.belongs_to :country
406
+ Location.belongs_to :city
407
+ City.has_one :location, -> { order :id }
408
+ Country.has_many :cities, -> { order :id }
409
+ end
364
410
 
365
- stub_index(:countries) do
366
- define_type Country do
411
+ context 'text fields with and without ignore_blank option' do
412
+ before do
413
+ stub_index(:countries) do
414
+ index_scope Country
367
415
  field :id
368
416
  field :cities do
369
417
  field :id
370
418
  field :name
419
+ field :historical_name, ignore_blank: false
420
+ field :description, ignore_blank: true
371
421
  end
372
422
  end
373
423
  end
424
+
425
+ let(:country_with_cities) do
426
+ cities = [
427
+ City.create!(id: 1, name: '', historical_name: '', description: ''),
428
+ City.create!(id: 2, name: '', historical_name: '', description: '')
429
+ ]
430
+
431
+ Country.create!(id: 1, cities: cities)
432
+ end
433
+
434
+ specify do
435
+ expect(CountriesIndex.root.compose(country_with_cities)).to eq(
436
+ 'id' => 1, 'cities' => [
437
+ {'id' => 1, 'name' => '', 'historical_name' => ''},
438
+ {'id' => 2, 'name' => '', 'historical_name' => ''}
439
+ ]
440
+ )
441
+ end
374
442
  end
375
443
 
376
- let(:country_with_cities) do
377
- cities = [City.create!(id: 1, name: 'City1'), City.create!(id: 2, name: 'City2')]
444
+ context 'nested fields' do
445
+ context 'with ignore_blank: true option' do
446
+ before do
447
+ stub_index(:countries) do
448
+ index_scope Country
449
+ field :id
450
+ field :cities, ignore_blank: true do
451
+ field :id
452
+ field :name
453
+ field :historical_name, ignore_blank: true
454
+ field :description
455
+ end
456
+ end
457
+ end
378
458
 
379
- if adapter == :sequel
380
- Country.create(id: 1).tap do |country|
381
- cities.each { |city| country.add_city(city) }
459
+ let(:country) { Country.create!(id: 1, cities: cities) }
460
+ context('without cities') do
461
+ let(:cities) { [] }
462
+ specify do
463
+ expect(CountriesIndex.root.compose(country))
464
+ .to eq('id' => 1)
465
+ end
466
+ end
467
+ context('with cities') do
468
+ let(:cities) { [City.create!(id: 1, name: '', historical_name: '')] }
469
+ specify do
470
+ expect(CountriesIndex.root.compose(country)).to eq(
471
+ 'id' => 1, 'cities' => [
472
+ {'id' => 1, 'name' => '', 'description' => nil}
473
+ ]
474
+ )
475
+ end
476
+ end
477
+ end
478
+
479
+ context 'with ignore_blank: false option' do
480
+ before do
481
+ stub_index(:countries) do
482
+ index_scope Country
483
+ field :id
484
+ field :cities, ignore_blank: false do
485
+ field :id
486
+ field :name
487
+ field :historical_name
488
+ field :description
489
+ end
490
+ end
491
+ end
492
+
493
+ let(:country_with_cities) { Country.create!(id: 1) }
494
+
495
+ specify do
496
+ expect(CountriesIndex.root.compose(country_with_cities))
497
+ .to eq('id' => 1, 'cities' => [])
498
+ end
499
+ end
500
+
501
+ context 'without ignore_blank: true option' do
502
+ before do
503
+ stub_index(:countries) do
504
+ index_scope Country
505
+ field :id
506
+ field :cities do
507
+ field :id
508
+ field :name
509
+ field :historical_name
510
+ field :description
511
+ end
512
+ end
513
+ end
514
+
515
+ let(:country_with_cities) { Country.create!(id: 1) }
516
+
517
+ specify do
518
+ expect(CountriesIndex.root.compose(country_with_cities))
519
+ .to eq('id' => 1, 'cities' => [])
382
520
  end
383
- else
384
- Country.create!(id: 1, cities: cities)
385
521
  end
386
522
  end
387
523
 
524
+ context 'geo_point field type' do
525
+ context 'with ignore_blank: true option' do
526
+ before do
527
+ stub_index(:countries) do
528
+ index_scope Country
529
+ field :id
530
+ field :cities do
531
+ field :id
532
+ field :name
533
+ field :location, type: :geo_point, ignore_blank: true do
534
+ field :lat
535
+ field :lon
536
+ end
537
+ end
538
+ end
539
+ end
540
+
541
+ specify do
542
+ expect(
543
+ CountriesIndex.root.compose({
544
+ 'id' => 1,
545
+ 'cities' => [
546
+ {'id' => 1, 'name' => 'City1', 'location' => {}},
547
+ {'id' => 2, 'name' => 'City2', 'location' => {}}
548
+ ]
549
+ })
550
+ ).to eq(
551
+ 'id' => 1, 'cities' => [
552
+ {'id' => 1, 'name' => 'City1'},
553
+ {'id' => 2, 'name' => 'City2'}
554
+ ]
555
+ )
556
+ end
557
+ end
558
+
559
+ context 'without ignore_blank option' do
560
+ before do
561
+ stub_index(:countries) do
562
+ index_scope Country
563
+ field :id
564
+ field :cities do
565
+ field :id
566
+ field :name
567
+ field :location, type: :geo_point do
568
+ field :lat
569
+ field :lon
570
+ end
571
+ end
572
+ end
573
+ end
574
+
575
+ specify do
576
+ expect(
577
+ CountriesIndex.root.compose({
578
+ 'id' => 1,
579
+ 'cities' => [
580
+ {'id' => 1, 'name' => 'City1', 'location' => {}},
581
+ {'id' => 2, 'name' => 'City2', 'location' => {}}
582
+ ]
583
+ })
584
+ ).to eq(
585
+ 'id' => 1, 'cities' => [
586
+ {'id' => 1, 'name' => 'City1'},
587
+ {'id' => 2, 'name' => 'City2'}
588
+ ]
589
+ )
590
+ end
591
+ end
592
+
593
+ context 'with ignore_blank: false flag' do
594
+ before do
595
+ stub_index(:countries) do
596
+ index_scope Country
597
+ field :id
598
+ field :cities do
599
+ field :id
600
+ field :name
601
+ field :location, type: :geo_point, ignore_blank: false do
602
+ field :lat
603
+ field :lon
604
+ end
605
+ end
606
+ end
607
+ end
608
+
609
+ specify do
610
+ expect(
611
+ CountriesIndex.root.compose({
612
+ 'id' => 1,
613
+ 'cities' => [
614
+ {'id' => 1, 'location' => {}, 'name' => 'City1'},
615
+ {'id' => 2, 'location' => '', 'name' => 'City2'}
616
+ ]
617
+ })
618
+ ).to eq(
619
+ 'id' => 1, 'cities' => [
620
+ {'id' => 1, 'location' => {}, 'name' => 'City1'},
621
+ {'id' => 2, 'location' => '', 'name' => 'City2'}
622
+ ]
623
+ )
624
+ end
625
+ end
626
+ end
627
+ end
628
+
629
+ context 'objects and scopes', :orm do
630
+ before do
631
+ stub_model(:city)
632
+ stub_model(:country)
633
+
634
+ City.belongs_to :country
635
+ Country.has_many :cities, -> { order :id }
636
+
637
+ stub_index(:countries) do
638
+ index_scope Country
639
+ field :id
640
+ field :cities do
641
+ field :id
642
+ field :name
643
+ end
644
+ end
645
+ end
646
+
647
+ let(:country_with_cities) do
648
+ cities = [City.create!(id: 1, name: 'City1'), City.create!(id: 2, name: 'City2')]
649
+
650
+ Country.create!(id: 1, cities: cities)
651
+ end
652
+
388
653
  specify do
389
- expect(CountriesIndex::Country.root.compose(country_with_cities)).to eq('id' => 1, 'cities' => [
654
+ expect(CountriesIndex.root.compose(country_with_cities)).to eq('id' => 1, 'cities' => [
390
655
  {'id' => 1, 'name' => 'City1'}, {'id' => 2, 'name' => 'City2'}
391
656
  ])
392
657
  end
@@ -394,20 +659,19 @@ describe Chewy::Fields::Base do
394
659
  context 'nested object' do
395
660
  before do
396
661
  stub_index(:cities) do
397
- define_type City do
662
+ index_scope City
663
+ field :id
664
+ field :country do
398
665
  field :id
399
- field :country do
400
- field :id
401
- field :name
402
- end
666
+ field :name
403
667
  end
404
668
  end
405
669
  end
406
670
 
407
671
  specify do
408
- expect(CitiesIndex::City.root.compose(
409
- City.create!(id: 1, country: Country.create!(id: 1, name: 'Country'))
410
- )).to eq('id' => 1, 'country' => {'id' => 1, 'name' => 'Country'})
672
+ expect(
673
+ CitiesIndex.root.compose(City.create!(id: 1, country: Country.create!(id: 1, name: 'Country')))
674
+ ).to eq('id' => 1, 'country' => {'id' => 1, 'name' => 'Country'})
411
675
  end
412
676
  end
413
677
  end