elasticsearch-explain-response 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +26 -22
- data/elasticsearch-explain-response.gemspec +1 -1
- data/lib/elasticsearch/api/response/explain_parser.rb +19 -5
- data/lib/elasticsearch/api/response/explain_renderer.rb +6 -1
- data/lib/elasticsearch/api/response/string_helper.rb +21 -0
- data/spec/elasticsearch/api/response/explain_response_spec.rb +68 -37
- data/spec/fixtures/response1.yml +4 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a45d429446a768356511e49f49519a4b59c82bb1
|
4
|
+
data.tar.gz: cae5a22110b309224a5ad2ae74c4f1e8a0616852
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37cdde1a62667643d3e0d137457e8890251e727ba51865ac8eaf97af61ac203d66cb5c646569557e9f16eb4c784a1dc0a4297340e4327ef605f0f0904f1d927d
|
7
|
+
data.tar.gz: 085fa522790f416383bed3f13c699c8c38f2f640d81a02b8948d0e022f7c2d067e052c301a20b5b72835473fa606790bb55a29c53dc317789fa1862723fd2242
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
Parse Elasticsearch Explain Response and summarize it in a simple way.
|
6
6
|
This gem is intended for developers working with Elasticsearch to debug search scores
|
7
7
|
in a easier way.
|
8
|
-
The current feature is very simple, but any
|
8
|
+
The current feature is very simple, but any suggestions or feature requests are welcomed.
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
@@ -17,41 +17,45 @@ gem 'elasticsearch-explain-response'
|
|
17
17
|
|
18
18
|
And then execute:
|
19
19
|
|
20
|
-
|
20
|
+
```
|
21
|
+
$ bundle
|
22
|
+
```
|
21
23
|
|
22
24
|
Or install it yourself as:
|
23
25
|
|
24
|
-
|
26
|
+
```
|
27
|
+
$ gem install elasticsearch-explain-response
|
28
|
+
```
|
25
29
|
|
26
30
|
## Usage
|
27
31
|
|
28
32
|
### Summarize the explanation in one line
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
```ruby
|
35
|
+
require 'elasticsearch'
|
36
|
+
client = Elasticsearch::Client.new
|
37
|
+
result = client.explain index: "megacorp", type: "employee", id: "1", q: "last_name:Smith"
|
38
|
+
puts Elasticsearch::API::Response::ExplainResponse.new(result["explanation"]).render_in_line
|
39
|
+
#=>
|
40
|
+
1.0 = (1.0(termFreq=1.0)) x 1.0(idf(2/3)) x 1.0(fieldNorm)
|
41
|
+
```
|
38
42
|
|
39
43
|
### Summarize the explanation in lines
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
```ruby
|
46
|
+
require 'elasticsearch'
|
47
|
+
client = Elasticsearch::Client.new
|
48
|
+
result = client.explain index: "megacorp", type: "employee", id: "1", q: "last_name:Smith"
|
49
|
+
puts Elasticsearch::API::Response::ExplainResponse.new(result["explanation"]).render
|
50
|
+
#=>
|
51
|
+
1.0 = 1.0(fieldWeight)
|
52
|
+
1.0 = 1.0(tf(1.0)) x 1.0(idf(2/3)) x 1.0(fieldNorm)
|
53
|
+
1.0 = 1.0(termFreq=1.0)
|
54
|
+
```
|
51
55
|
|
52
56
|
## Contributing
|
53
57
|
|
54
|
-
1. Fork it ( https://github.com/
|
58
|
+
1. Fork it ( https://github.com/tomoya55/elasticsearch-explain-response/fork )
|
55
59
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
56
60
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
57
61
|
4. Push to the branch (`git push origin my-new-feature`)
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "elasticsearch-explain-response"
|
7
|
-
spec.version = "0.0
|
7
|
+
spec.version = "0.1.0"
|
8
8
|
spec.authors = ["Tomoya Hirano"]
|
9
9
|
spec.email = ["hiranotomoya@gmail.com"]
|
10
10
|
spec.summary = %q{Parser for Elasticserach Explain response}
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require "elasticsearch/api/response/string_helper"
|
2
|
+
|
1
3
|
module Elasticsearch
|
2
4
|
module API
|
3
5
|
module Response
|
4
6
|
class ExplainParser
|
7
|
+
include StringHelper
|
8
|
+
|
5
9
|
def parse(explain_tree)
|
6
10
|
root = create_node(explain_tree, level: 0)
|
7
11
|
parse_details(root)
|
@@ -45,12 +49,15 @@ module Elasticsearch
|
|
45
49
|
type = "score"
|
46
50
|
operation = "score"
|
47
51
|
operator = "x"
|
48
|
-
when /\Amatch filter\: (?:cache\()?(?:(?<op>[\w]+)\()*(?<
|
52
|
+
when /\Amatch filter\: (?:cache\()?(?:(?<op>[\w]+)\()*(?<c>.+)\)*\z/
|
49
53
|
type = "match"
|
50
54
|
operation = "match"
|
51
55
|
operation += ".#{$~[:op]}" if $~[:op] && !%w[QueryWrapperFilter].include?($~[:op])
|
52
|
-
|
53
|
-
|
56
|
+
content = $~[:c]
|
57
|
+
content = content[0..-2] if content.end_with?(')')
|
58
|
+
hash = tokenize_contents(content)
|
59
|
+
field = hash.keys.join(", ")
|
60
|
+
value = hash.values.join(", ")
|
54
61
|
when /\AFunction for field ([\w\_]+)\:\z/
|
55
62
|
type = "func"
|
56
63
|
operation = "func"
|
@@ -70,6 +77,15 @@ module Elasticsearch
|
|
70
77
|
/\Afunction score\, score mode \[multiply\]\z/
|
71
78
|
type = "func score"
|
72
79
|
operator = "x"
|
80
|
+
when /\Ascript score function\, computed with script:\"(?<s>.+)\"\s*(?:and parameters:\s*(?<p>.+))?/m
|
81
|
+
type = "script"
|
82
|
+
operation = "script"
|
83
|
+
script, param = $~[:s], $~[:p]
|
84
|
+
script = script.gsub("\n", '')
|
85
|
+
script = "\"#{script}\""
|
86
|
+
param.gsub!("\n", '') if param
|
87
|
+
field = script.scan(/doc\[\'([\w\.]+)\'\]/).flatten.uniq.compact.join(" ")
|
88
|
+
value = [script, param].join(" ")
|
73
89
|
when "static boost factor", "boostFactor"
|
74
90
|
type = "boost"
|
75
91
|
operation = "boost"
|
@@ -93,8 +109,6 @@ module Elasticsearch
|
|
93
109
|
operation = description
|
94
110
|
end
|
95
111
|
|
96
|
-
# binding.pry if operator.nil?
|
97
|
-
|
98
112
|
Description.new(
|
99
113
|
raw: description,
|
100
114
|
type: type,
|
@@ -10,6 +10,7 @@ module Elasticsearch
|
|
10
10
|
disable_colorization if options[:colorize] == false
|
11
11
|
@max = options[:max] || 3
|
12
12
|
@plain_score = options[:plain_score] == true
|
13
|
+
@show_values = options[:show_values] == true
|
13
14
|
end
|
14
15
|
|
15
16
|
def render(tree)
|
@@ -108,7 +109,11 @@ module Elasticsearch
|
|
108
109
|
text = ''
|
109
110
|
text = description.operation if description.operation
|
110
111
|
if description.field && description.value
|
111
|
-
|
112
|
+
if @show_values
|
113
|
+
text += "(#{field(description.field)}:#{value(description.value)})"
|
114
|
+
else
|
115
|
+
text += "(#{field(description.field)})"
|
116
|
+
end
|
112
117
|
elsif description.field
|
113
118
|
text += "(#{field(description.field)})"
|
114
119
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module API
|
3
|
+
module Response
|
4
|
+
module StringHelper
|
5
|
+
WORD = /[\w\.\*]+/
|
6
|
+
WITH_QUOTE = /"[^"]*"/
|
7
|
+
WITH_BRACKET = /\[[^\]]*\]/
|
8
|
+
QUOTE_TOKENIZER = /(?:(?<field>#{WORD})(\:(?<value>(#{WORD}|#{WITH_QUOTE}|#{WITH_BRACKET})))?)+/
|
9
|
+
|
10
|
+
# @return [Hash] field name as a key and values as a value
|
11
|
+
def tokenize_contents(string)
|
12
|
+
string
|
13
|
+
.scan(QUOTE_TOKENIZER)
|
14
|
+
.each_with_object(Hash.new{|h,k| h[k] = []}) { |(field, value), memo|
|
15
|
+
memo[field] << value
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -28,77 +28,108 @@ describe Elasticsearch::API::Response::ExplainResponse do
|
|
28
28
|
|
29
29
|
describe "#render_in_line" do
|
30
30
|
let(:response) do
|
31
|
-
described_class.new(fake_response["explanation"], colorize: false, max:
|
31
|
+
described_class.new(fake_response["explanation"], colorize: false, max: 5, show_values: show_values)
|
32
32
|
end
|
33
33
|
|
34
34
|
subject do
|
35
35
|
response.render_in_line
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
38
|
+
context "with show_values" do
|
39
|
+
let(:show_values) do
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns summary of explain in line" do
|
44
|
+
expect(subject).to eq("0.05 = ((1.0(idf(2/3)) x 0.43(queryNorm)) x (1.0(tf(1.0)) x 1.0(idf(2/3)) x 0.25(fieldNorm(doc=0))) x 10.0(match(name.raw:smith)) x 0.99(func(updated_at)) x 5.93(script(popularity:\"def val = factor * log(sqrt(doc['popularity'].value) + 1) + 1\" {factor=1.0})) min 3.4e+38) x 0.5(coord(1/2)) x 1.0(queryBoost)")
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with fake_response2" do
|
48
|
+
let(:fake_response) do
|
49
|
+
fixture_load(:response2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns summary of explain in line" do
|
53
|
+
expect(subject).to eq("887.19 = ((10.0(match(name:hawaii)) x 10.0(match(name:guam)) x 0.7(match(name:\"new caledonia\", new, nueva, caledonia)) x 3.0(match(with_beach:T)) x 0.99(func(updated_at)) x 3.0(match(region_id:[3 TO 3]))) min 3.4e+38) x 1.0(queryBoost)")
|
54
|
+
end
|
55
|
+
end
|
40
56
|
end
|
41
57
|
|
42
|
-
context "with
|
43
|
-
let(:
|
44
|
-
|
58
|
+
context "with show_values false" do
|
59
|
+
let(:show_values) do
|
60
|
+
false
|
45
61
|
end
|
46
62
|
|
47
63
|
it "returns summary of explain in line" do
|
48
|
-
expect(subject).to eq("
|
64
|
+
expect(subject).to eq("0.05 = ((1.0(idf(2/3)) x 0.43(queryNorm)) x (1.0(tf(1.0)) x 1.0(idf(2/3)) x 0.25(fieldNorm(doc=0))) x 10.0(match(name.raw)) x 0.99(func(updated_at)) x 5.93(script(popularity)) min 3.4e+38) x 0.5(coord(1/2)) x 1.0(queryBoost)")
|
65
|
+
end
|
66
|
+
|
67
|
+
context "with fake_response2" do
|
68
|
+
let(:fake_response) do
|
69
|
+
fixture_load(:response2)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "returns summary of explain in line" do
|
73
|
+
expect(subject).to eq("887.19 = ((10.0(match(name)) x 10.0(match(name)) x 0.7(match(name)) x 3.0(match(with_beach)) x 0.99(func(updated_at)) x 3.0(match(region_id))) min 3.4e+38) x 1.0(queryBoost)")
|
74
|
+
end
|
49
75
|
end
|
50
76
|
end
|
51
77
|
end
|
52
78
|
|
53
79
|
describe "#render" do
|
54
80
|
let(:response) do
|
55
|
-
described_class.new(fake_response["explanation"], max: max, colorize: false, plain_score: plain_score)
|
81
|
+
described_class.new(fake_response["explanation"], max: max, colorize: false, plain_score: plain_score, show_values: show_values)
|
56
82
|
end
|
57
83
|
|
58
84
|
let(:max) { nil }
|
59
85
|
let(:plain_score) { nil }
|
86
|
+
let(:show_values) { false }
|
60
87
|
|
61
88
|
subject do
|
62
89
|
response.render.lines.map(&:rstrip)
|
63
90
|
end
|
64
91
|
|
65
|
-
|
66
|
-
|
67
|
-
"0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
|
68
|
-
" 0.11 = 0.11 min 3.4e+38",
|
69
|
-
" 0.11 = 0.11(weight(_all:smith))",
|
70
|
-
" 0.11 = 0.11(score)"
|
71
|
-
]
|
72
|
-
end
|
73
|
-
|
74
|
-
context "with max = 5" do
|
75
|
-
let(:max) { 5 }
|
92
|
+
context "with show_values" do
|
93
|
+
let(:show_values) { true }
|
76
94
|
|
77
95
|
it "returns summary of explain in lines" do
|
78
|
-
expect(subject).to match_array
|
96
|
+
expect(subject).to match_array [
|
79
97
|
"0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
|
80
98
|
" 0.11 = 0.11 min 3.4e+38",
|
81
99
|
" 0.11 = 0.11(weight(_all:smith))",
|
82
|
-
" 0.11 = 0.11(score)"
|
83
|
-
|
84
|
-
" 0.43 = 1.0(idf(2/3)) x 0.43(queryNorm)",
|
85
|
-
" 0.25 = 1.0(tf(1.0)) x 1.0(idf(2/3)) x 0.25(fieldNorm(doc=0))",
|
86
|
-
" 10.0 = 10.0 x 1.0(match(name.raw:smith))",
|
87
|
-
" 0.99 = 0.99(func(updated_at))"
|
88
|
-
])
|
100
|
+
" 0.11 = 0.11(score)"
|
101
|
+
]
|
89
102
|
end
|
90
|
-
end
|
91
103
|
|
92
|
-
|
93
|
-
|
104
|
+
context "with max = 5" do
|
105
|
+
let(:max) { 5 }
|
106
|
+
|
107
|
+
it "returns summary of explain in lines" do
|
108
|
+
expect(subject).to match_array([
|
109
|
+
"0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
|
110
|
+
" 0.11 = 0.11 min 3.4e+38",
|
111
|
+
" 0.11 = 0.11(weight(_all:smith))",
|
112
|
+
" 0.11 = 0.11(score)",
|
113
|
+
" 0.11 = 0.43(queryWeight) x 0.25(fieldWeight) x 10.0 x 0.99 x 5.93(script(popularity:\"def val = factor * log(sqrt(doc['popularity'].value) + 1) + 1\" {factor=1.0}))",
|
114
|
+
" 0.43 = 1.0(idf(2/3)) x 0.43(queryNorm)",
|
115
|
+
" 0.25 = 1.0(tf(1.0)) x 1.0(idf(2/3)) x 0.25(fieldNorm(doc=0))",
|
116
|
+
" 10.0 = 10.0 x 1.0(match(name.raw:smith))",
|
117
|
+
" 0.99 = 0.99(func(updated_at))"
|
118
|
+
])
|
119
|
+
end
|
120
|
+
end
|
94
121
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
122
|
+
context "with plain_score = true" do
|
123
|
+
let(:plain_score) { true }
|
124
|
+
|
125
|
+
it "returns summary of explain in lines" do
|
126
|
+
expect(subject).to match_array([
|
127
|
+
"0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
|
128
|
+
" 0.11 = 0.11 min 3.4028235e+38",
|
129
|
+
" 0.11 = 0.11(weight(_all:smith))",
|
130
|
+
" 0.11 = 0.11(score)"
|
131
|
+
])
|
132
|
+
end
|
102
133
|
end
|
103
134
|
end
|
104
135
|
end
|
data/spec/fixtures/response1.yml
CHANGED
@@ -59,6 +59,10 @@ explanation:
|
|
59
59
|
- value: 0.9857722
|
60
60
|
description: "exp(- MIN[Math.max(Math.abs(1.424917503E12(=doc value)\
|
61
61
|
\ - 1.432308964176E12(=origin))) - 0.0(=offset), 0)] * 1.938722193962471E-12)"
|
62
|
+
- value: 5.9284687
|
63
|
+
description: "script score function, computed with script:\"def val\
|
64
|
+
\ = factor * log(sqrt(doc['popularity'].value)\
|
65
|
+
\ + 1) + 1\" and parameters: \n{factor=1.0}"
|
62
66
|
- value: 3.4028235E+38
|
63
67
|
description: "maxBoost"
|
64
68
|
- value: 0.5
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch-explain-response
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomoya Hirano
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -115,6 +115,7 @@ files:
|
|
115
115
|
- lib/elasticsearch/api/response/explain_parser.rb
|
116
116
|
- lib/elasticsearch/api/response/explain_renderer.rb
|
117
117
|
- lib/elasticsearch/api/response/explain_response.rb
|
118
|
+
- lib/elasticsearch/api/response/string_helper.rb
|
118
119
|
- spec/elasticsearch/api/response/explain_response_spec.rb
|
119
120
|
- spec/fixtures/response1.yml
|
120
121
|
- spec/fixtures/response2.yml
|