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