chewy 7.2.3 → 7.2.4
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 +4 -4
- data/.github/workflows/ruby.yml +12 -4
- data/CHANGELOG.md +13 -1
- data/Gemfile +1 -0
- data/README.md +20 -3
- data/gemfiles/rails.7.0.activerecord.gemfile +13 -0
- data/lib/chewy/errors.rb +6 -0
- data/lib/chewy/fields/base.rb +68 -12
- data/lib/chewy/fields/root.rb +3 -11
- data/lib/chewy/index/actions.rb +3 -3
- data/lib/chewy/index/adapter/active_record.rb +5 -0
- data/lib/chewy/index/adapter/object.rb +3 -3
- data/lib/chewy/index/adapter/orm.rb +8 -6
- data/lib/chewy/index/import/bulk_builder.rb +219 -31
- data/lib/chewy/index/import/bulk_request.rb +1 -1
- data/lib/chewy/index/import.rb +3 -4
- data/lib/chewy/index/mapping.rb +2 -2
- data/lib/chewy/index/observe.rb +1 -1
- data/lib/chewy/rspec/update_index.rb +1 -1
- data/lib/chewy/search/request.rb +2 -2
- data/lib/chewy/search.rb +1 -1
- data/lib/chewy/strategy/active_job.rb +1 -1
- data/lib/chewy/strategy/sidekiq.rb +1 -1
- data/lib/chewy/version.rb +1 -1
- data/lib/chewy.rb +2 -2
- data/lib/tasks/chewy.rake +1 -1
- data/spec/chewy/fields/base_spec.rb +38 -18
- data/spec/chewy/index/adapter/active_record_spec.rb +26 -0
- data/spec/chewy/index/import/bulk_builder_spec.rb +304 -0
- data/spec/chewy/index/import/routine_spec.rb +3 -3
- data/spec/chewy/index/import_spec.rb +40 -0
- data/spec/support/active_record.rb +8 -1
- metadata +4 -5
- data/lib/chewy/backports/deep_dup.rb +0 -46
- data/lib/chewy/backports/duplicable.rb +0 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0de5ea12714d98c68dc3d74d1b6bb9debefea211a025749b8958a804a285bdcd
|
4
|
+
data.tar.gz: f9256684493364e7f6b1ae1b3da4d6376624369df3ff950750a6148cd7d6da2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f35594db25143614d6c88ac2aef7081975502e0434993e9b60495d6f3d63d2f13fee246275bd5b6b6c5b1deae4db06f5b734bf73369e77aeea22c7141b177417
|
7
|
+
data.tar.gz: 92dbe74fb416105b4c38dcb95b40646c53597d17c2cdd2800107c27a979da24abceccb0d6ab51b0b3783f769327a3a9fed323b9d504de65cc3541e90df0bd273
|
data/.github/workflows/ruby.yml
CHANGED
@@ -3,7 +3,7 @@ name: CI
|
|
3
3
|
on: [push]
|
4
4
|
|
5
5
|
jobs:
|
6
|
-
|
6
|
+
ruby-2:
|
7
7
|
runs-on: ubuntu-latest
|
8
8
|
strategy:
|
9
9
|
fail-fast: false
|
@@ -29,15 +29,23 @@ jobs:
|
|
29
29
|
- name: Tests
|
30
30
|
run: bundle exec rspec
|
31
31
|
|
32
|
-
ruby-3
|
32
|
+
ruby-3:
|
33
33
|
runs-on: ubuntu-latest
|
34
|
+
strategy:
|
35
|
+
fail-fast: false
|
36
|
+
matrix:
|
37
|
+
ruby: [ '3.0', 3.1 ]
|
38
|
+
gemfile: [ rails.6.1.activerecord, rails.7.0.activerecord ]
|
39
|
+
name: ${{ matrix.ruby }}-${{ matrix.gemfile }}
|
40
|
+
|
34
41
|
env:
|
35
|
-
BUNDLE_GEMFILE: gemfiles
|
42
|
+
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
|
43
|
+
|
36
44
|
steps:
|
37
45
|
- uses: actions/checkout@v2
|
38
46
|
- uses: ruby/setup-ruby@v1
|
39
47
|
with:
|
40
|
-
ruby-version:
|
48
|
+
ruby-version: ${{ matrix.ruby }}
|
41
49
|
bundler-cache: true
|
42
50
|
- name: Run Elasticsearch
|
43
51
|
uses: elastic/elastic-github-actions/elasticsearch@9de0f78f306e4ebc0838f057e6b754364685e759
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,17 @@
|
|
8
8
|
|
9
9
|
### Bugs Fixed
|
10
10
|
|
11
|
+
## 7.2.4 (2022-02-03)
|
12
|
+
|
13
|
+
### New Features
|
14
|
+
|
15
|
+
* [#760](https://github.com/toptal/chewy/pull/760): Replace parent-child mapping with a [join field](https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html#parent-child-mapping-types) ([@mrzasa][])
|
16
|
+
|
17
|
+
### Bugs Fixed
|
18
|
+
|
19
|
+
* [#825](https://github.com/toptal/chewy/issues/825): Fix mismatch argument names at update_mapping rake task ([@AgeevAndrew][])
|
20
|
+
* [#832](https://github.com/toptal/chewy/issues/832): Fix "cannot load such file -- `i18n/core_ext/hash`" ([@chrisandreae][])
|
21
|
+
|
11
22
|
## 7.2.3 (2021-10-29)
|
12
23
|
|
13
24
|
### New Features
|
@@ -634,6 +645,7 @@
|
|
634
645
|
* Initial version
|
635
646
|
|
636
647
|
[@0x0badc0de]: https://github.com/0x0badc0de
|
648
|
+
[@AgeevAndrew]: https://github.com/AgeevAndrew
|
637
649
|
[@aglushkov]: https://github.com/aglushkov
|
638
650
|
[@AlexVPopov]: https://github.com/AlexVPopov
|
639
651
|
[@AndreySavelyev]: https://github.com/AndreySavelyev
|
@@ -648,6 +660,7 @@
|
|
648
660
|
[@biow0lf]: https://github.com/biow0lf
|
649
661
|
[@Borzik]: https://github.com/Borzik
|
650
662
|
[@caldwecr]: https://github.com/caldwecr
|
663
|
+
[@chrisandreae]: https://github.com/chrisandreae
|
651
664
|
[@clupprich]: https://github.com/clupprich
|
652
665
|
[@dalthon]: https://github.com/dalthon
|
653
666
|
[@davekaro]: https://github.com/davekaro
|
@@ -720,4 +733,3 @@
|
|
720
733
|
[@Vitalina-Vakulchyk]: https://github.com/Vitalina-Vakulchyk
|
721
734
|
[@webgago]: https://github.com/webgago
|
722
735
|
[@yahooguntu]: https://github.com/yahooguntu
|
723
|
-
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -340,7 +340,7 @@ Chewy.settings = {
|
|
340
340
|
[See index settings here](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html).
|
341
341
|
[See root object settings here](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html).
|
342
342
|
|
343
|
-
See [mapping.rb](lib/chewy/
|
343
|
+
See [mapping.rb](lib/chewy/index/mapping.rb) for more details.
|
344
344
|
|
345
345
|
5. Add model-observing code
|
346
346
|
|
@@ -446,6 +446,23 @@ end
|
|
446
446
|
|
447
447
|
See the section on *Script fields* for details on calculating distance in a search.
|
448
448
|
|
449
|
+
### Join fields
|
450
|
+
|
451
|
+
You can use a [join field](https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html)
|
452
|
+
to implement parent-child relationships between documents.
|
453
|
+
It [replaces the old `parent_id` based parent-child mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html#parent-child-mapping-types)
|
454
|
+
|
455
|
+
To use it, you need to pass `relations` and `join` (with `type` and `id`) options:
|
456
|
+
```ruby
|
457
|
+
field :hierarchy_link, type: :join, relations: {question: %i[answer comment], answer: :vote, vote: :subvote}, join: {type: :comment_type, id: :commented_id}
|
458
|
+
```
|
459
|
+
assuming you have `comment_type` and `commented_id` fields in your model.
|
460
|
+
|
461
|
+
Note that when you reindex a parent, it's children and grandchildren will be reindexed as well.
|
462
|
+
This may require additional queries to the primary database and to elastisearch.
|
463
|
+
|
464
|
+
Also note that the join field doesn't support crutches (it should be a field directly defined on the model).
|
465
|
+
|
449
466
|
### Crutches™ technology
|
450
467
|
|
451
468
|
Assume you are defining your index like this (product has_many categories through product_categories):
|
@@ -959,7 +976,7 @@ Main methods of the request DSL are: `query`, `filter` and `post_filter`, it is
|
|
959
976
|
```ruby
|
960
977
|
CitiesIndex
|
961
978
|
.filter(term: {name: 'Bangkok'})
|
962
|
-
.query {
|
979
|
+
.query(match: {name: 'London'})
|
963
980
|
.query.not(range: {population: {gt: 1_000_000}})
|
964
981
|
```
|
965
982
|
|
@@ -969,7 +986,7 @@ You can query a set of indexes at once:
|
|
969
986
|
CitiesIndex.indices(CountriesIndex).query(match: {name: 'Some'})
|
970
987
|
```
|
971
988
|
|
972
|
-
See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html and https://github.com/elastic/elasticsearch-ruby
|
989
|
+
See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html and https://github.com/elastic/elasticsearch-dsl-ruby for more details.
|
973
990
|
|
974
991
|
An important part of requests manipulation is merging. There are 4 methods to perform it: `merge`, `and`, `or`, `not`. See [Chewy::Search::QueryProxy](lib/chewy/search/query_proxy.rb) for details. Also, `only` and `except` methods help to remove unneeded parts of the request.
|
975
992
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'activejob', '~> 7.0.0'
|
4
|
+
gem 'activerecord', '~> 7.0.0'
|
5
|
+
gem 'activesupport', '~> 7.0.0'
|
6
|
+
gem 'kaminari-core', '~> 1.1.0', require: false
|
7
|
+
gem 'parallel', require: false
|
8
|
+
gem 'rspec_junit_formatter', '~> 0.4.1'
|
9
|
+
gem 'sidekiq', require: false
|
10
|
+
|
11
|
+
gem 'rexml' if RUBY_VERSION >= '3.0.0'
|
12
|
+
|
13
|
+
gemspec path: '../'
|
data/lib/chewy/errors.rb
CHANGED
@@ -30,4 +30,10 @@ module Chewy
|
|
30
30
|
super message
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
class InvalidJoinFieldType < Error
|
35
|
+
def initialize(join_field_type, join_field_name, relations)
|
36
|
+
super("`#{join_field_type}` set for the join field `#{join_field_name}` is not on the :relations list (#{relations})")
|
37
|
+
end
|
38
|
+
end
|
33
39
|
end
|
data/lib/chewy/fields/base.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Chewy
|
2
2
|
module Fields
|
3
3
|
class Base
|
4
|
-
attr_reader :name, :
|
5
|
-
attr_accessor :parent
|
4
|
+
attr_reader :name, :join_options, :options, :children
|
5
|
+
attr_accessor :parent # used by Chewy::Index::Mapping to expand nested fields
|
6
6
|
|
7
7
|
def initialize(name, value: nil, **options)
|
8
8
|
@name = name.to_sym
|
@@ -10,9 +10,11 @@ module Chewy
|
|
10
10
|
update_options!(**options)
|
11
11
|
@value = value
|
12
12
|
@children = []
|
13
|
+
@allowed_relations = find_allowed_relations(options[:relations]) # for join fields
|
13
14
|
end
|
14
15
|
|
15
16
|
def update_options!(**options)
|
17
|
+
@join_options = options.delete(:join) || {}
|
16
18
|
@options = options
|
17
19
|
end
|
18
20
|
|
@@ -53,30 +55,70 @@ module Chewy
|
|
53
55
|
{name => result}
|
54
56
|
end
|
55
57
|
|
58
|
+
def value
|
59
|
+
if join_field?
|
60
|
+
join_type = join_options[:type]
|
61
|
+
join_id = join_options[:id]
|
62
|
+
# memoize
|
63
|
+
@value ||= proc do |object|
|
64
|
+
validate_join_type!(value_by_name_proc(join_type).call(object))
|
65
|
+
# If it's a join field and it has join_id, the value is compound and contains
|
66
|
+
# both name (type) and id of the parent object
|
67
|
+
if value_by_name_proc(join_id).call(object).present?
|
68
|
+
{
|
69
|
+
name: value_by_name_proc(join_type).call(object), # parent type
|
70
|
+
parent: value_by_name_proc(join_id).call(object) # parent id
|
71
|
+
}
|
72
|
+
else
|
73
|
+
value_by_name_proc(join_type).call(object)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
else
|
77
|
+
@value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
56
81
|
private
|
57
82
|
|
58
83
|
def geo_point?
|
59
84
|
@options[:type].to_s == 'geo_point'
|
60
85
|
end
|
61
86
|
|
87
|
+
def join_field?
|
88
|
+
@options[:type].to_s == 'join'
|
89
|
+
end
|
90
|
+
|
62
91
|
def ignore_blank?
|
63
92
|
@options.fetch(:ignore_blank) { geo_point? }
|
64
93
|
end
|
65
94
|
|
66
95
|
def evaluate(objects)
|
67
|
-
object = objects.first
|
68
|
-
|
69
96
|
if value.is_a?(Proc)
|
70
|
-
|
71
|
-
object.instance_exec(&value)
|
72
|
-
elsif value.arity.negative?
|
73
|
-
value.call(*object)
|
74
|
-
else
|
75
|
-
value.call(*objects.first(value.arity))
|
76
|
-
end
|
97
|
+
value_by_proc(objects, value)
|
77
98
|
else
|
78
|
-
|
99
|
+
value_by_name(objects, value)
|
100
|
+
end
|
101
|
+
end
|
79
102
|
|
103
|
+
def value_by_proc(objects, value)
|
104
|
+
object = objects.first
|
105
|
+
if value.arity.zero?
|
106
|
+
object.instance_exec(&value)
|
107
|
+
elsif value.arity.negative?
|
108
|
+
value.call(*object)
|
109
|
+
else
|
110
|
+
value.call(*objects.first(value.arity))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def value_by_name(objects, value)
|
115
|
+
object = objects.first
|
116
|
+
message = value.is_a?(Symbol) || value.is_a?(String) ? value.to_sym : name
|
117
|
+
value_by_name_proc(message).call(object)
|
118
|
+
end
|
119
|
+
|
120
|
+
def value_by_name_proc(message)
|
121
|
+
proc do |object|
|
80
122
|
if object.is_a?(Hash)
|
81
123
|
if object.key?(message)
|
82
124
|
object[message]
|
@@ -89,6 +131,20 @@ module Chewy
|
|
89
131
|
end
|
90
132
|
end
|
91
133
|
|
134
|
+
def validate_join_type!(type)
|
135
|
+
return unless type
|
136
|
+
return if @allowed_relations.include?(type.to_sym)
|
137
|
+
|
138
|
+
raise Chewy::InvalidJoinFieldType.new(type, @name, options[:relations])
|
139
|
+
end
|
140
|
+
|
141
|
+
def find_allowed_relations(relations)
|
142
|
+
return [] unless relations
|
143
|
+
return relations unless relations.is_a?(Hash)
|
144
|
+
|
145
|
+
(relations.keys + relations.values).flatten.uniq
|
146
|
+
end
|
147
|
+
|
92
148
|
def compose_children(value, *parent_objects)
|
93
149
|
return unless value
|
94
150
|
|
data/lib/chewy/fields/root.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Chewy
|
2
2
|
module Fields
|
3
3
|
class Root < Chewy::Fields::Base
|
4
|
-
attr_reader :dynamic_templates, :id
|
4
|
+
attr_reader :dynamic_templates, :id
|
5
5
|
|
6
6
|
def initialize(name, **options)
|
7
7
|
super(name, **options)
|
@@ -12,9 +12,7 @@ module Chewy
|
|
12
12
|
|
13
13
|
def update_options!(**options)
|
14
14
|
@id = options.fetch(:id, options.fetch(:_id, @id))
|
15
|
-
@
|
16
|
-
@parent_id = options.fetch(:parent_id, @parent_id)
|
17
|
-
@options.merge!(options.except(:id, :_id, :parent, :_parent, :parent_id, :type))
|
15
|
+
@options.merge!(options.except(:id, :_id, :type))
|
18
16
|
end
|
19
17
|
|
20
18
|
def mappings_hash
|
@@ -29,7 +27,7 @@ module Chewy
|
|
29
27
|
mappings[name]
|
30
28
|
end
|
31
29
|
|
32
|
-
def dynamic_template(*args)
|
30
|
+
ruby2_keywords def dynamic_template(*args)
|
33
31
|
options = args.extract_options!.deep_symbolize_keys
|
34
32
|
if args.first
|
35
33
|
template_name = :"template_#{dynamic_templates.count.next}"
|
@@ -50,12 +48,6 @@ module Chewy
|
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
53
|
-
def compose_parent(object)
|
54
|
-
return unless parent_id
|
55
|
-
|
56
|
-
parent_id.arity.zero? ? object.instance_exec(&parent_id) : parent_id.call(object)
|
57
|
-
end
|
58
|
-
|
59
51
|
def compose_id(object)
|
60
52
|
return unless id
|
61
53
|
|
data/lib/chewy/index/actions.rb
CHANGED
@@ -156,11 +156,11 @@ module Chewy
|
|
156
156
|
suffixed_name = index_name(suffix: suffix)
|
157
157
|
|
158
158
|
optimize_index_settings suffixed_name
|
159
|
-
result = import
|
159
|
+
result = import(**import_options.merge(
|
160
160
|
suffix: suffix,
|
161
161
|
journal: journal,
|
162
162
|
refresh: !Chewy.reset_disable_refresh_interval
|
163
|
-
)
|
163
|
+
))
|
164
164
|
original_index_settings suffixed_name
|
165
165
|
|
166
166
|
delete if indexes.blank?
|
@@ -176,7 +176,7 @@ module Chewy
|
|
176
176
|
result
|
177
177
|
else
|
178
178
|
purge!
|
179
|
-
import
|
179
|
+
import(**import_options.merge(journal: journal))
|
180
180
|
end
|
181
181
|
|
182
182
|
specification.lock!
|
@@ -94,6 +94,11 @@ module Chewy
|
|
94
94
|
object_class.connection.execute(sql).map(&converter)
|
95
95
|
end
|
96
96
|
|
97
|
+
def raw(scope, converter)
|
98
|
+
sql = scope.to_sql
|
99
|
+
object_class.connection.execute(sql).map(&converter)
|
100
|
+
end
|
101
|
+
|
97
102
|
def relation_class
|
98
103
|
::ActiveRecord::Relation
|
99
104
|
end
|
@@ -85,7 +85,7 @@ module Chewy
|
|
85
85
|
# @param args [Array<#to_json>]
|
86
86
|
# @option options [Integer] :batch_size import processing batch size
|
87
87
|
# @return [true, false]
|
88
|
-
def import(*args, &block)
|
88
|
+
ruby2_keywords def import(*args, &block)
|
89
89
|
collection, options = import_args(*args)
|
90
90
|
import_objects(collection, options, &block)
|
91
91
|
end
|
@@ -113,7 +113,7 @@ module Chewy
|
|
113
113
|
# end
|
114
114
|
#
|
115
115
|
# @see Chewy::Index::Adapter::Base#import_fields
|
116
|
-
def import_fields(*args, &block)
|
116
|
+
ruby2_keywords def import_fields(*args, &block)
|
117
117
|
return enum_for(:import_fields, *args) unless block_given?
|
118
118
|
|
119
119
|
options = args.extract_options!
|
@@ -139,7 +139,7 @@ module Chewy
|
|
139
139
|
# For the Object adapter returns the objects themselves in batches.
|
140
140
|
#
|
141
141
|
# @see Chewy::Index::Adapter::Base#import_references
|
142
|
-
def import_references(*args, &block)
|
142
|
+
ruby2_keywords def import_references(*args, &block)
|
143
143
|
return enum_for(:import_references, *args) unless block_given?
|
144
144
|
|
145
145
|
collection, options = import_args(*args)
|
@@ -72,7 +72,7 @@ module Chewy
|
|
72
72
|
# # or
|
73
73
|
# UsersIndex.import users.map(&:id) # user ids will be deleted from index
|
74
74
|
#
|
75
|
-
def import(*args, &block)
|
75
|
+
ruby2_keywords def import(*args, &block)
|
76
76
|
collection, options = import_args(*args)
|
77
77
|
|
78
78
|
if !collection.is_a?(relation_class) || options[:direct_import]
|
@@ -82,7 +82,7 @@ module Chewy
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
def import_fields(*args, &block)
|
85
|
+
ruby2_keywords def import_fields(*args, &block)
|
86
86
|
return enum_for(:import_fields, *args) unless block_given?
|
87
87
|
|
88
88
|
collection, options = import_args(*args)
|
@@ -101,11 +101,13 @@ module Chewy
|
|
101
101
|
additional_scope = options[options[:_index].to_sym].try(:[], :scope) || options[:scope]
|
102
102
|
|
103
103
|
loaded_objects = load_scope_objects(scope, additional_scope)
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
loaded_objects = raw(loaded_objects, options[:raw_import]) if options[:raw_import]
|
105
|
+
|
106
|
+
indexed_objects = loaded_objects.index_by do |object|
|
107
|
+
object.public_send(primary_key).to_s
|
108
|
+
end
|
107
109
|
|
108
|
-
ids.map { |id|
|
110
|
+
ids.map { |id| indexed_objects[id.to_s] }
|
109
111
|
end
|
110
112
|
|
111
113
|
private
|