chewy 7.1.0 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
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