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 +4 -4
- data/README.md +5 -5
- data/lib/elasticity.rb +14 -13
- data/lib/elasticity/multi_search.rb +16 -24
- data/lib/elasticity/multi_search_response_parser.rb +22 -0
- data/lib/elasticity/version.rb +1 -1
- data/spec/units/multi_search_response_parser_spec.rb +121 -0
- data/spec/units/multi_search_spec.rb +50 -4
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbf73e91f2ff19dfbacefd71a7b370fe7b49e4d6
|
4
|
+
data.tar.gz: 30717c761dc0501315b0d7bd0bbba132a0ed3b99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
|
data/lib/elasticity.rb
CHANGED
@@ -22,19 +22,20 @@ require "active_model"
|
|
22
22
|
require "elasticsearch"
|
23
23
|
|
24
24
|
module Elasticity
|
25
|
-
autoload :Bulk,
|
26
|
-
autoload :Config,
|
27
|
-
autoload :IndexConfig,
|
28
|
-
autoload :IndexMapper,
|
29
|
-
autoload :BaseDocument,
|
30
|
-
autoload :Document,
|
31
|
-
autoload :SegmentedDocument,
|
32
|
-
autoload :InstrumentedClient,
|
33
|
-
autoload :LogSubscriber,
|
34
|
-
autoload :MultiSearch,
|
35
|
-
autoload :Search,
|
36
|
-
autoload :Strategies,
|
37
|
-
autoload :ScrollableSearch,
|
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
|
-
|
28
|
+
@results[name] ||= result_for(name)
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
33
|
+
def result_for(name)
|
34
|
+
search = @searches[name]
|
35
|
+
return if search.nil?
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
37
|
+
query_response = response_for(@searches.keys.index(name))
|
38
|
+
MultiSearchResponseParser.parse(query_response, search)
|
39
|
+
end
|
41
40
|
|
42
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/elasticity/version.rb
CHANGED
@@ -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
|
-
|
102
|
-
|
103
|
-
|
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(
|
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.
|
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-
|
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.
|
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
|