chewy 7.2.0 → 7.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +14 -6
  3. data/CHANGELOG.md +71 -0
  4. data/Gemfile +1 -0
  5. data/README.md +46 -7
  6. data/chewy.gemspec +1 -1
  7. data/gemfiles/rails.7.0.activerecord.gemfile +13 -0
  8. data/lib/chewy/config.rb +1 -1
  9. data/lib/chewy/errors.rb +6 -0
  10. data/lib/chewy/fields/base.rb +68 -12
  11. data/lib/chewy/fields/root.rb +3 -11
  12. data/lib/chewy/index/actions.rb +3 -3
  13. data/lib/chewy/index/adapter/active_record.rb +5 -0
  14. data/lib/chewy/index/adapter/object.rb +3 -3
  15. data/lib/chewy/index/adapter/orm.rb +8 -6
  16. data/lib/chewy/index/import/bulk_builder.rb +219 -31
  17. data/lib/chewy/index/import/bulk_request.rb +1 -1
  18. data/lib/chewy/index/import.rb +3 -4
  19. data/lib/chewy/index/mapping.rb +2 -2
  20. data/lib/chewy/index/observe.rb +1 -1
  21. data/lib/chewy/minitest/helpers.rb +77 -0
  22. data/lib/chewy/rake_helper.rb +21 -1
  23. data/lib/chewy/rspec/build_query.rb +12 -0
  24. data/lib/chewy/rspec/helpers.rb +55 -0
  25. data/lib/chewy/rspec/update_index.rb +1 -1
  26. data/lib/chewy/rspec.rb +2 -0
  27. data/lib/chewy/runtime.rb +1 -1
  28. data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
  29. data/lib/chewy/search/parameters/order.rb +6 -19
  30. data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
  31. data/lib/chewy/search/parameters.rb +1 -1
  32. data/lib/chewy/search/request.rb +30 -6
  33. data/lib/chewy/search/scoping.rb +1 -1
  34. data/lib/chewy/search/scrolling.rb +1 -1
  35. data/lib/chewy/search.rb +1 -1
  36. data/lib/chewy/strategy/active_job.rb +1 -1
  37. data/lib/chewy/strategy/sidekiq.rb +1 -1
  38. data/lib/chewy/version.rb +1 -1
  39. data/lib/chewy.rb +17 -7
  40. data/lib/tasks/chewy.rake +1 -1
  41. data/migration_guide.md +6 -1
  42. data/spec/chewy/fields/base_spec.rb +38 -18
  43. data/spec/chewy/index/adapter/active_record_spec.rb +26 -0
  44. data/spec/chewy/index/import/bulk_builder_spec.rb +304 -0
  45. data/spec/chewy/index/import/routine_spec.rb +3 -3
  46. data/spec/chewy/index/import_spec.rb +40 -0
  47. data/spec/chewy/minitest/helpers_spec.rb +110 -0
  48. data/spec/chewy/rake_helper_spec.rb +19 -0
  49. data/spec/chewy/rspec/build_query_spec.rb +34 -0
  50. data/spec/chewy/rspec/helpers_spec.rb +61 -0
  51. data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
  52. data/spec/chewy/search/parameters/order_spec.rb +18 -11
  53. data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
  54. data/spec/chewy/search/parameters_spec.rb +6 -1
  55. data/spec/chewy/search/request_spec.rb +21 -9
  56. data/spec/chewy/search/scrolling_spec.rb +1 -1
  57. data/spec/chewy_spec.rb +3 -3
  58. data/spec/support/active_record.rb +8 -1
  59. metadata +24 -7
  60. data/lib/chewy/backports/deep_dup.rb +0 -46
  61. data/lib/chewy/backports/duplicable.rb +0 -91
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a958ecfd3273f1e8086b3c07aa823f3db8e5d2300a0ca2a86a563e1d6bd2df42
4
- data.tar.gz: 8195d2162307dbf368865cc5e5975372e169796aa85a5a6bd8ac404484b99d75
3
+ metadata.gz: 0de5ea12714d98c68dc3d74d1b6bb9debefea211a025749b8958a804a285bdcd
4
+ data.tar.gz: f9256684493364e7f6b1ae1b3da4d6376624369df3ff950750a6148cd7d6da2f
5
5
  SHA512:
6
- metadata.gz: 7b3286e50679ce8b43c9a595d90502cbd028ac8e94c93e240a3f820de15fbc19052887f1bf973670421bcc8a2e1cfe9dd4d9b2183cb0ccd828726220df005c7c
7
- data.tar.gz: 00a24e51c43cd0e191af699949ca3ac1ca738777fd3a22dfe1d97d7d55feb6ebfea29e2577d488ff1f97189e1430266110395afea3efcb949917d07a6c26477f
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
@@ -22,25 +22,33 @@ jobs:
22
22
  ruby-version: ${{ matrix.ruby }}
23
23
  bundler-cache: true
24
24
  - name: Run Elasticsearch
25
- uses: elastic/elastic-github-actions/elasticsearch@master
25
+ uses: elastic/elastic-github-actions/elasticsearch@9de0f78f306e4ebc0838f057e6b754364685e759
26
26
  with:
27
27
  stack-version: 7.10.1
28
28
  port: 9250
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
- uses: elastic/elastic-github-actions/elasticsearch@master
51
+ uses: elastic/elastic-github-actions/elasticsearch@9de0f78f306e4ebc0838f057e6b754364685e759
44
52
  with:
45
53
  stack-version: 7.10.1
46
54
  port: 9250
data/CHANGELOG.md CHANGED
@@ -8,6 +8,71 @@
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
+
22
+ ## 7.2.3 (2021-10-29)
23
+
24
+ ### New Features
25
+
26
+ * [#801](https://github.com/toptal/chewy/pull/801): Add the [`track_total_hits`](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-your-data.html#track-total-hits) option to the query. ([@milk1000cc][])
27
+ * [#810](https://github.com/toptal/chewy/pull/810): Use `unsafe_load` when loading config to support Psych 4.
28
+ * [#819](https://github.com/toptal/chewy/pull/819): Add the [`ignore_unavailable`](https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-index.html#multi-index) option to the request. ([@kolauren][])
29
+
30
+ ### Changes
31
+
32
+ * [#817](https://github.com/toptal/chewy/pull/817): Show warning message during rake chewy:reset or chewy:upgrade if journaling is disabled. ([@konalegi][])
33
+
34
+ ### Bugs Fixed
35
+
36
+ * [#816](https://github.com/toptal/chewy/pull/816): Move query [`preference`](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-search.html#search-preference) from body to query parameters. ([@afg419][])
37
+
38
+ ## 7.2.2 (2021-05-24)
39
+
40
+ ### Changes
41
+
42
+ * [#800](https://github.com/toptal/chewy/pull/800): Revert [#787](https://github.com/toptal/chewy/pull/787) progressbar feature to avoid performance degradation in parallel import ([@rabotyaga][])
43
+
44
+ * [#795](https://github.com/toptal/chewy/issues/795): **(Breaking)** Change the Chewy::Search::Parameters::Order implementation to use Array ([@jiajiawang][]):
45
+ * To allow multiple sorting options that may have the same key name. For example script based sorting whose key will always be `_script`.
46
+ * Behaviour change of chained `order` calls.
47
+ * e.g. `.order(_script: {a: 1}).order(_script: {b: 2})`
48
+ * Before `{:sort=>[{"_script"=>{:b=>2}}]}`
49
+ * After `{:sort=>[{"_script"=>{:a=>1}},{"_script"=>{:b=>2}}]}`
50
+
51
+ * [#654](https://github.com/toptal/chewy/issues/654): Add helpers and matchers for testing ([@Vitalina-Vakulchyk][]):
52
+ * `mock_elasticsearch_response` helpers both Rspec and Minitest - to mock elasticsearch response
53
+ * `mock_elasticsearch_response_sources` helpers both Rspec and Minitest - to mock elasticsearch response sources
54
+ * `assert_elasticsearch_query` helper for Minitest - to compare request and expected query (returns `true`/`false`)
55
+ * `build_query` matcher for Rspec - to compare request and expected query (returns `true`/`false`)
56
+
57
+ ## 7.2.1 (2021-05-11)
58
+
59
+ ### New Features
60
+
61
+ * [#469](https://github.com/toptal/chewy/issues/469): Add ability to output the progressbar with `ENV['PROGRESS']` during `reset` rake tasks ([@Vitalina-Vakulchyk][]):
62
+ * for `rake chewy:reset` and `rake chewy:parallel:reset`
63
+ * progressbar is hidden by default, set `ENV['PROGRESS']` to `true` to display it
64
+
65
+ ### Bugs Fixed
66
+
67
+ * [#796](https://github.com/toptal/chewy/pull/796): Fix clear scroll: pass `scroll_id` in body, as passing in path parameters is deprecated and can overflow `http.max_initial_line_length` ([@rabotyaga][])
68
+
69
+ ## 7.0.1 (2021-05-03)
70
+
71
+ ### Changes
72
+
73
+ * [#792](https://github.com/toptal/chewy/pull/792): Skip ES version memoization for search requests ([@rabotyaga][])
74
+ * See the Migration Guide for details
75
+
11
76
  ## 7.2.0 (2021-04-19)
12
77
 
13
78
  ### New Features
@@ -580,9 +645,11 @@
580
645
  * Initial version
581
646
 
582
647
  [@0x0badc0de]: https://github.com/0x0badc0de
648
+ [@AgeevAndrew]: https://github.com/AgeevAndrew
583
649
  [@aglushkov]: https://github.com/aglushkov
584
650
  [@AlexVPopov]: https://github.com/AlexVPopov
585
651
  [@AndreySavelyev]: https://github.com/AndreySavelyev
652
+ [@afg419]: https://github.com/afg419
586
653
  [@arion]: https://github.com/arion
587
654
  [@arturtr]: https://github.com/arturtr
588
655
  [@averell23]: https://github.com/averell23
@@ -593,6 +660,7 @@
593
660
  [@biow0lf]: https://github.com/biow0lf
594
661
  [@Borzik]: https://github.com/Borzik
595
662
  [@caldwecr]: https://github.com/caldwecr
663
+ [@chrisandreae]: https://github.com/chrisandreae
596
664
  [@clupprich]: https://github.com/clupprich
597
665
  [@dalthon]: https://github.com/dalthon
598
666
  [@davekaro]: https://github.com/davekaro
@@ -612,6 +680,7 @@
612
680
  [@inbeom]: https://github.com/inbeom
613
681
  [@jesjos]: https://github.com/jesjos
614
682
  [@JF-Lalonde]: https://github.com/JF-Lalonde
683
+ [@jiajiawang]: https://github.com/jiajiawang
615
684
  [@jimmybaker]: https://github.com/jimmybaker
616
685
  [@jirikolarik]: https://github.com/jirikolarik
617
686
  [@jirutka]: https://github.com/jirutka
@@ -622,6 +691,7 @@
622
691
  [@josephchoe]: https://github.com/josephchoe
623
692
  [@jshirley]: https://github.com/jshirley
624
693
  [@ka8725]: https://github.com/ka8725
694
+ [@kolauren]: https://github.com/kolauren
625
695
  [@konalegi]: https://github.com/konalegi
626
696
  [@lardawge]: https://github.com/lardawge
627
697
  [@leemhenson]: https://github.com/leemhenson
@@ -635,6 +705,7 @@
635
705
  [@mattzollinhofer]: https://github.com/mattzollinhofer
636
706
  [@menglewis]: https://github.com/menglewis
637
707
  [@mikeyhogarth]: https://github.com/mikeyhogarth
708
+ [@milk1000cc]: https://github.com/milk1000cc
638
709
  [@mkcode]: https://github.com/mkcode
639
710
  [@mpeychich]: https://github.com/mpeychich
640
711
  [@mrbrdo]: https://github.com/mrbrdo
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
@@ -53,11 +53,16 @@ Chewy is compatible with MRI 2.6-3.0¹.
53
53
  | ------------- | ---------------------------------- |
54
54
  | 7.2.x | 7.x |
55
55
  | 7.1.x | 7.x |
56
- | 7.0.0 | 6.8, 7.x |
56
+ | 7.0.x | 6.8, 7.x |
57
57
  | 6.0.0 | 5.x, 6.x |
58
58
  | 5.x | 5.x, limited support for 1.x & 2.x |
59
59
 
60
- See [Migration guide](migration_guide.md).
60
+ **Important:** Chewy doesn't follow SemVer, so you should always
61
+ check the release notes before upgrading. The major version is linked to the
62
+ newest supported Elasticsearch and the minor version bumps may include breaking changes.
63
+
64
+ See our [migration guide](migration_guide.md) for detailed upgrade instructions between
65
+ various Chewy versions.
61
66
 
62
67
  ### Active Record
63
68
 
@@ -97,7 +102,7 @@ $ docker run --rm --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.t
97
102
 
98
103
  ### Index
99
104
 
100
- Create `app/chewy/user_index.rb` with User Index:
105
+ Create `app/chewy/users_index.rb` with User Index:
101
106
 
102
107
  ```ruby
103
108
  class UsersIndex < Chewy::Index
@@ -335,7 +340,7 @@ Chewy.settings = {
335
340
  [See index settings here](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html).
336
341
  [See root object settings here](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html).
337
342
 
338
- See [mapping.rb](lib/chewy/type/mapping.rb) for more details.
343
+ See [mapping.rb](lib/chewy/index/mapping.rb) for more details.
339
344
 
340
345
  5. Add model-observing code
341
346
 
@@ -441,6 +446,23 @@ end
441
446
 
442
447
  See the section on *Script fields* for details on calculating distance in a search.
443
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
+
444
466
  ### Crutches™ technology
445
467
 
446
468
  Assume you are defining your index like this (product has_many categories through product_categories):
@@ -954,7 +976,7 @@ Main methods of the request DSL are: `query`, `filter` and `post_filter`, it is
954
976
  ```ruby
955
977
  CitiesIndex
956
978
  .filter(term: {name: 'Bangkok'})
957
- .query { match name: 'London' }
979
+ .query(match: {name: 'London'})
958
980
  .query.not(range: {population: {gt: 1_000_000}})
959
981
  ```
960
982
 
@@ -964,7 +986,7 @@ You can query a set of indexes at once:
964
986
  CitiesIndex.indices(CountriesIndex).query(match: {name: 'Some'})
965
987
  ```
966
988
 
967
- 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.
968
990
 
969
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.
970
992
 
@@ -1106,7 +1128,16 @@ rake chewy:journal:apply["$(date -v-1H -u +%FT%TZ)",users] # apply journaled cha
1106
1128
 
1107
1129
  ### RSpec integration
1108
1130
 
1109
- Just add `require 'chewy/rspec'` to your spec_helper.rb and you will get additional features: See [update_index.rb](lib/chewy/rspec/update_index.rb) for more details.
1131
+ Just add `require 'chewy/rspec'` to your spec_helper.rb and you will get additional features:
1132
+
1133
+ [update_index](lib/chewy/rspec/update_index.rb) helper
1134
+ `mock_elasticsearch_response` helper to mock elasticsearch response
1135
+ `mock_elasticsearch_response_sources` helper to mock elasticsearch response sources
1136
+ `build_query` matcher to compare request and expected query (returns `true`/`false`)
1137
+
1138
+ To use `mock_elasticsearch_response` and `mock_elasticsearch_response_sources` helpers add `include Chewy::Rspec::Helpers` to your tests.
1139
+
1140
+ See [chewy/rspec/](lib/chewy/rspec/) for more details.
1110
1141
 
1111
1142
  ### Minitest integration
1112
1143
 
@@ -1116,6 +1147,14 @@ Since you can set `:bypass` strategy for test suites and manually handle import
1116
1147
 
1117
1148
  But if you require chewy to index/update model regularly in your test suite then you can specify `:urgent` strategy for documents indexing. Add `Chewy.strategy(:urgent)` to test_helper.rb.
1118
1149
 
1150
+ Also, you can use additional helpers:
1151
+
1152
+ `mock_elasticsearch_response` to mock elasticsearch response
1153
+ `mock_elasticsearch_response_sources` to mock elasticsearch response sources
1154
+ `assert_elasticsearch_query` to compare request and expected query (returns `true`/`false`)
1155
+
1156
+ See [chewy/minitest/](lib/chewy/minitest/) for more details.
1157
+
1119
1158
  ### DatabaseCleaner
1120
1159
 
1121
1160
  If you use `DatabaseCleaner` in your tests with [the `transaction` strategy](https://github.com/DatabaseCleaner/database_cleaner#how-to-use), you may run into the problem that `ActiveRecord`'s models are not indexed automatically on save despite the fact that you set the callbacks to do this with the `update_index` method. The issue arises because `chewy` indices data on `after_commit` run as default, but all `after_commit` callbacks are not run with the `DatabaseCleaner`'s' `transaction` strategy. You can solve this issue by changing the `Chewy.use_after_commit_callbacks` option. Just add the following initializer in your Rails application:
data/chewy.gemspec CHANGED
@@ -31,6 +31,6 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
31
31
  spec.add_development_dependency 'unparser'
32
32
 
33
33
  spec.add_dependency 'activesupport', '>= 5.2'
34
- spec.add_dependency 'elasticsearch', '>= 6.3.0'
34
+ spec.add_dependency 'elasticsearch', '>= 7.12.0', '< 7.14.0'
35
35
  spec.add_dependency 'elasticsearch-dsl'
36
36
  end
@@ -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/config.rb CHANGED
@@ -133,7 +133,7 @@ module Chewy
133
133
 
134
134
  if File.exist?(file)
135
135
  yaml = ERB.new(File.read(file)).result
136
- hash = YAML.load(yaml) # rubocop:disable Security/YAMLLoad
136
+ hash = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(yaml) : YAML.load(yaml) # rubocop:disable Security/YAMLLoad
137
137
  hash[Rails.env].try(:deep_symbolize_keys) if hash
138
138
  end
139
139
  end || {}
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