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 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