chewy 8.0.1 → 8.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36ec23aabab2b6b62d9425ad6505f5589958400f21411c90d9808113336aac35
4
- data.tar.gz: 0b082e6327fa4092477c7888714662f791a940b9a1045047ce7a183d41656f2c
3
+ metadata.gz: 702dbe73f6b523156348491049ed540490d755d66c3242a2422c4da92090346c
4
+ data.tar.gz: 730beaf500e9d4feeafc5033bd028abc67662f9ae3299a95aa5d3f323f6ee011
5
5
  SHA512:
6
- metadata.gz: ad839b902eeb12a5f94547e21f0ed9b2d078be1869b0070be7698fd357edde8d6d759502301dd823eb53d8a3fa42bd6d5584cee53dd482df6c05d1b46e2d110a
7
- data.tar.gz: 3fdef90ea2c73f41274556d5f001e7931bcbbb9b2bdd02df6204fbd75c5b8139ed2cbff00cd448922e8215d2f35f6f3695bd52505db2f30cd20968b54732c401
6
+ metadata.gz: 87105fad14e8959fabbad411e3b22af813b6261c0cff1a45f14205819e5564f03d936acf6fcd909ac152d98325c2619628c3732fa1eb628d6edb42a5b9670a95
7
+ data.tar.gz: b9f693b6e4180e9c2023456439e1de74c86dff2a453f50be369c50f30f7b419eab75173f2071707cf922457dc69fb9fc26cf0e82ad79034e3eea79212091a791
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## master (unreleased)
4
+
5
+ ### New Features
6
+
7
+ ### Bug Fixes
8
+
9
+ ### Changes
10
+
11
+ ## 8.1.0 (2026-05-28)
12
+
13
+ ### New Features
14
+
15
+ * [#887](https://github.com/toptal/chewy/pull/887): Add support [runtime_mappings](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html). ([@TakuyaKurimoto](https://github.com/TakuyaKurimoto))
16
+ * [#996](https://github.com/toptal/chewy/pull/996): Add `context:` option to `import`/`import!` for passing custom data to crutch blocks and field value procs without redundant DB queries.
17
+
18
+ ### Bug Fixes
19
+
20
+ * [#878](https://github.com/toptal/chewy/issues/878): Flatten `Chewy::Stash::Journal.for` to use a single `terms` filter instead of a chain of `bool.should` clauses. Fixes Elasticsearch `indices.query.bool.max_nested_depth` errors when applying or cleaning the journal across many indices.
21
+
22
+ ### Changes
23
+
24
+ * [#916](https://github.com/toptal/chewy/pull/916): Raise error in #scroll_batches when search backend returns a failure. ([@tomdev][])
25
+ * [#1008](https://github.com/toptal/chewy/pull/1008): Promote Elasticsearch to a native GitHub Actions service with a health-check gate, replacing the fragile `docker compose` + `sleep 15` approach. ([@mattmenefee][])
26
+ * [#1010](https://github.com/toptal/chewy/pull/1010): Add Chewy 7/ES 7 to Chewy 8/ES 8 migration guide and fix stale `Elasticsearch::Transport` namespace references in docs. ([@mattmenefee][])
27
+ * [#1011](https://github.com/toptal/chewy/pull/1011): Replace deprecated `Sidekiq::Testing` API with new `Sidekiq 8.1+` testing API and silence Sidekiq logger during spec runs. ([@mattmenefee][], [@mjankowski][])
28
+ * [#1013](https://github.com/toptal/chewy/pull/1013): Fix `drop_indices` test helper to use `format: 'json'` for ES version portability. If you define a custom `drop_indices` helper in your test suite, update it to use `Chewy.client.cat.indices(format: 'json')` instead of parsing the text-format response. ([@mattmenefee][])
29
+ * [#1014](https://github.com/toptal/chewy/pull/1014): Improve contributing documentation with development setup instructions, PR workflow, and grammar fixes. ([@mattmenefee][])
30
+
3
31
  ## 8.0.1 (2026-03-12)
4
32
 
5
33
  ### New Features
@@ -874,10 +902,12 @@
874
902
  [@marshall]: https://github.com/marshall
875
903
  [@matchbookmac]: https://github.com/matchbookmac
876
904
  [@matthee]: https://github.com/matthee
905
+ [@mattmenefee]: https://github.com/mattmenefee
877
906
  [@mattzollinhofer]: https://github.com/mattzollinhofer
878
907
  [@menglewis]: https://github.com/menglewis
879
908
  [@mikeyhogarth]: https://github.com/mikeyhogarth
880
909
  [@milk1000cc]: https://github.com/milk1000cc
910
+ [@mjankowski]: https://github.com/mjankowski
881
911
  [@mkcode]: https://github.com/mkcode
882
912
  [@mpeychich]: https://github.com/mpeychich
883
913
  [@mrbrdo]: https://github.com/mrbrdo
@@ -901,6 +931,7 @@
901
931
  [@socialchorus]: https://github.com/socialchorus
902
932
  [@taylor-au]: https://github.com/taylor-au
903
933
  [@TikiTDO]: https://github.com/TikiTDO
934
+ [@tomdev]: https://github.com/tomdev
904
935
  [@undr]: https://github.com/undr
905
936
  [@Vitalina-Vakulchyk]: https://github.com/Vitalina-Vakulchyk
906
937
  [@webgago]: https://github.com/webgago
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/chewy.svg)](http://badge.fury.io/rb/chewy)
2
2
  [![GitHub Actions](https://github.com/toptal/chewy/actions/workflows/ruby.yml/badge.svg)](https://github.com/toptal/chewy/actions/workflows/ruby.yml)
3
- [![Code Climate](https://codeclimate.com/github/toptal/chewy.svg)](https://codeclimate.com/github/toptal/chewy)
4
- [![Inline docs](http://inch-ci.org/github/toptal/chewy.svg?branch=master)](http://inch-ci.org/github/toptal/chewy)
5
3
 
6
4
  # Chewy
7
5
 
@@ -47,7 +45,7 @@ Chewy aims to support all Ruby and Rails versions that are currently maintained
47
45
 
48
46
  ### Ruby
49
47
 
50
- Chewy is compatible with MRI 3.2-3.4.
48
+ Chewy is compatible with MRI 3.2-4.0.
51
49
 
52
50
  ### Elasticsearch compatibility matrix
53
51
 
@@ -274,19 +272,7 @@ Use the standard client settings with your Cloud credentials (API key or user/pa
274
272
 
275
273
  ## Contributing
276
274
 
277
- 1. Fork it (http://github.com/toptal/chewy/fork)
278
- 2. Create your feature branch (`git checkout -b my-new-feature`)
279
- 3. Implement your changes, cover it with specs and make sure old specs are passing
280
- 4. Commit your changes (`git commit -am 'Add some feature'`)
281
- 5. Push to the branch (`git push origin my-new-feature`)
282
- 6. Create new Pull Request
283
-
284
- Use the following Rake tasks to control the Elasticsearch cluster while developing, if you prefer native Elasticsearch installation over the dockerized one:
285
-
286
- ```bash
287
- rake elasticsearch:start # start Elasticsearch cluster on 9250 port for tests
288
- rake elasticsearch:stop # stop Elasticsearch
289
- ```
275
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup instructions, how to run tests, and the pull request workflow.
290
276
 
291
277
  ## Copyright
292
278
 
data/lib/chewy/errors.rb CHANGED
@@ -37,6 +37,9 @@ module Chewy
37
37
  end
38
38
  end
39
39
 
40
+ class MissingHitsInScrollError < Error
41
+ end
42
+
40
43
  class ImportScopeCleanupError < Error
41
44
  end
42
45
 
@@ -61,8 +61,8 @@ module Chewy
61
61
  # @param fields [Array<Symbol>] a list of fields to compose, every field will be composed if empty
62
62
  # @return [Hash] JSON-ready hash with stringified keys
63
63
  #
64
- def compose(object, crutches = nil, fields: [])
65
- result = evaluate([object, crutches])
64
+ def compose(object, crutches = nil, fields: [], context: {})
65
+ result = evaluate([object, crutches, context])
66
66
 
67
67
  if children.present?
68
68
  child_fields = if fields.present?
@@ -72,7 +72,7 @@ module Chewy
72
72
  end
73
73
 
74
74
  child_fields.each_with_object({}) do |field, memo|
75
- memo.merge!(field.compose(result, crutches) || {})
75
+ memo.merge!(field.compose(result, crutches, context) || {})
76
76
  end.as_json
77
77
  elsif fields.present?
78
78
  result.as_json(only: fields, root: false)
@@ -9,9 +9,12 @@ module Chewy
9
9
  end
10
10
 
11
11
  class Crutches
12
- def initialize(index, collection)
12
+ attr_reader :context
13
+
14
+ def initialize(index, collection, context = {})
13
15
  @index = index
14
16
  @collection = collection
17
+ @context = context
15
18
  @crutches_instances = {}
16
19
  end
17
20
 
@@ -26,7 +29,14 @@ module Chewy
26
29
  end
27
30
 
28
31
  def [](name)
29
- @crutches_instances[name] ||= @index._crutches[:"#{name}"].call(@collection)
32
+ @crutches_instances[name] ||= begin
33
+ block = @index._crutches[:"#{name}"]
34
+ if block.arity > 1 || block.arity < -1
35
+ block.call(@collection, @context)
36
+ else
37
+ block.call(@collection)
38
+ end
39
+ end
30
40
  end
31
41
  end
32
42
 
@@ -13,11 +13,12 @@ module Chewy
13
13
  # @param to_index [Array<Object>] objects to index
14
14
  # @param delete [Array<Object>] objects or ids to delete
15
15
  # @param fields [Array<Symbol, String>] and array of fields for documents update
16
- def initialize(index, to_index: [], delete: [], fields: [])
16
+ def initialize(index, to_index: [], delete: [], fields: [], context: {})
17
17
  @index = index
18
18
  @to_index = to_index
19
19
  @delete = delete
20
20
  @fields = fields.map!(&:to_sym)
21
+ @context = context
21
22
  end
22
23
 
23
24
  # Returns ES API-ready bulk requiest body.
@@ -42,7 +43,7 @@ module Chewy
42
43
  private
43
44
 
44
45
  def crutches_for_index
45
- @crutches_for_index ||= Chewy::Index::Crutch::Crutches.new @index, @to_index
46
+ @crutches_for_index ||= Chewy::Index::Crutch::Crutches.new @index, @to_index, @context
46
47
  end
47
48
 
48
49
  def index_entry(object)
@@ -257,7 +258,7 @@ module Chewy
257
258
  end
258
259
 
259
260
  def data_for(object, fields: [], crutches: crutches_for_index)
260
- @index.compose(object, crutches, fields: fields)
261
+ @index.compose(object, crutches, fields: fields, context: @context)
261
262
  end
262
263
 
263
264
  def parent_changed?(data, old_parent)
@@ -56,6 +56,7 @@ module Chewy
56
56
  {}
57
57
  end
58
58
  end
59
+ @context = @options[:context] || {}
59
60
  @errors = []
60
61
  @stats = {}
61
62
  @leftovers = []
@@ -78,7 +79,7 @@ module Chewy
78
79
  # @param delete [Array<Object>] any acceptable objects for deleting
79
80
  # @return [true, false] the result of the request, true if no errors
80
81
  def process(index: [], delete: [])
81
- bulk_builder = BulkBuilder.new(@index, to_index: index, delete: delete, fields: @options[:update_fields])
82
+ bulk_builder = BulkBuilder.new(@index, to_index: index, delete: delete, fields: @options[:update_fields], context: @context)
82
83
  bulk_body = bulk_builder.bulk_body
83
84
 
84
85
  if @options[:journal]
@@ -115,13 +115,13 @@ module Chewy
115
115
  # @param crutches [Object] optional crutches object; if omitted - a crutch for the single passed object is created as a fallback
116
116
  # @param fields [Array<Symbol>] and array of fields to restrict the generated document
117
117
  # @return [Hash] a JSON-ready hash
118
- def compose(object, crutches = nil, fields: [])
119
- crutches ||= Chewy::Index::Crutch::Crutches.new self, [object]
118
+ def compose(object, crutches = nil, fields: [], context: {})
119
+ crutches ||= Chewy::Index::Crutch::Crutches.new self, [object], context
120
120
 
121
121
  if witchcraft? && root.children.present?
122
- cauldron(fields: fields).brew(object, crutches)
122
+ cauldron(fields: fields).brew(object, crutches, context)
123
123
  else
124
- root.compose(object, crutches, fields: fields)
124
+ root.compose(object, crutches, fields: fields, context: context)
125
125
  end
126
126
  end
127
127
 
@@ -59,15 +59,15 @@ module Chewy
59
59
  @fields = fields
60
60
  end
61
61
 
62
- def brew(object, crutches = nil)
63
- alicorn.call(locals, object, crutches).as_json
62
+ def brew(object, crutches = nil, context = {})
63
+ alicorn.call(locals, object, crutches, context).as_json
64
64
  end
65
65
 
66
66
  private
67
67
 
68
68
  def alicorn
69
69
  @alicorn ||= singleton_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
70
- -> (locals, object0, crutches) do
70
+ -> (locals, object0, crutches, context) do
71
71
  #{composed_values(@index.root, 0)}
72
72
  end
73
73
  RUBY
@@ -171,7 +171,7 @@ module Chewy
171
171
  end
172
172
  end
173
173
 
174
- def source_for(proc, nesting)
174
+ def source_for(proc, nesting) # rubocop:disable Metrics/AbcSize
175
175
  lambdas = exctract_lambdas(ast_from_proc(proc))
176
176
 
177
177
  raise "No lambdas found, try to reformat your code:\n`#{proc.source}`" unless lambdas
@@ -189,6 +189,7 @@ module Chewy
189
189
  source = replace_lvar(source, proc_params[n], :"object#{n}") if proc_params[n]
190
190
  end
191
191
  source = replace_lvar(source, proc_params[nesting + 1], :crutches) if proc_params[nesting + 1]
192
+ source = replace_lvar(source, proc_params[nesting + 2], :context) if proc_params[nesting + 2]
192
193
 
193
194
  binding_variable_list(source).each do |variable|
194
195
  locals.push(proc.binding.eval(variable.to_s))
@@ -11,7 +11,7 @@ module Chewy
11
11
  # Instantiate a new MultiSearch instance.
12
12
  #
13
13
  # @param queries [Array<Chewy::Search::Request>]
14
- # @option [Elasticsearch::Transport::Client] :client (Chewy.client)
14
+ # @option [Elasticsearch::Client] :client (Chewy.client)
15
15
  # The Elasticsearch client that should be used for issuing requests.
16
16
  def initialize(queries, client: Chewy.client)
17
17
  @client = client
@@ -0,0 +1,14 @@
1
+ module Chewy
2
+ module Search
3
+ class Parameters
4
+ # Just a standard hash storage. Nothing to see here.
5
+ #
6
+ # @see Chewy::Search::Parameters::HashStorage
7
+ # @see Chewy::Search::Request#runtime_mappings
8
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html
9
+ class RuntimeMappings < Storage
10
+ include HashStorage
11
+ end
12
+ end
13
+ end
14
+ end
@@ -25,7 +25,7 @@ module Chewy
25
25
  search_type preference limit offset terminate_after
26
26
  timeout min_score source stored_fields search_after
27
27
  load script_fields suggest aggs aggregations collapse none
28
- indices_boost rescore highlight total total_count
28
+ indices_boost rescore highlight runtime_mappings total total_count
29
29
  total_entries indices types delete_all count exists?
30
30
  exist? find pluck scroll_batches scroll_hits
31
31
  scroll_results scroll_wrappers ignore_unavailable
@@ -656,7 +656,23 @@ module Chewy
656
656
  # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html
657
657
  # @param value [Hash]
658
658
  # @return [Chewy::Search::Request]
659
- %i[script_fields indices_boost rescore highlight].each do |name|
659
+ #
660
+ # @!method runtime_mappings(value)
661
+ # Add a `runtime_mappings` part to the request. Further
662
+ # call values are merged to the storage hash.
663
+ #
664
+ # @example
665
+ # PlacesIndex
666
+ # .runtime_mappings(field1: {type: "keyword", script: {lang: "painless", source: "emit('some script here')"}})
667
+ # .runtime_mappings(field2: {type: "keyword", script: {lang: "painless", source: "emit('some script here')"}})
668
+ # # => <PlacesIndex::Query {..., :body=>{:runtime_mappings=>{
669
+ # # "field1"=>{:type=>"keyword", :script=>{:lang=>"painless", :source=>"emit('some script here')"}},
670
+ # # "field2"=>{:type=>"keyword", :script=>{:lang=>"painless", :source=>"emit('some script here')"}}}}}>
671
+ # @see Chewy::Search::Parameters::RuntimeMappings
672
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html
673
+ # @param value [Hash]
674
+ # @return [Chewy::Search::Request]
675
+ %i[script_fields indices_boost rescore highlight runtime_mappings].each do |name|
660
676
  define_method name do |value|
661
677
  modify(name) { update!(value) }
662
678
  end
@@ -29,20 +29,28 @@ module Chewy
29
29
 
30
30
  result = perform(size: batch_size, scroll: scroll)
31
31
  total = [raw_limit_value, result.fetch('hits', {}).fetch('total', {}).fetch('value', 0)].compact.min
32
+
33
+ total_batches = total / batch_size
32
34
  last_batch_size = total % batch_size
33
- fetched = 0
35
+
36
+ total_batches += 1 if last_batch_size != 0
37
+
34
38
  scroll_id = nil
35
39
 
36
- loop do
40
+ total_batches.times do |batch_counter|
41
+ last_run = total_batches - 1 == batch_counter
42
+
37
43
  hits = result.fetch('hits', {}).fetch('hits', [])
38
- fetched += hits.size
39
- hits = hits.first(last_batch_size) if last_batch_size != 0 && fetched >= total
44
+ hits = hits.first(last_batch_size) if last_run && last_batch_size != 0
45
+
46
+ raise Chewy::MissingHitsInScrollError if hits.empty?
47
+
40
48
  yield(hits) if hits.present?
41
49
  scroll_id = result['_scroll_id']
42
50
 
43
- break if result['terminated_early'] || fetched >= total
51
+ break if result['terminated_early']
44
52
 
45
- result = perform_scroll(scroll: scroll, scroll_id: scroll_id)
53
+ result = perform_scroll(scroll: scroll, scroll_id: scroll_id) unless last_run
46
54
  end
47
55
  ensure
48
56
  Chewy.client.clear_scroll(body: {scroll_id: scroll_id}) if scroll_id
data/lib/chewy/stash.rb CHANGED
@@ -38,17 +38,21 @@ module Chewy
38
38
 
39
39
  # Selects all the journal entries for the specified indices.
40
40
  #
41
+ # Uses a single `terms` filter rather than a chain of `bool.should`
42
+ # clauses so the query depth stays constant regardless of how many
43
+ # indices are passed. Avoids hitting the Elasticsearch
44
+ # `indices.query.bool.max_nested_depth` limit (default 30) when
45
+ # cleaning or applying journals across many indices.
46
+ #
41
47
  # @param indices [Chewy::Index, Array<Chewy::Index>]
42
48
  def self.for(*something)
43
49
  something = something.flatten.compact
50
+ return all if something.empty?
51
+
44
52
  indexes = something.flat_map { |s| Chewy.derive_name(s) }
45
- return none if something.present? && indexes.blank?
53
+ return none if indexes.blank?
46
54
 
47
- scope = all
48
- indexes.each do |index|
49
- scope = scope.or(filter(term: {index_name: index.derivable_name}))
50
- end
51
- scope
55
+ filter(terms: {index_name: indexes.map(&:derivable_name).uniq})
52
56
  end
53
57
 
54
58
  default_import_options journal: false
data/lib/chewy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '8.0.1'.freeze
2
+ VERSION = '8.1.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chewy
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.1
4
+ version: 8.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toptal, LLC
8
8
  - pyromaniac
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2026-03-12 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activesupport
@@ -147,6 +146,7 @@ files:
147
146
  - lib/chewy/search/parameters/query.rb
148
147
  - lib/chewy/search/parameters/request_cache.rb
149
148
  - lib/chewy/search/parameters/rescore.rb
149
+ - lib/chewy/search/parameters/runtime_mappings.rb
150
150
  - lib/chewy/search/parameters/script_fields.rb
151
151
  - lib/chewy/search/parameters/search_after.rb
152
152
  - lib/chewy/search/parameters/search_type.rb
@@ -186,7 +186,6 @@ licenses:
186
186
  - MIT
187
187
  metadata:
188
188
  rubygems_mfa_required: 'true'
189
- post_install_message:
190
189
  rdoc_options: []
191
190
  require_paths:
192
191
  - lib
@@ -201,8 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
200
  - !ruby/object:Gem::Version
202
201
  version: '0'
203
202
  requirements: []
204
- rubygems_version: 3.4.19
205
- signing_key:
203
+ rubygems_version: 4.0.12
206
204
  specification_version: 4
207
205
  summary: Elasticsearch ODM client wrapper
208
206
  test_files: []