es-elasticity 0.8.3 → 0.8.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
  SHA1:
3
- metadata.gz: 027f21d0948b94e32c1df9c760f02149816e6444
4
- data.tar.gz: 1edede15a6eab106d37ac9d39f4c7baed22f6736
3
+ metadata.gz: cbf73e91f2ff19dfbacefd71a7b370fe7b49e4d6
4
+ data.tar.gz: 30717c761dc0501315b0d7bd0bbba132a0ed3b99
5
5
  SHA512:
6
- metadata.gz: 9c7749d76c8dcb7bf44a1f51becafa5bef7cb987b6ea71f15ad1ebc7fefe0e11938191f02f3dba49755c609c23eccd99bcc029bc80717512606deea25a54f0da
7
- data.tar.gz: 28804e4fabfe66e03ac4494fde70663fe6055265f65758e459518454302d59386cdbc4c32f0b5bc9fd621f36263403b578891ed39d008fd6603e1d315d65f8a0
6
+ metadata.gz: 3015d7e3925ccf52831fb5410660db47ce24dfbb449b693745b0a05fc0607fef067475eb6a1efaa9f79887093ebda07640aa27fba5971989307ae44454a6eeee
7
+ data.tar.gz: a919b8cc74b7b9083d160fe00d5ddfe9556c184cff6842d14aef038722fb444c09056235ae68e071c18c0ffa98de6b6c29f66cc622921e748c6d45bdea98f3ad
data/README.md CHANGED
@@ -90,8 +90,8 @@ class Search::User < Elasticity::Document
90
90
  },
91
91
  }
92
92
 
93
- # Creates a search object from the body and return it.
94
- # The returned object # is a lazy evaluated search that behaves like a collection, being
93
+ # Creates a search object from the body and return it. The returned
94
+ # object is a lazy evaluated search that behaves like a collection, being
95
95
  # automatically triggered when data is iterated over.
96
96
  self.search(body)
97
97
  end
@@ -237,8 +237,8 @@ class Search::User < Elasticity::SegmentedDocument
237
237
  },
238
238
  }
239
239
 
240
- # Creates a search object from the body and return it.
241
- # The returned object # is a lazy evaluated search that behaves like a collection, being
240
+ # Creates a search object from the body and return it. The returned
241
+ # object is a lazy evaluated search that behaves like a collection, being
242
242
  # automatically triggered when data is iterated over.
243
243
  self.search(body)
244
244
  end
@@ -279,7 +279,7 @@ users.adults.to_a # => [#<Search::User{"doximity.com"}:0x819cc5a50cd5 @_id="AVCH
279
279
 
280
280
  ### Strategies and Hot-remapping
281
281
 
282
- Strategies define how index creation and index operation happens on the lower level. Basically it define the structure that backs the document model. Currently, there are two strategies available: single-index and alias-index.
282
+ Strategies define how index creation and index operation happens on the lower level. Basically it defines the structure that backs the document model. Currently, there are two strategies available: single-index and alias-index.
283
283
 
284
284
  The single-index strategy is the most straightforward one. It causes one index to be created and any operation will be performed directly on that index. It's very simple but it has the downside of being a lot harder to update existing mapping since you'll have to drop the index and recreate from scratch.
285
285
 
@@ -22,19 +22,20 @@ require "active_model"
22
22
  require "elasticsearch"
23
23
 
24
24
  module Elasticity
25
- autoload :Bulk, "elasticity/bulk"
26
- autoload :Config, "elasticity/config"
27
- autoload :IndexConfig, "elasticity/index_config"
28
- autoload :IndexMapper, "elasticity/index_mapper"
29
- autoload :BaseDocument, "elasticity/base_document"
30
- autoload :Document, "elasticity/document"
31
- autoload :SegmentedDocument, "elasticity/segmented_document"
32
- autoload :InstrumentedClient, "elasticity/instrumented_client"
33
- autoload :LogSubscriber, "elasticity/log_subscriber"
34
- autoload :MultiSearch, "elasticity/multi_search"
35
- autoload :Search, "elasticity/search"
36
- autoload :Strategies, "elasticity/strategies"
37
- autoload :ScrollableSearch, "elasticity/scrollable_search"
25
+ autoload :Bulk, "elasticity/bulk"
26
+ autoload :Config, "elasticity/config"
27
+ autoload :IndexConfig, "elasticity/index_config"
28
+ autoload :IndexMapper, "elasticity/index_mapper"
29
+ autoload :BaseDocument, "elasticity/base_document"
30
+ autoload :Document, "elasticity/document"
31
+ autoload :SegmentedDocument, "elasticity/segmented_document"
32
+ autoload :InstrumentedClient, "elasticity/instrumented_client"
33
+ autoload :LogSubscriber, "elasticity/log_subscriber"
34
+ autoload :MultiSearch, "elasticity/multi_search"
35
+ autoload :Search, "elasticity/search"
36
+ autoload :Strategies, "elasticity/strategies"
37
+ autoload :ScrollableSearch, "elasticity/scrollable_search"
38
+ autoload :MultiSearchResponseParser, "elasticity/multi_search_response_parser"
38
39
 
39
40
  def self.configure
40
41
  @config = Config.new
@@ -1,7 +1,7 @@
1
1
  module Elasticity
2
2
  class MultiSearch
3
-
4
3
  def initialize(msearch_args = {})
4
+ @results = {}
5
5
  @searches = {}
6
6
  @mappers = {}
7
7
  @msearch_args = msearch_args
@@ -25,39 +25,31 @@ module Elasticity
25
25
  end
26
26
 
27
27
  def [](name)
28
- results_collection[name]
28
+ @results[name] ||= result_for(name)
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def results_collection
34
- @results_collection ||= fetch
35
- end
33
+ def result_for(name)
34
+ search = @searches[name]
35
+ return if search.nil?
36
36
 
37
- def fetch
38
- bodies = @searches.values.map do |hsh|
39
- hsh[:search_definition].to_msearch_args
40
- end
37
+ query_response = response_for(@searches.keys.index(name))
38
+ MultiSearchResponseParser.parse(query_response, search)
39
+ end
41
40
 
42
- response = ActiveSupport::Notifications.instrument("multi_search.elasticity", args: { body: bodies }) do
41
+ def response_for(index)
42
+ @response ||= ActiveSupport::Notifications.instrument("multi_search.elasticity", args: { body: bodies }) do
43
43
  args = { body: bodies.map(&:dup) }.reverse_merge(@msearch_args)
44
44
  Elasticity.config.client.msearch(args)
45
45
  end
46
- results = {}
47
-
48
- @searches.keys.each_with_index do |name, idx|
49
- resp = response["responses"][idx]
50
- search = @searches[name]
51
-
52
- results[name] = case
53
- when search[:documents]
54
- Search::Results.new(resp, search[:search_definition].body, search[:documents].method(:map_hit))
55
- when search[:active_records]
56
- Search::ActiveRecordProxy.map_response(search[:active_records], search[:search_definition].body, resp)
57
- end
58
- end
46
+ @response["responses"][index]
47
+ end
59
48
 
60
- results
49
+ def bodies
50
+ @bodies ||= @searches.values.map do |hsh|
51
+ hsh[:search_definition].to_msearch_args
52
+ end
61
53
  end
62
54
  end
63
55
  end
@@ -0,0 +1,22 @@
1
+ module Elasticity
2
+ class MultiSearchResponseParser
3
+ class UnknownError < StandardError; end
4
+
5
+ def self.parse(response, search)
6
+ raise error_for(response["status"]), response.to_json if response["error"]
7
+
8
+ case
9
+ when search[:documents]
10
+ Search::Results.new(response, search[:search_definition].body, search[:documents].method(:map_hit))
11
+ when search[:active_records]
12
+ Search::ActiveRecordProxy.map_response(search[:active_records], search[:search_definition].body, response)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def self.error_for(status)
19
+ Elasticsearch::Transport::Transport::ERRORS[status] || UnknownError
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Elasticity
2
- VERSION = "0.8.3"
2
+ VERSION = "0.8.4"
3
3
  end
@@ -0,0 +1,121 @@
1
+ require "elasticity/multi_search_response_parser"
2
+
3
+ RSpec.describe Elasticity::MultiSearchResponseParser do
4
+ describe ".parse" do
5
+ let :response do
6
+ {
7
+ "hits" => {
8
+ "total" => 2,
9
+ "hits" => [
10
+ { "_id" => 1, "_source" => { "name" => "foo" }},
11
+ { "_id" => 2, "_source" => { "name" => "bar" }}
12
+ ]
13
+ }
14
+ }
15
+ end
16
+
17
+ let :klass do
18
+ Class.new do
19
+ include ActiveModel::Model
20
+ attr_accessor :_id, :name
21
+
22
+ def self.map_hit(hit)
23
+ new(_id: hit["_id"], name: hit["_source"]["name"])
24
+ end
25
+
26
+ def ==(other)
27
+ self._id == other._id && self.name == other.name
28
+ end
29
+ end
30
+ end
31
+
32
+ let :search do
33
+ body = {
34
+ index: "index_first",
35
+ type: "document_first",
36
+ search: { search: :first, size: 2 }
37
+ }
38
+
39
+ {
40
+ search_definition: OpenStruct.new(body: body),
41
+ documents: klass
42
+ }
43
+ end
44
+
45
+ it "parses a simple reponse" do
46
+ search_result = described_class.parse(response, search)
47
+
48
+ expect(search_result[0].name).to eq "foo"
49
+ expect(search_result[1].name).to eq "bar"
50
+ end
51
+
52
+ context "for a 400 error response" do
53
+ let(:response) do
54
+ {
55
+ "error" => {
56
+ "root_cause" => [
57
+ {
58
+ "type" => "too_many_clauses",
59
+ "reason" => "too_many_clauses: maxClauseCount is set to 1024"
60
+ }
61
+ ],
62
+ },
63
+ "status" => 400
64
+ }
65
+ end
66
+
67
+ it "raises an error for the given status code" do
68
+ expect { described_class.parse response, search }.to(
69
+ raise_error Elasticsearch::Transport::Transport::Errors::BadRequest,
70
+ response.to_json
71
+ )
72
+ end
73
+ end
74
+
75
+ context "for a 500 error response" do
76
+ let(:response) do
77
+ {
78
+ "error" => {
79
+ "root_cause" => [
80
+ {
81
+ "type" => "not_index_found",
82
+ "reason" => "not_index_found: index bla was not found"
83
+ }
84
+ ],
85
+ },
86
+ "status" => 500
87
+ }
88
+ end
89
+
90
+ it "raises an error for the given status code" do
91
+ expect { described_class.parse response, search }.to(
92
+ raise_error Elasticsearch::Transport::Transport::Errors::InternalServerError,
93
+ response.to_json
94
+ )
95
+ end
96
+ end
97
+
98
+ context "for an unknown error response" do
99
+ let(:response) do
100
+ {
101
+ "error" => {
102
+ "root_cause" => [
103
+ {
104
+ "type" => "known_error",
105
+ "reason" => "known_error: Something wrong happened"
106
+ }
107
+ ],
108
+ },
109
+ "status" => 555
110
+ }
111
+ end
112
+
113
+ it "raises an unkown error for an known status code" do
114
+ expect { described_class.parse response, search }.to(
115
+ raise_error Elasticity::MultiSearchResponseParser::UnknownError,
116
+ response.to_json
117
+ )
118
+ end
119
+ end
120
+ end
121
+ end
@@ -95,18 +95,64 @@ RSpec.describe Elasticity::MultiSearch do
95
95
  expect(subject[:first].current_page).to eq 1
96
96
  expect(subject[:first].aggregations).to eq aggregations["aggregations"]
97
97
  expect(subject[:second].aggregations).to eq Hash.new
98
+ expect(subject[:third]).to be_nil
98
99
  end
99
100
 
100
101
  it "performs multi search with additional arguments" do
101
- msearch = Elasticity::MultiSearch.new(search_type: :dfs_query_then_fetch)
102
- msearch.add(:first, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_first", "document_first", { search: :first, size: 2 })), documents: klass)
103
- msearch.add(:second, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_second", "document_second", { search: :second })), documents: klass)
102
+ subject = Elasticity::MultiSearch.new(search_type: :dfs_query_then_fetch)
103
+ subject.add(:first, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_first", "document_first", { search: :first, size: 2 })), documents: klass)
104
+ subject.add(:second, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_second", "document_second", { search: :second })), documents: klass)
104
105
 
105
106
  expect(Elasticity.config.client).to receive(:msearch).with(search_type: :dfs_query_then_fetch, body: [
106
107
  { index: "index_first", type: "document_first", search: { search: :first, size: 2 } },
107
108
  { index: "index_second", type: "document_second", search: { search: :second } },
108
109
  ]).and_return(response)
109
110
 
110
- expect(Array(msearch[:first])).to eq([klass.new(_id: 1, name: "foo"), klass.new(_id: 2, name: "bar")])
111
+ expect(Array(subject[:first])).to eq([klass.new(_id: 1, name: "foo"), klass.new(_id: 2, name: "bar")])
112
+ end
113
+
114
+ context "when there was an error for one query" do
115
+ let(:error) do
116
+ {
117
+ "error" => {
118
+ "root_cause" => [
119
+ {
120
+ "type" => "too_many_clauses",
121
+ "reason" => "too_many_clauses: maxClauseCount is set to 1024"
122
+ }
123
+ ],
124
+ "type" => "search_phase_execution_exception",
125
+ "grouped" => true,
126
+ },
127
+ "status" => 400
128
+ }
129
+ end
130
+
131
+ let(:response) do
132
+ {
133
+ "responses" => [
134
+ index_with_two_hits_and_aggregations,
135
+ index_with_one_hit,
136
+ error
137
+ ]
138
+ }
139
+ end
140
+
141
+ before do
142
+ expect(Elasticity.config.client).to receive(:msearch).with(body: [
143
+ { index: "index_first", type: "document_first", search: { search: :first, size: 2 } },
144
+ { index: "index_second", type: "document_second", search: { search: :second } },
145
+ { index: "index_third", type: "document_third", search: { search: :third } },
146
+ ]).and_return(response)
147
+ end
148
+
149
+ it "raises an error while trying to access the query result" do
150
+ subject.add(:first, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_first", "document_first", { search: :first, size: 2 })), documents: klass)
151
+ subject.add(:second, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_second", "document_second", { search: :second })), documents: klass)
152
+ subject.add(:third, Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new("index_third", "document_third", { search: :third })), documents: klass)
153
+
154
+ expect(Array(subject[:first])).to eq [klass.new(_id: 1, name: "foo"), klass.new(_id: 2, name: "bar")]
155
+ expect { subject[:third] }.to raise_error Elasticsearch::Transport::Transport::Errors::BadRequest, error.to_json
156
+ end
111
157
  end
112
158
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: es-elasticity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Kochenburger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-07 00:00:00.000000000 Z
11
+ date: 2017-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -221,6 +221,7 @@ files:
221
221
  - lib/elasticity/instrumented_client.rb
222
222
  - lib/elasticity/log_subscriber.rb
223
223
  - lib/elasticity/multi_search.rb
224
+ - lib/elasticity/multi_search_response_parser.rb
224
225
  - lib/elasticity/railtie.rb
225
226
  - lib/elasticity/scrollable_search.rb
226
227
  - lib/elasticity/search.rb
@@ -235,6 +236,7 @@ files:
235
236
  - spec/units/document_spec.rb
236
237
  - spec/units/index_config_spec.rb
237
238
  - spec/units/index_mapper_spec.rb
239
+ - spec/units/multi_search_response_parser_spec.rb
238
240
  - spec/units/multi_search_spec.rb
239
241
  - spec/units/search_spec.rb
240
242
  - spec/units/strategies/single_index_spec.rb
@@ -258,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
260
  version: '0'
259
261
  requirements: []
260
262
  rubyforge_project:
261
- rubygems_version: 2.0.0
263
+ rubygems_version: 2.0.14.1
262
264
  signing_key:
263
265
  specification_version: 4
264
266
  summary: ActiveModel-based library for working with Elasticsearch
@@ -269,6 +271,7 @@ test_files:
269
271
  - spec/units/document_spec.rb
270
272
  - spec/units/index_config_spec.rb
271
273
  - spec/units/index_mapper_spec.rb
274
+ - spec/units/multi_search_response_parser_spec.rb
272
275
  - spec/units/multi_search_spec.rb
273
276
  - spec/units/search_spec.rb
274
277
  - spec/units/strategies/single_index_spec.rb