chewy 6.0.0 → 7.6.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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -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/dependabot.yml +42 -0
  7. data/.github/workflows/ruby.yml +60 -0
  8. data/.rubocop.yml +16 -8
  9. data/.rubocop_todo.yml +110 -22
  10. data/CHANGELOG.md +396 -105
  11. data/CODE_OF_CONDUCT.md +14 -0
  12. data/CONTRIBUTING.md +63 -0
  13. data/Gemfile +4 -10
  14. data/Guardfile +3 -1
  15. data/README.md +497 -275
  16. data/chewy.gemspec +5 -20
  17. data/gemfiles/base.gemfile +12 -0
  18. data/gemfiles/rails.6.1.activerecord.gemfile +10 -15
  19. data/gemfiles/rails.7.0.activerecord.gemfile +14 -0
  20. data/gemfiles/rails.7.1.activerecord.gemfile +14 -0
  21. data/lib/chewy/config.rb +60 -52
  22. data/lib/chewy/elastic_client.rb +31 -0
  23. data/lib/chewy/errors.rb +7 -10
  24. data/lib/chewy/fields/base.rb +79 -13
  25. data/lib/chewy/fields/root.rb +4 -14
  26. data/lib/chewy/index/actions.rb +54 -37
  27. data/lib/chewy/{type → index}/adapter/active_record.rb +30 -6
  28. data/lib/chewy/{type → index}/adapter/base.rb +2 -3
  29. data/lib/chewy/{type → index}/adapter/object.rb +27 -31
  30. data/lib/chewy/{type → index}/adapter/orm.rb +17 -18
  31. data/lib/chewy/index/aliases.rb +14 -5
  32. data/lib/chewy/index/crutch.rb +40 -0
  33. data/lib/chewy/index/import/bulk_builder.rb +311 -0
  34. data/lib/chewy/{type → index}/import/bulk_request.rb +6 -7
  35. data/lib/chewy/{type → index}/import/journal_builder.rb +11 -12
  36. data/lib/chewy/{type → index}/import/routine.rb +18 -17
  37. data/lib/chewy/{type → index}/import.rb +76 -32
  38. data/lib/chewy/{type → index}/mapping.rb +29 -34
  39. data/lib/chewy/index/observe/active_record_methods.rb +87 -0
  40. data/lib/chewy/index/observe/callback.rb +34 -0
  41. data/lib/chewy/index/observe.rb +17 -0
  42. data/lib/chewy/index/specification.rb +1 -0
  43. data/lib/chewy/{type → index}/syncer.rb +59 -59
  44. data/lib/chewy/{type → index}/witchcraft.rb +11 -7
  45. data/lib/chewy/{type → index}/wrapper.rb +2 -2
  46. data/lib/chewy/index.rb +67 -94
  47. data/lib/chewy/journal.rb +25 -14
  48. data/lib/chewy/log_subscriber.rb +5 -1
  49. data/lib/chewy/minitest/helpers.rb +86 -13
  50. data/lib/chewy/minitest/search_index_receiver.rb +24 -26
  51. data/lib/chewy/railtie.rb +6 -20
  52. data/lib/chewy/rake_helper.rb +169 -113
  53. data/lib/chewy/rspec/build_query.rb +12 -0
  54. data/lib/chewy/rspec/helpers.rb +55 -0
  55. data/lib/chewy/rspec/update_index.rb +55 -44
  56. data/lib/chewy/rspec.rb +2 -0
  57. data/lib/chewy/runtime/version.rb +1 -1
  58. data/lib/chewy/runtime.rb +1 -1
  59. data/lib/chewy/search/loader.rb +19 -41
  60. data/lib/chewy/search/parameters/collapse.rb +16 -0
  61. data/lib/chewy/search/parameters/concerns/query_storage.rb +2 -2
  62. data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
  63. data/lib/chewy/search/parameters/indices.rb +13 -58
  64. data/lib/chewy/search/parameters/knn.rb +16 -0
  65. data/lib/chewy/search/parameters/order.rb +6 -19
  66. data/lib/chewy/search/parameters/source.rb +5 -1
  67. data/lib/chewy/search/parameters/storage.rb +1 -1
  68. data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
  69. data/lib/chewy/search/parameters.rb +6 -4
  70. data/lib/chewy/search/query_proxy.rb +9 -2
  71. data/lib/chewy/search/request.rb +169 -134
  72. data/lib/chewy/search/response.rb +5 -5
  73. data/lib/chewy/search/scoping.rb +7 -8
  74. data/lib/chewy/search/scrolling.rb +13 -13
  75. data/lib/chewy/search.rb +9 -19
  76. data/lib/chewy/stash.rb +19 -30
  77. data/lib/chewy/strategy/active_job.rb +1 -1
  78. data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
  79. data/lib/chewy/strategy/base.rb +10 -0
  80. data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +168 -0
  81. data/lib/chewy/strategy/delayed_sidekiq/worker.rb +76 -0
  82. data/lib/chewy/strategy/delayed_sidekiq.rb +30 -0
  83. data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
  84. data/lib/chewy/strategy/sidekiq.rb +2 -1
  85. data/lib/chewy/strategy.rb +6 -19
  86. data/lib/chewy/version.rb +1 -1
  87. data/lib/chewy.rb +39 -86
  88. data/lib/generators/chewy/install_generator.rb +1 -1
  89. data/lib/tasks/chewy.rake +36 -32
  90. data/migration_guide.md +46 -8
  91. data/spec/chewy/config_spec.rb +16 -41
  92. data/spec/chewy/elastic_client_spec.rb +26 -0
  93. data/spec/chewy/fields/base_spec.rb +432 -147
  94. data/spec/chewy/fields/root_spec.rb +20 -28
  95. data/spec/chewy/fields/time_fields_spec.rb +5 -5
  96. data/spec/chewy/index/actions_spec.rb +368 -59
  97. data/spec/chewy/{type → index}/adapter/active_record_spec.rb +156 -40
  98. data/spec/chewy/{type → index}/adapter/object_spec.rb +21 -6
  99. data/spec/chewy/index/aliases_spec.rb +3 -3
  100. data/spec/chewy/index/import/bulk_builder_spec.rb +494 -0
  101. data/spec/chewy/{type → index}/import/bulk_request_spec.rb +5 -12
  102. data/spec/chewy/{type → index}/import/journal_builder_spec.rb +9 -19
  103. data/spec/chewy/{type → index}/import/routine_spec.rb +19 -19
  104. data/spec/chewy/{type → index}/import_spec.rb +164 -98
  105. data/spec/chewy/index/mapping_spec.rb +135 -0
  106. data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
  107. data/spec/chewy/index/observe/callback_spec.rb +139 -0
  108. data/spec/chewy/index/observe_spec.rb +143 -0
  109. data/spec/chewy/index/settings_spec.rb +3 -1
  110. data/spec/chewy/index/specification_spec.rb +20 -30
  111. data/spec/chewy/{type → index}/syncer_spec.rb +14 -19
  112. data/spec/chewy/{type → index}/witchcraft_spec.rb +20 -22
  113. data/spec/chewy/index/wrapper_spec.rb +100 -0
  114. data/spec/chewy/index_spec.rb +60 -105
  115. data/spec/chewy/journal_spec.rb +25 -74
  116. data/spec/chewy/minitest/helpers_spec.rb +123 -15
  117. data/spec/chewy/minitest/search_index_receiver_spec.rb +28 -30
  118. data/spec/chewy/multi_search_spec.rb +4 -5
  119. data/spec/chewy/rake_helper_spec.rb +315 -55
  120. data/spec/chewy/rspec/build_query_spec.rb +34 -0
  121. data/spec/chewy/rspec/helpers_spec.rb +61 -0
  122. data/spec/chewy/rspec/update_index_spec.rb +74 -71
  123. data/spec/chewy/runtime_spec.rb +2 -2
  124. data/spec/chewy/search/loader_spec.rb +19 -53
  125. data/spec/chewy/search/pagination/kaminari_examples.rb +4 -6
  126. data/spec/chewy/search/pagination/kaminari_spec.rb +2 -2
  127. data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
  128. data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
  129. data/spec/chewy/search/parameters/indices_spec.rb +26 -117
  130. data/spec/chewy/search/parameters/knn_spec.rb +5 -0
  131. data/spec/chewy/search/parameters/order_spec.rb +18 -11
  132. data/spec/chewy/search/parameters/query_storage_examples.rb +67 -21
  133. data/spec/chewy/search/parameters/search_after_spec.rb +4 -1
  134. data/spec/chewy/search/parameters/source_spec.rb +8 -2
  135. data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
  136. data/spec/chewy/search/parameters_spec.rb +18 -4
  137. data/spec/chewy/search/query_proxy_spec.rb +68 -17
  138. data/spec/chewy/search/request_spec.rb +292 -110
  139. data/spec/chewy/search/response_spec.rb +12 -12
  140. data/spec/chewy/search/scrolling_spec.rb +10 -17
  141. data/spec/chewy/search_spec.rb +40 -34
  142. data/spec/chewy/stash_spec.rb +9 -21
  143. data/spec/chewy/strategy/active_job_spec.rb +16 -16
  144. data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
  145. data/spec/chewy/strategy/atomic_spec.rb +9 -10
  146. data/spec/chewy/strategy/delayed_sidekiq_spec.rb +208 -0
  147. data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
  148. data/spec/chewy/strategy/sidekiq_spec.rb +12 -12
  149. data/spec/chewy/strategy_spec.rb +19 -15
  150. data/spec/chewy_spec.rb +24 -107
  151. data/spec/spec_helper.rb +3 -22
  152. data/spec/support/active_record.rb +25 -7
  153. metadata +78 -339
  154. data/.circleci/config.yml +0 -240
  155. data/Appraisals +0 -81
  156. data/gemfiles/rails.5.2.activerecord.gemfile +0 -17
  157. data/gemfiles/rails.5.2.mongoid.6.4.gemfile +0 -17
  158. data/gemfiles/rails.6.0.activerecord.gemfile +0 -17
  159. data/gemfiles/sequel.4.45.gemfile +0 -11
  160. data/lib/chewy/backports/deep_dup.rb +0 -46
  161. data/lib/chewy/backports/duplicable.rb +0 -91
  162. data/lib/chewy/search/pagination/will_paginate.rb +0 -43
  163. data/lib/chewy/search/parameters/types.rb +0 -20
  164. data/lib/chewy/strategy/resque.rb +0 -27
  165. data/lib/chewy/strategy/shoryuken.rb +0 -40
  166. data/lib/chewy/type/actions.rb +0 -43
  167. data/lib/chewy/type/adapter/mongoid.rb +0 -67
  168. data/lib/chewy/type/adapter/sequel.rb +0 -93
  169. data/lib/chewy/type/crutch.rb +0 -32
  170. data/lib/chewy/type/import/bulk_builder.rb +0 -122
  171. data/lib/chewy/type/observe.rb +0 -82
  172. data/lib/chewy/type.rb +0 -120
  173. data/lib/sequel/plugins/chewy_observe.rb +0 -63
  174. data/spec/chewy/search/pagination/will_paginate_examples.rb +0 -63
  175. data/spec/chewy/search/pagination/will_paginate_spec.rb +0 -23
  176. data/spec/chewy/search/parameters/types_spec.rb +0 -5
  177. data/spec/chewy/strategy/resque_spec.rb +0 -46
  178. data/spec/chewy/strategy/shoryuken_spec.rb +0 -70
  179. data/spec/chewy/type/actions_spec.rb +0 -50
  180. data/spec/chewy/type/adapter/mongoid_spec.rb +0 -372
  181. data/spec/chewy/type/adapter/sequel_spec.rb +0 -472
  182. data/spec/chewy/type/import/bulk_builder_spec.rb +0 -194
  183. data/spec/chewy/type/mapping_spec.rb +0 -175
  184. data/spec/chewy/type/observe_spec.rb +0 -137
  185. data/spec/chewy/type/wrapper_spec.rb +0 -100
  186. data/spec/chewy/type_spec.rb +0 -55
  187. data/spec/support/mongoid.rb +0 -93
  188. data/spec/support/sequel.rb +0 -80
@@ -1,43 +0,0 @@
1
- module Chewy
2
- class Type
3
- module Actions
4
- extend ActiveSupport::Concern
5
-
6
- module ClassMethods
7
- # Deletes all documents of a type and reimports them
8
- #
9
- # @example
10
- # UsersIndex::User.reset
11
- #
12
- # @see Chewy::Type::Import::ClassMethods#import
13
- # @see Chewy::Type::Import::ClassMethods#import
14
- # @return [true, false] the result of import
15
- def reset
16
- delete_all
17
- import
18
- end
19
-
20
- # Performs missing and outdated objects synchronization for the current type.
21
- #
22
- # @example
23
- # UsersIndex::User.sync
24
- #
25
- # @see Chewy::Type::Syncer
26
- # @param parallel [true, Integer, Hash] options for parallel execution or the number of processes
27
- # @return [Hash{Symbol, Object}, nil] a number of missing and outdated documents reindexed and their ids, nil in case of errors
28
- def sync(parallel: nil)
29
- syncer = Syncer.new(self, parallel: parallel)
30
- count = syncer.perform
31
- {count: count, missing: syncer.missing_ids, outdated: syncer.outdated_ids} if count
32
- end
33
-
34
- # A {Chewy::Journal} instance for the particular type
35
- #
36
- # @return [Chewy::Journal] journal instance
37
- def journal
38
- @journal ||= Chewy::Journal.new(self)
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,67 +0,0 @@
1
- require 'chewy/type/adapter/orm'
2
-
3
- module Chewy
4
- class Type
5
- module Adapter
6
- class Mongoid < Orm
7
- def self.accepts?(target)
8
- defined?(::Mongoid::Document) && (
9
- target.is_a?(Class) && target.ancestors.include?(::Mongoid::Document) ||
10
- target.is_a?(::Mongoid::Criteria))
11
- end
12
-
13
- def identify(collection)
14
- super(collection).map { |id| id.is_a?(BSON::ObjectId) ? id.to_s : id }
15
- end
16
-
17
- private
18
-
19
- def cleanup_default_scope!
20
- Chewy.logger.warn('Default type scope order, limit and offset are ignored and will be nullified') if Chewy.logger && @default_scope.options.values_at(:sort, :limit, :skip).compact.present?
21
-
22
- @default_scope.options.delete(:limit)
23
- @default_scope.options.delete(:skip)
24
- @default_scope = @default_scope.reorder(nil)
25
- end
26
-
27
- def import_scope(scope, options)
28
- pluck_in_batches(scope, **options.slice(:batch_size)).map do |ids|
29
- yield grouped_objects(default_scope_where_ids_in(ids))
30
- end.all?
31
- end
32
-
33
- def primary_key
34
- :_id
35
- end
36
-
37
- def pluck(scope, fields: [])
38
- scope.pluck(primary_key, *fields)
39
- end
40
-
41
- def pluck_in_batches(scope, fields: [], batch_size: nil, **options)
42
- return enum_for(:pluck_in_batches, scope, fields: fields, batch_size: batch_size, **options) unless block_given?
43
-
44
- scope.batch_size(batch_size).no_timeout.pluck(primary_key, *fields).each_slice(batch_size) do |batch|
45
- yield batch
46
- end
47
- end
48
-
49
- def scope_where_ids_in(scope, ids)
50
- scope.where(primary_key.in => ids)
51
- end
52
-
53
- def all_scope
54
- target.all
55
- end
56
-
57
- def relation_class
58
- ::Mongoid::Criteria
59
- end
60
-
61
- def object_class
62
- ::Mongoid::Document
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,93 +0,0 @@
1
- require 'chewy/type/adapter/base'
2
-
3
- module Chewy
4
- class Type
5
- module Adapter
6
- class Sequel < Orm
7
- attr_reader :default_scope
8
- alias_method :default_dataset, :default_scope
9
-
10
- def self.accepts?(target)
11
- defined?(::Sequel::Model) && (
12
- target.is_a?(Class) && target < ::Sequel::Model ||
13
- target.is_a?(::Sequel::Dataset))
14
- end
15
-
16
- private
17
-
18
- def cleanup_default_scope!
19
- Chewy.logger.warn('Default type scope order, limit and offset are ignored and will be nullified') if Chewy.logger && @default_scope != @default_scope.unordered.unlimited
20
-
21
- @default_scope = @default_scope.unordered.unlimited
22
- end
23
-
24
- def import_scope(scope, options)
25
- pluck_in_batches(scope, **options.slice(:batch_size)).inject(true) do |result, ids|
26
- result & yield(grouped_objects(default_scope_where_ids_in(ids).all))
27
- end
28
- end
29
-
30
- def primary_key
31
- target.primary_key
32
- end
33
-
34
- def full_column_name(column)
35
- ::Sequel.qualify(target.table_name, column)
36
- end
37
-
38
- def all_scope
39
- target.dataset
40
- end
41
-
42
- def target_columns
43
- @target_columns ||= target.columns.to_set
44
- end
45
-
46
- def pluck(scope, fields: [])
47
- fields = fields.map(&:to_sym).unshift(primary_key).map do |column|
48
- target_columns.include?(column) ? full_column_name(column) : column
49
- end
50
- scope.distinct.select_map(fields.one? ? fields.first : fields)
51
- end
52
-
53
- def pluck_in_batches(scope, fields: [], batch_size: nil, **options)
54
- return enum_for(:pluck_in_batches, scope, fields: fields, batch_size: batch_size, **options) unless block_given?
55
-
56
- scope = scope.unordered.order(full_column_name(primary_key).asc).limit(batch_size)
57
-
58
- ids = pluck(scope, fields: fields)
59
- count = 0
60
-
61
- while ids.present?
62
- yield ids
63
- break if ids.size < batch_size
64
- last_id = ids.last.is_a?(Array) ? ids.last.first : ids.last
65
- ids = pluck(scope.where { |_o| full_column_name(primary_key) > last_id }, fields: fields)
66
- end
67
-
68
- count
69
- end
70
-
71
- def scope_where_ids_in(scope, ids)
72
- scope.where(full_column_name(primary_key) => Array.wrap(ids))
73
- end
74
-
75
- def model_of_relation(relation)
76
- relation.model
77
- end
78
-
79
- def relation_class
80
- ::Sequel::Dataset
81
- end
82
-
83
- def object_class
84
- ::Sequel::Model
85
- end
86
-
87
- def load_scope_objects(*args)
88
- super.all
89
- end
90
- end
91
- end
92
- end
93
- end
@@ -1,32 +0,0 @@
1
- module Chewy
2
- class Type
3
- module Crutch
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- class_attribute :_crutches
8
- self._crutches = {}
9
- end
10
-
11
- class Crutches
12
- def initialize(type, collection)
13
- @type = type
14
- @collection = collection
15
- @type._crutches.each_key do |name|
16
- singleton_class.class_eval <<-METHOD, __FILE__, __LINE__ + 1
17
- def #{name}
18
- @#{name} ||= @type._crutches[:#{name}].call @collection
19
- end
20
- METHOD
21
- end
22
- end
23
- end
24
-
25
- module ClassMethods
26
- def crutch(name, &block)
27
- self._crutches = _crutches.merge(name.to_sym => block)
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,122 +0,0 @@
1
- module Chewy
2
- class Type
3
- module Import
4
- # This class purpose is to build ES client-acceptable bulk
5
- # request body from the passed objects for index and deletion.
6
- # It handles parent-child relationships as well by fetching
7
- # existing documents from ES, taking their `_parent` field and
8
- # using it in the bulk body.
9
- # If fields are passed - it creates partial update entries except for
10
- # the cases when the type has parent and parent_id has been changed.
11
- class BulkBuilder
12
- # @param type [Chewy::Type] desired type
13
- # @param index [Array<Object>] objects to index
14
- # @param delete [Array<Object>] objects or ids to delete
15
- # @param fields [Array<Symbol, String>] and array of fields for documents update
16
- def initialize(type, index: [], delete: [], fields: [])
17
- @type = type
18
- @index = index
19
- @delete = delete
20
- @fields = fields.map!(&:to_sym)
21
- end
22
-
23
- # Returns ES API-ready bulk requiest body.
24
- # @see https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-api/lib/elasticsearch/api/actions/bulk.rb
25
- # @return [Array<Hash>] bulk body
26
- def bulk_body
27
- @bulk_body ||= @index.flat_map(&method(:index_entry)).concat(
28
- @delete.flat_map(&method(:delete_entry))
29
- )
30
- end
31
-
32
- # The only purpose of this method is to cache document ids for
33
- # all the passed object for index to avoid ids recalculation.
34
- #
35
- # @return [Hash[String => Object]] an ids-objects index hash
36
- def index_objects_by_id
37
- @index_objects_by_id ||= index_object_ids.invert.stringify_keys!
38
- end
39
-
40
- private
41
-
42
- def crutches
43
- @crutches ||= Chewy::Type::Crutch::Crutches.new @type, @index
44
- end
45
-
46
- def parents
47
- return unless type_root.parent_id
48
-
49
- @parents ||= begin
50
- ids = @index.map do |object|
51
- object.respond_to?(:id) ? object.id : object
52
- end
53
- ids.concat(@delete.map do |object|
54
- object.respond_to?(:id) ? object.id : object
55
- end)
56
- @type.filter(ids: {values: ids}).order('_doc').pluck(:_id, :_parent).to_h
57
- end
58
- end
59
-
60
- def index_entry(object)
61
- entry = {}
62
- entry[:_id] = index_object_ids[object] if index_object_ids[object]
63
-
64
- if parents
65
- entry[:parent] = type_root.compose_parent(object)
66
- parent = entry[:_id].present? && parents[entry[:_id].to_s]
67
- end
68
-
69
- if parent && entry[:parent].to_s != parent
70
- entry[:data] = @type.compose(object, crutches)
71
- [{delete: entry.except(:data).merge(parent: parent)}, {index: entry}]
72
- elsif @fields.present?
73
- return [] unless entry[:_id]
74
- entry[:data] = {doc: @type.compose(object, crutches, fields: @fields)}
75
- [{update: entry}]
76
- else
77
- entry[:data] = @type.compose(object, crutches)
78
- [{index: entry}]
79
- end
80
- end
81
-
82
- def delete_entry(object)
83
- entry = {}
84
- entry[:_id] = entry_id(object)
85
- entry[:_id] ||= object.as_json
86
-
87
- return [] if entry[:_id].blank?
88
-
89
- if parents
90
- parent = entry[:_id].present? && parents[entry[:_id].to_s]
91
- return [] unless parent
92
- entry[:parent] = parent
93
- end
94
-
95
- [{delete: entry}]
96
- end
97
-
98
- def entry_id(object)
99
- if type_root.id
100
- type_root.compose_id(object)
101
- else
102
- id = object.id if object.respond_to?(:id)
103
- id ||= object[:id] || object['id'] if object.is_a?(Hash)
104
- id = id.to_s if defined?(BSON) && id.is_a?(BSON::ObjectId)
105
- id
106
- end
107
- end
108
-
109
- def index_object_ids
110
- @index_object_ids ||= @index.each_with_object({}) do |object, result|
111
- id = entry_id(object)
112
- result[object] = id if id.present?
113
- end
114
- end
115
-
116
- def type_root
117
- @type_root ||= @type.root
118
- end
119
- end
120
- end
121
- end
122
- end
@@ -1,82 +0,0 @@
1
- module Chewy
2
- class Type
3
- module Observe
4
- extend ActiveSupport::Concern
5
-
6
- module Helpers
7
- def update_proc(type_name, *args, &block)
8
- options = args.extract_options!
9
- method = args.first
10
-
11
- proc do
12
- reference = if type_name.is_a?(Proc)
13
- if type_name.arity.zero?
14
- instance_exec(&type_name)
15
- else
16
- type_name.call(self)
17
- end
18
- else
19
- type_name
20
- end
21
-
22
- type = Chewy.derive_type(reference)
23
-
24
- next if Chewy.strategy.current.name == :bypass
25
-
26
- backreference = if method && method.to_s == 'self'
27
- self
28
- elsif method
29
- send(method)
30
- else
31
- instance_eval(&block)
32
- end
33
-
34
- type.update_index(backreference, options)
35
- end
36
- end
37
-
38
- def extract_callback_options!(args)
39
- options = args.extract_options!
40
- result = options.each_key.with_object({}) do |key, hash|
41
- hash[key] = options.delete(key) if %i[if unless].include?(key)
42
- end
43
- args.push(options) unless options.empty?
44
- result
45
- end
46
- end
47
-
48
- extend Helpers
49
-
50
- module MongoidMethods
51
- def update_index(type_name, *args, &block)
52
- callback_options = Observe.extract_callback_options!(args)
53
- update_proc = Observe.update_proc(type_name, *args, &block)
54
-
55
- after_save(callback_options, &update_proc)
56
- after_destroy(callback_options, &update_proc)
57
- end
58
- end
59
-
60
- module ActiveRecordMethods
61
- def update_index(type_name, *args, &block)
62
- callback_options = Observe.extract_callback_options!(args)
63
- update_proc = Observe.update_proc(type_name, *args, &block)
64
-
65
- if Chewy.use_after_commit_callbacks
66
- after_commit(**callback_options, &update_proc)
67
- else
68
- after_save(**callback_options, &update_proc)
69
- after_destroy(**callback_options, &update_proc)
70
- end
71
- end
72
- end
73
-
74
- module ClassMethods
75
- def update_index(objects, options = {})
76
- Chewy.strategy.current.update(self, objects, options)
77
- true
78
- end
79
- end
80
- end
81
- end
82
- end
data/lib/chewy/type.rb DELETED
@@ -1,120 +0,0 @@
1
- require 'chewy/search'
2
- require 'chewy/type/adapter/object'
3
- require 'chewy/type/adapter/active_record'
4
- require 'chewy/type/adapter/mongoid'
5
- require 'chewy/type/adapter/sequel'
6
- require 'chewy/type/mapping'
7
- require 'chewy/type/wrapper'
8
- require 'chewy/type/observe'
9
- require 'chewy/type/actions'
10
- require 'chewy/type/syncer'
11
- require 'chewy/type/crutch'
12
- require 'chewy/type/import'
13
- require 'chewy/type/witchcraft'
14
-
15
- module Chewy
16
- class Type
17
- IMPORT_OPTIONS_KEYS = %i[
18
- batch_size bulk_size consistency direct_import journal
19
- pipeline raw_import refresh replication
20
- ].freeze
21
-
22
- include Search
23
- include Mapping
24
- include Wrapper
25
- include Observe
26
- include Actions
27
- include Crutch
28
- include Witchcraft
29
- include Import
30
-
31
- singleton_class.delegate :index_name, :derivable_index_name, :client, to: :index
32
-
33
- class_attribute :_default_import_options
34
- self._default_import_options = {}
35
-
36
- class << self
37
- # Chewy index current type belongs to. Defined inside `Chewy.create_type`
38
- #
39
- def index
40
- raise NotImplementedError, 'Looks like this type was defined outside the index scope and `.index` method is undefined for it'
41
- end
42
-
43
- # Current type adapter. Defined inside `Chewy.create_type`, derived from
44
- # `Chewy::Index.define_type` arguments.
45
- #
46
- def adapter
47
- raise NotImplementedError
48
- end
49
-
50
- # Returns type name string
51
- #
52
- def type_name
53
- adapter.type_name
54
- end
55
-
56
- # Appends type name to {Chewy::Index.derivable_name}
57
- #
58
- # @example
59
- # class Namespace::UsersIndex < Chewy::Index
60
- # define_type User
61
- # end
62
- # UsersIndex::User.derivable_name # => 'namespace/users#user'
63
- #
64
- # @see Chewy::Index.derivable_name
65
- # @return [String, nil] derivable name or nil when it is impossible to calculate
66
- def derivable_name
67
- @derivable_name ||= [index.derivable_name, type_name].join('#') if index && index.derivable_name
68
- end
69
-
70
- # This method is an API shared with {Chewy::Index}, added for convenience.
71
- #
72
- # @return [Chewy::Type] array containing itself
73
- def types
74
- [self]
75
- end
76
-
77
- # Returns list of public class methods defined in current type
78
- #
79
- def scopes
80
- public_methods - Chewy::Type.public_methods
81
- end
82
-
83
- def default_import_options(params)
84
- params.assert_valid_keys(IMPORT_OPTIONS_KEYS)
85
- self._default_import_options = _default_import_options.merge(params)
86
- end
87
-
88
- def method_missing(method, *args, &block)
89
- if index.scopes.include?(method)
90
- define_singleton_method method do |*method_args, &method_block|
91
- all.scoping { index.public_send(method, *method_args, &method_block) }
92
- end
93
- send(method, *args, &block)
94
- else
95
- super
96
- end
97
- end
98
-
99
- def respond_to_missing?(method, _)
100
- index.scopes.include?(method) || super
101
- end
102
-
103
- def const_missing(name)
104
- to_resolve = "#{self}::#{name}"
105
- to_resolve[index.to_s] = ''
106
-
107
- @__resolved_constants ||= {}
108
-
109
- if to_resolve.empty? || @__resolved_constants[to_resolve]
110
- super
111
- else
112
- @__resolved_constants[to_resolve] = true
113
- to_resolve.constantize
114
- end
115
- rescue NotImplementedError
116
- super
117
- end
118
- end
119
- end
120
- end
@@ -1,63 +0,0 @@
1
- require 'active_support/callbacks'
2
-
3
- module Sequel
4
- module Plugins
5
- # This Sequel plugin adds support for chewy's model-observing hook for
6
- # updating indexes after model save or destroy.
7
- #
8
- # Usage:
9
- #
10
- # # Make all model subclasses support the `update_index` hook (called
11
- # # before loading subclasses).
12
- # Sequel::Model.plugin :chewy_observe
13
- #
14
- # # Make the Album class support the `update_index` hooks.
15
- # Album.plugin :chewy_observe
16
- #
17
- # # Declare one or more `update_index` observers in model.
18
- # class Album < Sequel::Model
19
- # update_index('albums#album') { self }
20
- # end
21
- #
22
- module ChewyObserve
23
- extend ::Chewy::Type::Observe::Helpers
24
-
25
- def self.apply(model)
26
- model.instance_eval do
27
- include ActiveSupport::Callbacks
28
- define_callbacks :commit, :destroy_commit, :save, :destroy
29
- end
30
- end
31
-
32
- # Class level methods for Sequel::Model
33
- #
34
- module ClassMethods
35
- def update_index(type_name, *args, &block)
36
- callback_options = ChewyObserve.extract_callback_options!(args)
37
- update_proc = ChewyObserve.update_proc(type_name, *args, &block)
38
-
39
- set_callback(:save, callback_options, &update_proc)
40
- set_callback(:destroy, callback_options, &update_proc)
41
- end
42
- end
43
-
44
- # Instance level methods for Sequel::Model
45
- #
46
- module InstanceMethods
47
- def after_save
48
- run_callbacks(:save) do
49
- super
50
- db.after_commit {} if Chewy.use_after_commit_callbacks
51
- end
52
- end
53
-
54
- def after_destroy
55
- run_callbacks(:destroy) do
56
- super
57
- db.after_commit {} if Chewy.use_after_commit_callbacks
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end
@@ -1,63 +0,0 @@
1
- require 'spec_helper'
2
-
3
- shared_examples :will_paginate do |request_base_class|
4
- before { Chewy.massacre }
5
-
6
- before do
7
- stub_index(:products) do
8
- define_type(:product) do
9
- field :name
10
- field :age, type: 'integer'
11
- end
12
- end
13
- end
14
-
15
- let(:except_fields) { %w[_score _explanation] }
16
- let(:request_class) do
17
- Class.new(request_base_class).tap do |k|
18
- k.include Chewy::Search::Pagination::WillPaginate
19
- end
20
- end
21
- let(:search) { request_class.new(ProductsIndex).order(:age) }
22
-
23
- specify { expect(search.total_pages).to eq(1) } # defaults to 1 on will_paginate
24
-
25
- context do
26
- let(:data) { Array.new(10) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
27
-
28
- before { ProductsIndex::Product.import!(data.map { |h| double(h) }) }
29
- before { allow(::WillPaginate).to receive_messages(per_page: 3) }
30
-
31
- describe '#page' do
32
- specify { expect(search.map { |e| e.attributes.except(*except_fields) }).to match_array(data) }
33
- specify { expect(search.page(1).map { |e| e.attributes.except(*except_fields) }).to eq(data[0..2]) }
34
- specify { expect(search.page(2).map { |e| e.attributes.except(*except_fields) }).to eq(data[3..5]) }
35
- end
36
-
37
- describe '#paginate' do
38
- specify { expect(search.paginate(page: 2, per_page: 4).map { |e| e.attributes.except(*except_fields) }).to eq(data[4..7]) }
39
- specify { expect(search.paginate(per_page: 2, page: 3).page(3).map { |e| e.attributes.except(*except_fields) }).to eq(data[4..5]) }
40
- specify { expect(search.paginate(per_page: 5).map { |e| e.attributes.except(*except_fields) }).to eq(data[0..4]) }
41
- specify { expect(search.paginate(per_page: 4).map { |e| e.attributes.except(*except_fields) }).to eq(data[0..3]) }
42
- end
43
-
44
- describe '#total_pages' do
45
- specify { expect(search.paginate(page: 2, per_page: 5).total_pages).to eq(2) }
46
- specify { expect(search.paginate(page: 3, per_page: 2).total_pages).to eq(5) }
47
- end
48
-
49
- describe '#total_entries' do
50
- specify { expect(search.paginate(page: 1, per_page: 4).total_entries).to eq(10) }
51
- specify { expect(search.query(range: {age: {gt: 20}}).limit(3).total_entries).to eq(8) }
52
- end
53
-
54
- describe '#load' do
55
- specify { expect(search.paginate(per_page: 2, page: 1).load.first.age).to eq(10) }
56
- specify { expect(search.paginate(per_page: 2, page: 3).load.first.age).to eq(50) }
57
- specify { expect(search.paginate(per_page: 2, page: 3).load.page(2).load.first.age).to eq(30) }
58
-
59
- specify { expect(search.paginate(per_page: 4, page: 1).load.total_count).to eq(10) }
60
- specify { expect(search.paginate(per_page: 2, page: 3).load.total_pages).to eq(5) }
61
- end
62
- end
63
- end