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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a027fc99d931ad37a70f2cdf906363917af16afc6dd82bbc1da8066e63dad9ee
4
- data.tar.gz: 602bbb699c4971f8653207bb52d8bb7c40899128b1c6034c89f0f4481207f569
3
+ metadata.gz: 0de5ea12714d98c68dc3d74d1b6bb9debefea211a025749b8958a804a285bdcd
4
+ data.tar.gz: f9256684493364e7f6b1ae1b3da4d6376624369df3ff950750a6148cd7d6da2f
5
5
  SHA512:
6
- metadata.gz: 444a1e44ad27a6d066daf31ceee258ee51e4ef1398fe6af4bf7294090b2391d2f136aa1726d5eb9f1c22f1c87abfd16f4a05c1198a0496e2062a13f8f31340db
7
- data.tar.gz: 5f484506393af6b6959593850c9c7cb73307628076eb9676bd72e53b7fa02a987986d79443c35c2c7a0efd652615fb7ccf6ed418ab7bdf85eb69fbe6b2b02c24
6
+ metadata.gz: f35594db25143614d6c88ac2aef7081975502e0434993e9b60495d6f3d63d2f13fee246275bd5b6b6c5b1deae4db06f5b734bf73369e77aeea22c7141b177417
7
+ data.tar.gz: 92dbe74fb416105b4c38dcb95b40646c53597d17c2cdd2800107c27a979da24abceccb0d6ab51b0b3783f769327a3a9fed323b9d504de65cc3541e90df0bd273
@@ -3,7 +3,7 @@ name: CI
3
3
  on: [push]
4
4
 
5
5
  jobs:
6
- tests:
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-0-activerecord-6-1:
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/rails.6.1.activerecord.gemfile
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: '3.0'
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
@@ -19,3 +19,4 @@ gem 'redcarpet'
19
19
  gem 'yard'
20
20
 
21
21
  gem 'rexml' if RUBY_VERSION >= '3.0.0'
22
+ gem 'ruby2_keywords' if RUBY_VERSION < '2.7'
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/type/mapping.rb) for more details.
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 { match name: 'London' }
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/tree/master/elasticsearch-dsl for more details.
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
@@ -1,8 +1,8 @@
1
1
  module Chewy
2
2
  module Fields
3
3
  class Base
4
- attr_reader :name, :options, :value, :children
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
- if value.arity.zero?
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
- message = value.is_a?(Symbol) || value.is_a?(String) ? value.to_sym : name
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
 
@@ -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, :parent, :parent_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
- @parent = options.fetch(:parent, options.fetch(:_parent, @parent))
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
 
@@ -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 import_options.merge(
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 import_options.merge(journal: journal)
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
- .index_by do |object|
105
- object.public_send(primary_key).to_s
106
- end
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| loaded_objects[id.to_s] }
110
+ ids.map { |id| indexed_objects[id.to_s] }
109
111
  end
110
112
 
111
113
  private