chewy 5.0.0 → 5.1.0

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: 2decd9fc56f1c1116f0470c5dfce2fb84b3dbbab25ed42bcc1813f2f91069292
4
- data.tar.gz: 969f6d74cff1f399ffa9752d2559a9affcaa2fe96a63e05cdfe4aa2b0add0af4
3
+ metadata.gz: 55ee493bcdd9f0f001572ffade6a8fef187fd9fd0a87176dd52f07b18db6ec39
4
+ data.tar.gz: 1ed5e0e4d39c7e8b2202051322c7c312078ba644c62f49c320153d6bd2054ee7
5
5
  SHA512:
6
- metadata.gz: e55c97184ed6f171f9383087b792b6261167687acad1add6ca2d5196e87676b1600c54277174e213fd07de6d6d811124a20130ac7ca7ccb5b7077a78a710fbbc
7
- data.tar.gz: b0ceb62da09eea3332e1d67bdca2ab56dfadae6366ca27a8009e502da01057891e7c6a33bfb67f81f4b35119eff550b4390d40e9860e2441065d72c4d30fca78
6
+ metadata.gz: 9fe7425716df8484306cee104f3b3365a9ae36d2e6496b0653b88dd9a13f8e78f48a6d4f579f0e9a2887fb2d36632a1bd970c901d3ba10c87f9fd6405b56327d
7
+ data.tar.gz: f1c81281f0343256c47b20e52bf9363df6bc1c86f3c53e031f4facdb93d223fe6685e74ea3b048b5869a8d0c09ba0a0672c100a303d8b6cabd358e71de141c03
@@ -31,7 +31,7 @@ matrix:
31
31
  - rvm: 2.3.6
32
32
  gemfile: gemfiles/rails.4.2.mongoid.5.2.gemfile
33
33
  env: ES_VERSION=2.4.6
34
- - rvm: 2.5.0
34
+ - rvm: 2.5.1
35
35
  gemfile: gemfiles/rails.5.2.activerecord.gemfile
36
36
  env: ES_VERSION=5.6.7
37
37
  before_install:
data/Appraisals CHANGED
@@ -16,7 +16,7 @@
16
16
  end
17
17
  end
18
18
 
19
- %w[5.0 5.1].each do |activesupport|
19
+ %w[5.0 5.1 5.2].each do |activesupport|
20
20
  appraise "rails.#{activesupport}.activerecord" do
21
21
  gem 'activerecord', "~> #{activesupport}.0"
22
22
  gem 'activesupport', "~> #{activesupport}.0"
@@ -34,22 +34,6 @@ end
34
34
  end
35
35
  end
36
36
 
37
- appraise 'rails.5.2.activerecord' do
38
- gem 'activerecord', '~> 5.2.0.rc1'
39
- gem 'activesupport', '~> 5.2.0.rc1'
40
-
41
- gem 'activejob', '~> 5.2.0.rc1'
42
- gem 'resque', require: false
43
- gem 'shoryuken', require: false
44
- gem 'aws-sdk-sqs', require: false
45
- gem 'sidekiq', require: false
46
-
47
- gem 'kaminari-core', '~> 1.1.0', require: false
48
- gem 'will_paginate', require: false
49
-
50
- gem 'parallel', require: false
51
- end
52
-
53
37
  appraise 'rails.4.2.mongoid.5.2' do
54
38
  gem 'mongoid', '~> 5.2.0'
55
39
  gem 'activesupport', '~> 4.2.0'
@@ -1,5 +1,19 @@
1
1
  # master
2
2
 
3
+ # Version 5.1.0
4
+
5
+ ## Breaking changes
6
+
7
+ * Add support for multiple indices in request (@pyromaniac, #657)
8
+
9
+ * Support `search_type`, `request_cache`, and `allow_partial_search_results` as query string parameters (@mattzollinhofer, #647)
10
+
11
+ ## Changes
12
+
13
+ * Speed up imports when `bulk_size` is specified (@yahooguntu, #606)
14
+
15
+ * Insert `RequestStrategy` middleware before `ActionDispatch::ShowExceptions` (@dck, #682)
16
+
3
17
  # Version 5.0.0
4
18
 
5
19
  ## Breaking changes
data/README.md CHANGED
@@ -34,7 +34,7 @@ Chewy is an ODM and wrapper for [the official Elasticsearch client](https://gith
34
34
  * [Non-block notation](#non-block-notation)
35
35
  * [Designing your own strategies](#designing-your-own-strategies)
36
36
  * [Rails application strategies integration](#rails-application-strategies-integration)
37
- * [ActiveSupport::Notifications support](#activesupport-notifications-support)
37
+ * [ActiveSupport::Notifications support](#activesupportnotifications-support)
38
38
  * [NewRelic integration](#newrelic-integration)
39
39
  * [Search requests](#search-requests)
40
40
  * [Composing requests](#composing-requests)
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec| # rubocop:disable BlockLength
22
22
  spec.add_development_dependency 'elasticsearch-extensions'
23
23
  spec.add_development_dependency 'rake'
24
24
  spec.add_development_dependency 'resque_spec'
25
- spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'rspec', '~> 3.7.0'
26
26
  spec.add_development_dependency 'rspec-collection_matchers'
27
27
  spec.add_development_dependency 'rspec-its'
28
28
  spec.add_development_dependency 'rubocop', '0.52.1'
@@ -2,9 +2,9 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 5.2.0.rc1"
6
- gem "activesupport", "~> 5.2.0.rc1"
7
- gem "activejob", "~> 5.2.0.rc1"
5
+ gem "activerecord", "~> 5.2.0"
6
+ gem "activesupport", "~> 5.2.0"
7
+ gem "activejob", "~> 5.2.0"
8
8
  gem "resque", require: false
9
9
  gem "shoryuken", require: false
10
10
  gem "aws-sdk-sqs", require: false
@@ -40,6 +40,11 @@ module Chewy
40
40
  @criteria = Criteria.new
41
41
  end
42
42
 
43
+ # A compatibility layer with the new request DSL.
44
+ def render
45
+ _request
46
+ end
47
+
43
48
  # Comparation with other query or collection
44
49
  # If other is collection - search request is executed and
45
50
  # result is used for comparation
@@ -68,7 +68,7 @@ module Chewy
68
68
  end
69
69
 
70
70
  initializer 'chewy.request_strategy' do |app|
71
- app.config.middleware.insert_after(Rails::Rack::Logger, RequestStrategy)
71
+ app.config.middleware.insert_before(ActionDispatch::ShowExceptions, RequestStrategy)
72
72
  end
73
73
 
74
74
  initializer 'chewy.add_indices_path' do |_app|
@@ -58,11 +58,7 @@ module Chewy
58
58
  # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
59
59
  # @return [Hash] the request result
60
60
  def search_string(query, options = {})
61
- options = options.merge(
62
- index: all._indexes.map(&:index_name),
63
- type: all._types.map(&:type_name),
64
- q: query
65
- )
61
+ options = options.merge(all.render.slice(:index, :type).merge(q: query))
66
62
  Chewy.client.search(options)
67
63
  end
68
64
 
@@ -10,6 +10,8 @@ module Chewy
10
10
  # @see Chewy::Search::Request#parameters
11
11
  # @see Chewy::Search::Parameters::Storage
12
12
  class Parameters
13
+ QUERY_STRING_STORAGES = %i[indices search_type request_cache allow_partial_search_results].freeze
14
+
13
15
  # Default storage classes warehouse. It is probably possible to
14
16
  # add your own classes here if necessary, but I'm not sure it will work.
15
17
  #
@@ -101,11 +103,7 @@ module Chewy
101
103
  #
102
104
  # @return [Hash] request body
103
105
  def render
104
- body = @storages.except(:filter, :query, :none).values.inject({}) do |result, storage|
105
- result.merge!(storage.render || {})
106
- end
107
- body.merge!(render_query || {})
108
- body.present? ? {body: body} : {}
106
+ render_query_string_params.merge(render_body)
109
107
  end
110
108
 
111
109
  protected
@@ -126,6 +124,25 @@ module Chewy
126
124
  names
127
125
  end
128
126
 
127
+ def render_query_string_params
128
+ query_string_storages = @storages.select do |storage_name, _|
129
+ QUERY_STRING_STORAGES.include?(storage_name)
130
+ end
131
+
132
+ query_string_storages.values.inject({}) do |result, storage|
133
+ result.merge!(storage.render || {})
134
+ end
135
+ end
136
+
137
+ def render_body
138
+ exceptions = %i[filter query none] + QUERY_STRING_STORAGES
139
+ body = @storages.except(*exceptions).values.inject({}) do |result, storage|
140
+ result.merge!(storage.render || {})
141
+ end
142
+ body.merge!(render_query || {})
143
+ {body: body}
144
+ end
145
+
129
146
  def render_query
130
147
  none = @storages[:none].render
131
148
 
@@ -0,0 +1,27 @@
1
+ require 'chewy/search/parameters/storage'
2
+
3
+ module Chewy
4
+ module Search
5
+ class Parameters
6
+ # Stores boolean value, but has 3 states: `true`, `false` and `nil`.
7
+ #
8
+ # @see Chewy::Search::Request#allow_partial_search_results
9
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/6.4/search-request-body.html#_parameters_4
10
+ class AllowPartialSearchResults < Storage
11
+ # We don't want to render `nil`, but render `true` and `false` values.
12
+ #
13
+ # @see Chewy::Search::Parameters::Storage#render
14
+ # @return [{Symbol => Object}, nil]
15
+ def render
16
+ {self.class.param_name => value} unless value.nil?
17
+ end
18
+
19
+ private
20
+
21
+ def normalize(value)
22
+ !!value unless value.nil?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,123 @@
1
+ require 'chewy/search/parameters/storage'
2
+
3
+ module Chewy
4
+ module Search
5
+ class Parameters
6
+ # Stores indices and/or types to query.
7
+ # Renders it to lists of string accepted by ElasticSearch
8
+ # API.
9
+ #
10
+ # The semantics behind it can be described in the
11
+ # following statements:
12
+ # 1. If index is added to the storage, no matter, a class
13
+ # or a string/symbol, it gets appended to the list.
14
+ # 2. If type is added to the storage, it filters out types
15
+ # assigned via indices.
16
+ # 3. But when a type class with non-existing index is added,
17
+ # this index got also added to the list if indices.
18
+ # 4. In cases when of an index identifier added, type
19
+ # indetifiers also got appended instead of filtering.
20
+ class Indices < Storage
21
+ # Two index storages are equal if they produce the
22
+ # same output on render.
23
+ #
24
+ # @see Chewy::Search::Parameters::Storage#==
25
+ # @param other [Chewy::Search::Parameters::Storage] any storage instance
26
+ # @return [true, false] the result of comparision
27
+ def ==(other)
28
+ super || other.class == self.class && other.render == render
29
+ end
30
+
31
+ # Just adds types to types and indices to indices.
32
+ #
33
+ # @see Chewy::Search::Parameters::Storage#update!
34
+ # @param other_value [{Symbol => Array<Chewy::Index, Chewy::Type, String, Symbol>}] any acceptable storage value
35
+ # @return [{Symbol => Array<Chewy::Index, Chewy::Type, String, Symbol>}] updated value
36
+ def update!(other_value)
37
+ new_value = normalize(other_value)
38
+
39
+ @value = {
40
+ indices: value[:indices] | new_value[:indices],
41
+ types: value[:types] | new_value[:types]
42
+ }
43
+ end
44
+
45
+ # Returns desired index and type names.
46
+ #
47
+ # @see Chewy::Search::Parameters::Storage#render
48
+ # @return [{Symbol => Array<String>}] rendered value with the parameter name
49
+ def render
50
+ {
51
+ index: index_names.uniq.sort,
52
+ type: type_names.uniq.sort
53
+ }.reject { |_, v| v.blank? }
54
+ end
55
+
56
+ # Returns index classes used for the request.
57
+ # No strings/symbos included.
58
+ #
59
+ # @return [Array<Chewy::Index>] a list of index classes
60
+ def indices
61
+ index_classes | type_classes.map(&:index)
62
+ end
63
+
64
+ # Returns type classes used for the request.
65
+ # No strings/symbos included.
66
+ #
67
+ # @return [Array<Chewy::Type>] a list of types classes
68
+ def types
69
+ type_classes | (index_classes - type_classes.map(&:index)).flat_map(&:types)
70
+ end
71
+
72
+ private
73
+
74
+ def initialize_clone(origin)
75
+ @value = origin.value.dup
76
+ end
77
+
78
+ def normalize(value)
79
+ value ||= {}
80
+
81
+ {
82
+ indices: Array.wrap(value[:indices]).flatten.compact,
83
+ types: Array.wrap(value[:types]).flatten.compact
84
+ }
85
+ end
86
+
87
+ def index_classes
88
+ value[:indices].select do |klass|
89
+ klass.is_a?(Class) && klass < Chewy::Index
90
+ end
91
+ end
92
+
93
+ def index_identifiers
94
+ value[:indices] - index_classes
95
+ end
96
+
97
+ def index_names
98
+ indices.map(&:index_name) | index_identifiers.map(&:to_s)
99
+ end
100
+
101
+ def type_classes
102
+ value[:types].select do |klass|
103
+ klass.is_a?(Class) && klass < Chewy::Type
104
+ end
105
+ end
106
+
107
+ def type_identifiers
108
+ value[:types] - type_classes
109
+ end
110
+
111
+ def type_names
112
+ type_names = types.map(&:type_name)
113
+
114
+ if index_identifiers.blank? && type_identifiers.present?
115
+ (type_names & type_identifiers.map(&:to_s)).presence || type_names
116
+ else
117
+ type_names | type_identifiers.map(&:to_s)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,6 +1,6 @@
1
1
  module Chewy
2
2
  module Search
3
- # The main requset DSL class. Supports multiple index requests.
3
+ # The main request DSL class. Supports multiple index requests.
4
4
  # Supports ES2 and ES5 search API and query DSL.
5
5
  #
6
6
  # @note The class tries to be as immutable as possible,
@@ -26,8 +26,9 @@ module Chewy
26
26
  timeout min_score source stored_fields search_after
27
27
  load script_fields suggest aggs aggregations none
28
28
  indices_boost rescore highlight total total_count
29
- total_entries types delete_all count exists? exist? find pluck
30
- scroll_batches scroll_hits scroll_results scroll_wrappers
29
+ total_entries indices types delete_all count exists?
30
+ exist? find pluck scroll_batches scroll_hits
31
+ scroll_results scroll_wrappers
31
32
  ].to_set.freeze
32
33
  DEFAULT_BATCH_SIZE = 1000
33
34
  DEFAULT_PLUCK_BATCH_SIZE = 10_000
@@ -51,24 +52,32 @@ module Chewy
51
52
  alias_method :total_count, :total
52
53
  alias_method :total_entries, :total
53
54
 
54
- attr_reader :_indexes, :_types
55
-
56
55
  # The class is initialized with the list of chewy indexes and/or
57
56
  # types, which are later used to compose requests.
57
+ # Any symbol/string passed is treated as an index identifier.
58
58
  #
59
59
  # @example
60
+ # Chewy::Search::Request.new(:places)
61
+ # # => <Chewy::Search::Request {:index=>["places"]}>
60
62
  # Chewy::Search::Request.new(PlacesIndex)
61
63
  # # => <Chewy::Search::Request {:index=>["places"], :type=>["city", "country"]}>
62
64
  # Chewy::Search::Request.new(PlacesIndex::City)
63
65
  # # => <Chewy::Search::Request {:index=>["places"], :type=>["city"]}>
64
66
  # Chewy::Search::Request.new(UsersIndex, PlacesIndex::City)
65
67
  # # => <Chewy::Search::Request {:index=>["users", "places"], :type=>["city", "user"]}>
66
- # @param indexes_or_types [Array<Chewy::Index, Chewy::Type>] indexes and types in any combinations
67
- def initialize(*indexes_or_types)
68
- @_types = indexes_or_types.select { |klass| klass < Chewy::Type }
69
- @_indexes = indexes_or_types.select { |klass| klass < Chewy::Index }
70
- @_types |= @_indexes.flat_map(&:types)
71
- @_indexes |= @_types.map(&:index)
68
+ # @param indexes_or_types [Array<Chewy::Index, Chewy::Type, String, Symbol>] indices and types in any combinations
69
+ def initialize(*indices_or_types)
70
+ indices = indices_or_types.reject do |klass|
71
+ klass.is_a?(Class) && klass < Chewy::Type
72
+ end
73
+
74
+ types = indices_or_types.select do |klass|
75
+ klass.is_a?(Class) && klass < Chewy::Type
76
+ end
77
+
78
+ parameters.modify!(:indices) do
79
+ replace!(indices: indices, types: types)
80
+ end
72
81
  end
73
82
 
74
83
  # Underlying parameter storage collection.
@@ -110,7 +119,7 @@ module Chewy
110
119
  #
111
120
  # @return [Hash] request body
112
121
  def render
113
- @render ||= render_base.merge(parameters.render)
122
+ @render ||= parameters.render
114
123
  end
115
124
 
116
125
  # Includes the class name and the result of rendering.
@@ -281,21 +290,39 @@ module Chewy
281
290
  # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-docvalue-fields.html
282
291
  # @param values [Array<String, Symbol>] field names
283
292
  # @return [Chewy::Search::Request]
293
+ %i[order docvalue_fields].each do |name|
294
+ define_method name do |value, *values|
295
+ modify(name) { update!([value, *values]) }
296
+ end
297
+ end
298
+
299
+ # @!method indices(*values)
300
+ # Modifies `index` request parameter. Updates the storage on every call.
301
+ # Added passed indexes to the parameter list.
302
+ #
303
+ # @example
304
+ # UsersIndex.indices(CitiesIndex).indices(:another)
305
+ # # => <UsersIndex::Query {:index=>["another", "cities", "users"], :type=>["city", "user"]}>
306
+ # @see Chewy::Search::Parameters::Indices
307
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
308
+ # @param values [Array<Chewy::Index, String, Symbol>] index names
309
+ # @return [Chewy::Search::Request]
284
310
  #
285
311
  # @!method types(*values)
286
- # Modifies `types` request parameter. Updates the storage on every call.
287
- # Constrains types passed on the request initialization.
312
+ # Modifies `type` request parameter. Updates the storage on every call.
313
+ # Constrains types passed on the request initialization or adds them
314
+ # to the list depending on circumstances.
288
315
  #
289
316
  # @example
290
- # PlacesIndex.types(:city).types(:unexistent)
291
- # # => <PlacesIndex::Query {:index=>["places"], :type=>["city"]}>
292
- # @see Chewy::Search::Parameters::Types
317
+ # UsersIndex.types(CitiesIndex::City).types(:unexistent)
318
+ # # => <UsersIndex::Query {:index=>["cities", "users"], :type=>["city", "user"]}>
319
+ # @see Chewy::Search::Parameters::Indices
293
320
  # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
294
- # @param values [Array<String, Symbol>] type names
321
+ # @param values [Array<Chewy::Type, String, Symbol>] type names
295
322
  # @return [Chewy::Search::Request]
296
- %i[order docvalue_fields types].each do |name|
323
+ %i[indices types].each do |name|
297
324
  define_method name do |value, *values|
298
- modify(name) { update!([value, *values]) }
325
+ modify(:indices) { update!(name => [value, *values]) }
299
326
  end
300
327
  end
301
328
 
@@ -759,7 +786,7 @@ module Chewy
759
786
  # @param values [Array<String, Symbol>]
760
787
  # @return [Chewy::Search::Request] new scope
761
788
  def only(*values)
762
- chain { parameters.only!(values.flatten(1)) }
789
+ chain { parameters.only!(values.flatten(1) + [:indices]) }
763
790
  end
764
791
 
765
792
  # Returns a new scope containing all the storages except specified.
@@ -912,9 +939,7 @@ module Chewy
912
939
  def delete_all(refresh: true)
913
940
  request_body = only(WHERE_STORAGES).render.merge(refresh: refresh)
914
941
  ActiveSupport::Notifications.instrument 'delete_query.chewy',
915
- request: request_body, indexes: _indexes, types: _types,
916
- index: _indexes.one? ? _indexes.first : _indexes,
917
- type: _types.one? ? _types.first : _types do
942
+ notification_payload(request: request_body) do
918
943
  if Runtime.version < '5.0'
919
944
  delete_by_query_plugin(request_body)
920
945
  else
@@ -933,9 +958,7 @@ module Chewy
933
958
  private
934
959
 
935
960
  def compare_internals(other)
936
- _indexes.sort_by(&:derivable_name) == other._indexes.sort_by(&:derivable_name) &&
937
- _types.sort_by(&:derivable_name) == other._types.sort_by(&:derivable_name) &&
938
- parameters == other.parameters
961
+ parameters == other.parameters
939
962
  end
940
963
 
941
964
  def modify(name, &block)
@@ -947,45 +970,43 @@ module Chewy
947
970
  end
948
971
 
949
972
  def reset
950
- @response, @render, @render_base, @type_names, @index_names, @loader = nil
973
+ @response, @render, @loader = nil
951
974
  end
952
975
 
953
976
  def perform(additional = {})
954
977
  request_body = render.merge(additional)
955
978
  ActiveSupport::Notifications.instrument 'search_query.chewy',
956
- request: request_body, indexes: _indexes, types: _types,
957
- index: _indexes.one? ? _indexes.first : _indexes,
958
- type: _types.one? ? _types.first : _types do
959
- begin
960
- Chewy.client.search(request_body)
961
- rescue Elasticsearch::Transport::Transport::Errors::NotFound
962
- {}
979
+ notification_payload(request: request_body) do
980
+ begin
981
+ Chewy.client.search(request_body)
982
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
983
+ {}
984
+ end
963
985
  end
964
- end
965
986
  end
966
987
 
967
- def raw_limit_value
968
- parameters[:limit].value
988
+ def notification_payload(additional)
989
+ {
990
+ indexes: _indices, types: _types,
991
+ index: _indices.one? ? _indices.first : _indices,
992
+ type: _types.one? ? _types.first : _types
993
+ }.merge(additional)
969
994
  end
970
995
 
971
- def raw_offset_value
972
- parameters[:offset].value
996
+ def _indices
997
+ parameters[:indices].indices
973
998
  end
974
999
 
975
- def index_names
976
- @index_names ||= _indexes.map(&:index_name).uniq
1000
+ def _types
1001
+ parameters[:indices].types
977
1002
  end
978
1003
 
979
- def type_names
980
- @type_names ||= if parameters[:types].value.present?
981
- _types.map(&:type_name).uniq & parameters[:types].value
982
- else
983
- _types.map(&:type_name).uniq
984
- end
1004
+ def raw_limit_value
1005
+ parameters[:limit].value
985
1006
  end
986
1007
 
987
- def render_base
988
- @render_base ||= {index: index_names, type: type_names, body: {}}
1008
+ def raw_offset_value
1009
+ parameters[:offset].value
989
1010
  end
990
1011
 
991
1012
  def delete_by_query_plugin(request)
@@ -998,7 +1019,10 @@ module Chewy
998
1019
  end
999
1020
 
1000
1021
  def loader
1001
- @loader ||= Loader.new(indexes: @_indexes, **parameters[:load].value)
1022
+ @loader ||= Loader.new(
1023
+ indexes: parameters[:indices].indices,
1024
+ **parameters[:load].value
1025
+ )
1002
1026
  end
1003
1027
 
1004
1028
  def fetch_field(hit, field)
@@ -125,11 +125,9 @@ module Chewy
125
125
 
126
126
  def perform_scroll(body)
127
127
  ActiveSupport::Notifications.instrument 'search_query.chewy',
128
- request: body, indexes: _indexes, types: _types,
129
- index: _indexes.one? ? _indexes.first : _indexes,
130
- type: _types.one? ? _types.first : _types do
131
- Chewy.client.scroll(body)
132
- end
128
+ notification_payload(request: body) do
129
+ Chewy.client.scroll(body)
130
+ end
133
131
  end
134
132
  end
135
133
  end
@@ -54,15 +54,17 @@ module Chewy
54
54
 
55
55
  def request_bodies(body)
56
56
  if @bulk_size
57
+ serializer = ::Elasticsearch::API.serializer
57
58
  pieces = body.each_with_object(['']) do |piece, result|
58
59
  operation, meta = piece.to_a.first
59
60
  data = meta.delete(:data)
60
- piece = [{operation => meta}, data].compact.map(&:to_json).join("\n")
61
+ piece = serializer.dump(operation => meta)
62
+ piece << "\n" << serializer.dump(data) if data.present?
61
63
 
62
64
  if result.last.bytesize + piece.bytesize > @bulk_size
63
65
  result.push(piece)
64
66
  else
65
- result[-1] = [result[-1], piece].reject(&:blank?).join("\n")
67
+ result[-1].blank? ? (result[-1] = piece) : (result[-1] << "\n" << piece)
66
68
  end
67
69
  end
68
70
  pieces.each { |piece| piece << "\n" }
@@ -31,7 +31,7 @@ module Chewy
31
31
  index_name: @type.index.derivable_name,
32
32
  type_name: @type.type_name,
33
33
  action: action,
34
- references: identify(objects).map(&:to_json).map(&Base64.method(:encode64)),
34
+ references: identify(objects).map { |item| Base64.encode64(::Elasticsearch::API.serializer.dump(item)) },
35
35
  created_at: Time.now.utc
36
36
  }
37
37
  end
@@ -41,7 +41,7 @@ module Chewy
41
41
 
42
42
  %w[_id _type _index].each do |name|
43
43
  define_method name do
44
- data[name]
44
+ _data[name]
45
45
  end
46
46
  end
47
47
 
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '5.0.0'.freeze
2
+ VERSION = '5.1.0'.freeze
3
3
  end
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Search::Parameters::Indices do
4
+ before do
5
+ stub_index(:first) do
6
+ define_type :one
7
+ define_type :two
8
+ end
9
+
10
+ stub_index(:second) do
11
+ define_type :three
12
+ end
13
+ end
14
+
15
+ subject { described_class.new(indices: FirstIndex, types: SecondIndex::Three) }
16
+
17
+ describe '#initialize' do
18
+ specify { expect(described_class.new.value).to eq(indices: [], types: []) }
19
+ specify { expect(described_class.new(nil).value).to eq(indices: [], types: []) }
20
+ specify { expect(described_class.new(foo: :whatever).value).to eq(indices: [], types: []) }
21
+ specify { expect(subject.value).to eq(indices: [FirstIndex], types: [SecondIndex::Three]) }
22
+ end
23
+
24
+ describe '#replace!' do
25
+ specify do
26
+ expect { subject.replace!(nil) }
27
+ .to change { subject.value }
28
+ .from(indices: [FirstIndex], types: [SecondIndex::Three])
29
+ .to(indices: [], types: [])
30
+ end
31
+
32
+ specify do
33
+ expect { subject.replace!(indices: SecondIndex, types: FirstIndex::One) }
34
+ .to change { subject.value }
35
+ .from(indices: [FirstIndex], types: [SecondIndex::Three])
36
+ .to(indices: [SecondIndex], types: [FirstIndex::One])
37
+ end
38
+ end
39
+
40
+ describe '#update!' do
41
+ specify do
42
+ expect { subject.update!(nil) }
43
+ .not_to change { subject.value }
44
+ end
45
+
46
+ specify do
47
+ expect { subject.update!(indices: SecondIndex, types: [FirstIndex::One, SecondIndex::Three]) }
48
+ .to change { subject.value }
49
+ .from(indices: [FirstIndex], types: [SecondIndex::Three])
50
+ .to(indices: [FirstIndex, SecondIndex], types: [SecondIndex::Three, FirstIndex::One])
51
+ end
52
+ end
53
+
54
+ describe '#merge!' do
55
+ specify do
56
+ expect { subject.merge!(described_class.new) }
57
+ .not_to change { subject.value }
58
+ end
59
+
60
+ specify do
61
+ expect { subject.merge!(described_class.new(indices: SecondIndex, types: [FirstIndex::One, SecondIndex::Three])) }
62
+ .to change { subject.value }
63
+ .from(indices: [FirstIndex], types: [SecondIndex::Three])
64
+ .to(indices: [FirstIndex, SecondIndex], types: [SecondIndex::Three, FirstIndex::One])
65
+ end
66
+ end
67
+
68
+ describe '#render' do
69
+ specify { expect(described_class.new.render).to eq({}) }
70
+ specify do
71
+ expect(described_class.new(
72
+ indices: FirstIndex
73
+ ).render).to eq(index: %w[first], type: %w[one two])
74
+ end
75
+ specify do
76
+ expect(described_class.new(
77
+ indices: :whatever
78
+ ).render).to eq(index: %w[whatever])
79
+ end
80
+ specify do
81
+ expect(described_class.new(
82
+ types: FirstIndex::One
83
+ ).render).to eq(index: %w[first], type: %w[one])
84
+ end
85
+ specify do
86
+ expect(described_class.new(
87
+ types: :whatever
88
+ ).render).to eq({})
89
+ end
90
+ specify do
91
+ expect(described_class.new(
92
+ indices: FirstIndex, types: SecondIndex::Three
93
+ ).render).to eq(index: %w[first second], type: %w[one three two])
94
+ end
95
+ specify do
96
+ expect(described_class.new(
97
+ indices: FirstIndex, types: :one
98
+ ).render).to eq(index: %w[first], type: %w[one])
99
+ end
100
+ specify do
101
+ expect(described_class.new(
102
+ indices: FirstIndex, types: :whatever
103
+ ).render).to eq(index: %w[first], type: %w[one two])
104
+ end
105
+ specify do
106
+ expect(described_class.new(
107
+ indices: FirstIndex, types: %i[one whatever]
108
+ ).render).to eq(index: %w[first], type: %w[one])
109
+ end
110
+ specify do
111
+ expect(described_class.new(
112
+ indices: :whatever, types: SecondIndex::Three
113
+ ).render).to eq(index: %w[second whatever], type: %w[three])
114
+ end
115
+ specify do
116
+ expect(described_class.new(
117
+ indices: :whatever, types: [SecondIndex::Three, :whatever]
118
+ ).render).to eq(index: %w[second whatever], type: %w[three whatever])
119
+ end
120
+ specify do
121
+ expect(described_class.new(
122
+ indices: [FirstIndex, :whatever], types: FirstIndex::One
123
+ ).render).to eq(index: %w[first whatever], type: %w[one])
124
+ end
125
+ specify do
126
+ expect(described_class.new(
127
+ indices: FirstIndex, types: [FirstIndex::One, :whatever]
128
+ ).render).to eq(index: %w[first], type: %w[one])
129
+ end
130
+ specify do
131
+ expect(described_class.new(
132
+ indices: FirstIndex, types: [SecondIndex::Three, :whatever]
133
+ ).render).to eq(index: %w[first second], type: %w[one three two])
134
+ end
135
+ specify do
136
+ expect(described_class.new(
137
+ indices: [FirstIndex, :whatever], types: [FirstIndex::One, :whatever]
138
+ ).render).to eq(index: %w[first whatever], type: %w[one whatever])
139
+ end
140
+ specify do
141
+ expect(described_class.new(
142
+ indices: [FirstIndex, :whatever], types: [SecondIndex::Three, FirstIndex::One]
143
+ ).render).to eq(index: %w[first second whatever], type: %w[one three])
144
+ end
145
+ end
146
+
147
+ describe '#==' do
148
+ specify { expect(described_class.new).to eq(described_class.new) }
149
+ specify do
150
+ expect(described_class.new(indices: SecondIndex, types: [SecondIndex::Three, :whatever]))
151
+ .to eq(described_class.new(indices: SecondIndex, types: :whatever))
152
+ end
153
+ specify do
154
+ expect(described_class.new(indices: :first, types: %w[one two]))
155
+ .to eq(described_class.new(indices: FirstIndex))
156
+ end
157
+ specify do
158
+ expect(described_class.new(indices: FirstIndex, types: SecondIndex::Three))
159
+ .not_to eq(described_class.new(indices: FirstIndex))
160
+ end
161
+ end
162
+
163
+ describe '#indices' do
164
+ specify do
165
+ expect(described_class.new(
166
+ indices: [FirstIndex, :whatever],
167
+ types: [SecondIndex::Three, :whatever]
168
+ ).indices).to contain_exactly(FirstIndex, SecondIndex)
169
+ end
170
+ end
171
+
172
+ describe '#types' do
173
+ specify do
174
+ expect(described_class.new(
175
+ indices: [FirstIndex, :whatever],
176
+ types: [SecondIndex::Three, :whatever]
177
+ ).types).to contain_exactly(
178
+ FirstIndex::One, FirstIndex::Two, SecondIndex::Three
179
+ )
180
+ end
181
+
182
+ specify do
183
+ expect(described_class.new(
184
+ indices: [FirstIndex, :whatever],
185
+ types: [FirstIndex::One, SecondIndex::Three, :whatever]
186
+ ).types).to contain_exactly(
187
+ FirstIndex::One, SecondIndex::Three
188
+ )
189
+ end
190
+ end
191
+ end
@@ -100,7 +100,22 @@ describe Chewy::Search::Parameters do
100
100
  subject { described_class.new(offset: 10, order: 'foo') }
101
101
 
102
102
  specify { expect(subject.render).to eq(body: {from: 10, sort: ['foo']}) }
103
- specify { expect(described_class.new.render).to eq({}) }
103
+ specify { expect(described_class.new.render).to eq(body: {}) }
104
+
105
+ context do
106
+ subject { described_class.new(request_cache: true) }
107
+ specify { expect(subject.render).to eq(body: {}, request_cache: true) }
108
+ end
109
+
110
+ context do
111
+ subject { described_class.new(search_type: 'query_then_fetch') }
112
+ specify { expect(subject.render).to eq(body: {}, search_type: 'query_then_fetch') }
113
+ end
114
+
115
+ context do
116
+ subject { described_class.new(allow_partial_search_results: true) }
117
+ specify { expect(subject.render).to eq(body: {}, allow_partial_search_results: true) }
118
+ end
104
119
 
105
120
  context do
106
121
  subject { described_class.new(query: {foo: 'bar'}, filter: {moo: 'baz'}) }
@@ -57,11 +57,11 @@ describe Chewy::Search::Request do
57
57
  describe '#inspect' do
58
58
  specify do
59
59
  expect(described_class.new(ProductsIndex).inspect)
60
- .to eq('<Chewy::Search::Request {:index=>["products"], :type=>["product", "city", "country"], :body=>{}}>')
60
+ .to eq('<Chewy::Search::Request {:index=>["products"], :type=>["city", "country", "product"], :body=>{}}>')
61
61
  end
62
62
  specify do
63
63
  expect(ProductsIndex.limit(10).inspect)
64
- .to eq('<ProductsIndex::Query {:index=>["products"], :type=>["product", "city", "country"], :body=>{:size=>10}}>')
64
+ .to eq('<ProductsIndex::Query {:index=>["products"], :type=>["city", "country", "product"], :body=>{:size=>10}}>')
65
65
  end
66
66
  end
67
67
 
@@ -151,13 +151,20 @@ describe Chewy::Search::Request do
151
151
  end
152
152
 
153
153
  describe '#request_cache' do
154
- specify { expect(subject.request_cache(true).render[:body]).to include(request_cache: true) }
155
- specify { expect(subject.request_cache(true).request_cache(false).render[:body]).to include(request_cache: false) }
156
- specify { expect(subject.request_cache(true).request_cache(nil).render[:body]).to be_blank }
154
+ specify { expect(subject.request_cache(true).render).to include(request_cache: true) }
155
+ specify { expect(subject.request_cache(true).request_cache(false).render).to include(request_cache: false) }
156
+ specify { expect(subject.request_cache(true).request_cache(nil).render[:request_cache]).to be_blank }
157
157
  specify { expect { subject.request_cache(true) }.not_to change { subject.render } }
158
158
  end
159
159
 
160
- %i[search_type preference timeout].each do |name|
160
+ describe '#search_type' do
161
+ specify { expect(subject.search_type('foo').render).to include(search_type: 'foo') }
162
+ specify { expect(subject.search_type('foo').search_type('bar').render).to include(search_type: 'bar') }
163
+ specify { expect(subject.search_type('foo').search_type(nil).render[:search_type]).to be_blank }
164
+ specify { expect { subject.search_type('foo') }.not_to change { subject.render } }
165
+ end
166
+
167
+ %i[preference timeout].each do |name|
161
168
  describe "##{name}" do
162
169
  specify { expect(subject.send(name, :foo).render[:body]).to include(name => 'foo') }
163
170
  specify { expect(subject.send(name, :foo).send(name, :bar).render[:body]).to include(name => 'bar') }
@@ -218,8 +225,17 @@ describe Chewy::Search::Request do
218
225
  specify { expect { subject.docvalue_fields(:foo) }.not_to change { subject.render } }
219
226
  end
220
227
 
228
+ describe '#indices' do
229
+ specify { expect(described_class.new(:products).render[:index]).to eq(%w[products]) }
230
+ specify { expect(subject.indices(:cities).render[:index]).to eq(%w[cities products]) }
231
+ specify { expect(subject.indices(CitiesIndex, :whatever).render[:index]).to eq(%w[cities products whatever]) }
232
+ specify { expect(subject.indices([CitiesIndex, :products]).render[:index]).to eq(%w[cities products]) }
233
+ specify { expect { subject.indices(:cities) }.not_to change { subject.render } }
234
+ end
235
+
221
236
  describe '#types' do
222
237
  specify { expect(subject.types(:product).render[:type]).to contain_exactly('product') }
238
+ specify { expect(described_class.new(ProductsIndex::City).types(ProductsIndex::Country).render[:type]).to match_array(%w[city country]) }
223
239
  specify { expect(subject.types(%i[product city]).types(nil).render[:type]).to match_array(%w[product city]) }
224
240
  specify { expect(subject.types(:product).types(:product, :city, :something).render[:type]).to match_array(%w[product city]) }
225
241
  specify { expect(subject.types(nil).render[:body]).to be_blank }
@@ -415,7 +431,7 @@ describe Chewy::Search::Request do
415
431
  expect(outer_payload).to eq(
416
432
  index: ProductsIndex,
417
433
  indexes: [ProductsIndex],
418
- request: {index: ['products'], type: %w[product city country], body: {query: {match: {name: 'name3'}}}},
434
+ request: {index: ['products'], type: %w[city country product], body: {query: {match: {name: 'name3'}}}},
419
435
  type: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country],
420
436
  types: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country]
421
437
  )
@@ -644,7 +660,7 @@ describe Chewy::Search::Request do
644
660
  expect(outer_payload).to eq(
645
661
  index: ProductsIndex,
646
662
  indexes: [ProductsIndex],
647
- request: {index: ['products'], type: %w[product city country], body: {query: {match: {name: 'name3'}}}, refresh: true},
663
+ request: {index: ['products'], type: %w[city country product], body: {query: {match: {name: 'name3'}}}, refresh: true},
648
664
  type: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country],
649
665
  types: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country]
650
666
  )
@@ -659,7 +675,7 @@ describe Chewy::Search::Request do
659
675
  expect(outer_payload).to eq(
660
676
  index: ProductsIndex,
661
677
  indexes: [ProductsIndex],
662
- request: {index: ['products'], type: %w[product city country], body: {query: {match: {name: 'name3'}}}, refresh: false},
678
+ request: {index: ['products'], type: %w[city country product], body: {query: {match: {name: 'name3'}}}, refresh: false},
663
679
  type: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country],
664
680
  types: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country]
665
681
  )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chewy
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pyromaniac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-13 00:00:00.000000000 Z
11
+ date: 2019-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 3.7.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: 3.7.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rspec-collection_matchers
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -324,6 +324,7 @@ files:
324
324
  - lib/chewy/search/pagination/will_paginate.rb
325
325
  - lib/chewy/search/parameters.rb
326
326
  - lib/chewy/search/parameters/aggs.rb
327
+ - lib/chewy/search/parameters/allow_partial_search_results.rb
327
328
  - lib/chewy/search/parameters/concerns/bool_storage.rb
328
329
  - lib/chewy/search/parameters/concerns/hash_storage.rb
329
330
  - lib/chewy/search/parameters/concerns/integer_storage.rb
@@ -334,6 +335,7 @@ files:
334
335
  - lib/chewy/search/parameters/explain.rb
335
336
  - lib/chewy/search/parameters/filter.rb
336
337
  - lib/chewy/search/parameters/highlight.rb
338
+ - lib/chewy/search/parameters/indices.rb
337
339
  - lib/chewy/search/parameters/indices_boost.rb
338
340
  - lib/chewy/search/parameters/limit.rb
339
341
  - lib/chewy/search/parameters/load.rb
@@ -450,7 +452,7 @@ files:
450
452
  - spec/chewy/search/parameters/filter_spec.rb
451
453
  - spec/chewy/search/parameters/hash_storage_examples.rb
452
454
  - spec/chewy/search/parameters/highlight_spec.rb
453
- - spec/chewy/search/parameters/indices_boost_spec.rb
455
+ - spec/chewy/search/parameters/indices_spec.rb
454
456
  - spec/chewy/search/parameters/integer_storage_examples.rb
455
457
  - spec/chewy/search/parameters/limit_spec.rb
456
458
  - spec/chewy/search/parameters/load_spec.rb
@@ -534,8 +536,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
534
536
  - !ruby/object:Gem::Version
535
537
  version: '0'
536
538
  requirements: []
537
- rubyforge_project:
538
- rubygems_version: 2.7.4
539
+ rubygems_version: 3.0.6
539
540
  signing_key:
540
541
  specification_version: 4
541
542
  summary: Elasticsearch ODM client wrapper
@@ -592,7 +593,7 @@ test_files:
592
593
  - spec/chewy/search/parameters/filter_spec.rb
593
594
  - spec/chewy/search/parameters/hash_storage_examples.rb
594
595
  - spec/chewy/search/parameters/highlight_spec.rb
595
- - spec/chewy/search/parameters/indices_boost_spec.rb
596
+ - spec/chewy/search/parameters/indices_spec.rb
596
597
  - spec/chewy/search/parameters/integer_storage_examples.rb
597
598
  - spec/chewy/search/parameters/limit_spec.rb
598
599
  - spec/chewy/search/parameters/load_spec.rb
@@ -1,83 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Chewy::Search::Parameters::IndicesBoost do
4
- subject { described_class.new(index: 1.2) }
5
-
6
- describe '#initialize' do
7
- specify { expect(described_class.new.value).to eq({}) }
8
- specify { expect(described_class.new(nil).value).to eq({}) }
9
- specify { expect(subject.value).to eq('index' => 1.2) }
10
- specify { expect(described_class.new(index: '1.2', other: 2).value).to eq('index' => 1.2, 'other' => 2.0) }
11
- end
12
-
13
- describe '#replace!' do
14
- specify do
15
- expect { subject.replace!(nil) }
16
- .to change { subject.value }.from('index' => 1.2).to({})
17
- end
18
-
19
- specify do
20
- expect { subject.replace!(other: 3.1) }
21
- .to change { subject.value }
22
- .from('index' => 1.2).to('other' => 3.1)
23
- end
24
- end
25
-
26
- describe '#update!' do
27
- specify do
28
- expect { subject.update!(nil) }
29
- .not_to change { subject.value }
30
- end
31
-
32
- specify do
33
- expect { subject.update!(other: 3.1) }
34
- .to change { subject.value }
35
- .from('index' => 1.2).to('index' => 1.2, 'other' => 3.1)
36
- end
37
-
38
- context do
39
- before { subject.update!(other: 3.1) }
40
-
41
- specify do
42
- expect { subject.update!(index: 1.5) }
43
- .to change { subject.value }
44
- .from('index' => 1.2, 'other' => 3.1).to('index' => 1.5, 'other' => 3.1)
45
- end
46
-
47
- specify do
48
- expect { subject.update!(index: 1.5) }
49
- .to change { subject.value.keys }
50
- .from(%w[index other]).to(%w[other index])
51
- end
52
- end
53
- end
54
-
55
- describe '#merge!' do
56
- specify do
57
- expect { subject.merge!(described_class.new) }
58
- .not_to change { subject.value }
59
- end
60
-
61
- specify do
62
- expect { subject.merge!(described_class.new(other: 3.1)) }
63
- .to change { subject.value }
64
- .from('index' => 1.2)
65
- .to('index' => 1.2, 'other' => 3.1)
66
- end
67
- end
68
-
69
- describe '#render' do
70
- specify { expect(described_class.new.render).to be_nil }
71
- specify { expect(described_class.new(index: 1.2).render).to eq(indices_boost: [{'index' => 1.2}]) }
72
- specify { expect(described_class.new(index: 1.2, other: 1.3).render).to eq(indices_boost: [{'index' => 1.2}, {'other' => 1.3}]) }
73
- specify { expect(described_class.new(index: 1.2, other: 1.3).tap { |i| i.update!(index: '1.5') }.render).to eq(indices_boost: [{'other' => 1.3}, {'index' => 1.5}]) }
74
- end
75
-
76
- describe '#==' do
77
- specify { expect(described_class.new).to eq(described_class.new) }
78
- specify { expect(described_class.new(index: 1.2)).to eq(described_class.new(index: 1.2)) }
79
- specify { expect(described_class.new(index: 1.2)).not_to eq(described_class.new(other: 1.3)) }
80
- specify { expect(described_class.new(index: 1.2, other: 1.3)).to eq(described_class.new(index: 1.2, other: 1.3)) }
81
- specify { expect(described_class.new(index: 1.2, other: 1.3)).not_to eq(described_class.new(other: 1.3, index: 1.2)) }
82
- end
83
- end