elasticsearch-explain-response 0.0.2 → 0.0.3
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 +2 -0
- data/circle.yml +3 -0
- data/elasticsearch-explain-response.gemspec +3 -2
- data/lib/elasticsearch/api/response/color_helper.rb +1 -1
- data/lib/elasticsearch/api/response/description.rb +18 -0
- data/lib/elasticsearch/api/response/explain_node.rb +46 -0
- data/lib/elasticsearch/api/response/explain_parser.rb +110 -0
- data/lib/elasticsearch/api/response/explain_renderer.rb +135 -0
- data/lib/elasticsearch/api/response/explain_response.rb +13 -128
- data/spec/elasticsearch/api/response/explain_response_spec.rb +60 -17
- data/spec/fixtures/response1.yml +45 -17
- data/spec/fixtures/response2.yml +70 -0
- metadata +23 -3
- data/.travis.yml +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0c6480d4c7cba899094ad513da2b10279491b6a
|
4
|
+
data.tar.gz: 9134791842a913d82b011bc17afc5f1da46ced47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d916f784a14901ab7382f5a4461bd6f9315d81086a16e8485af4ee86d338a01ee6b09c7148a3b84ae7d961b8a052401fe7845564d656fda6a3b2f4ef3040ba0a
|
7
|
+
data.tar.gz: 0803f897d4e0bd067fbdf4e1be0c858853a2d7b23341fc8033c997bd3e9466b647f567294e3f9518bc7fd62d682ccdf24c7737fad2023a5e32bd1b1eca2bda48
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Elasticsearch-explain-response
|
2
2
|
|
3
|
+
[](https://circleci.com/gh/tomoya55/elasticsearch-explain-response/tree/master)
|
4
|
+
|
3
5
|
Parse Elasticsearch Explain Response and summarize it in a simple way.
|
4
6
|
This gem is intended for developers working with Elasticsearch to debug search scores
|
5
7
|
in a easier way.
|
data/circle.yml
ADDED
@@ -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.0.3"
|
8
8
|
spec.authors = ["Tomoya Hirano"]
|
9
9
|
spec.email = ["hiranotomoya@gmail.com"]
|
10
10
|
spec.summary = %q{Parser for Elasticserach Explain response}
|
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.7"
|
21
21
|
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
-
spec.add_development_dependency "rspec"
|
22
|
+
spec.add_development_dependency "rspec", "~>3.2"
|
23
|
+
spec.add_development_dependency "pry"
|
23
24
|
spec.add_development_dependency "elasticsearch", "~> 1.0.0"
|
24
25
|
spec.add_development_dependency "ansi", "~> 1.5.0"
|
25
26
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module API
|
3
|
+
module Response
|
4
|
+
class Description
|
5
|
+
attr_reader :raw, :type, :operator, :operation, :field, :value
|
6
|
+
|
7
|
+
def initialize(raw:, type:, operator:, operation: nil, field: nil, value: nil)
|
8
|
+
@raw = raw
|
9
|
+
@type = type
|
10
|
+
@operator = operator
|
11
|
+
@operation = operation
|
12
|
+
@field = field
|
13
|
+
@value = value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module API
|
3
|
+
module Response
|
4
|
+
class ExplainNode
|
5
|
+
attr_reader :score, :description, :details, :level
|
6
|
+
attr_accessor :children
|
7
|
+
|
8
|
+
def initialize(score:, description:, details: [], level: 0)
|
9
|
+
@score = score
|
10
|
+
@description = description
|
11
|
+
@details = details
|
12
|
+
@level = level
|
13
|
+
@children = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def operator
|
17
|
+
description.operator
|
18
|
+
end
|
19
|
+
|
20
|
+
def type
|
21
|
+
description.type
|
22
|
+
end
|
23
|
+
|
24
|
+
def func?
|
25
|
+
type == "func"
|
26
|
+
end
|
27
|
+
|
28
|
+
def match?
|
29
|
+
type == "match"
|
30
|
+
end
|
31
|
+
|
32
|
+
def match_all?
|
33
|
+
type == "match" && description.field == "*" && description.value == "*"
|
34
|
+
end
|
35
|
+
|
36
|
+
def boost?
|
37
|
+
type == "boost"
|
38
|
+
end
|
39
|
+
|
40
|
+
def max_boost?
|
41
|
+
type == "maxBoost"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module API
|
3
|
+
module Response
|
4
|
+
class ExplainParser
|
5
|
+
def parse(explain_tree)
|
6
|
+
root = create_node(explain_tree, level: 0)
|
7
|
+
parse_details(root)
|
8
|
+
root
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def create_node(detail, level:)
|
14
|
+
ExplainNode.new(
|
15
|
+
score: detail["value"] || 0.0,
|
16
|
+
description: parse_description(detail["description"]),
|
17
|
+
details: detail["details"] || [],
|
18
|
+
level: level
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_details(node)
|
23
|
+
node.details.each do |detail|
|
24
|
+
child = create_node(detail, level: node.level.succ)
|
25
|
+
node.children << child
|
26
|
+
parse_details(child)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_description(description)
|
31
|
+
case description
|
32
|
+
when /\Aweight\((\w+)\:(\w+)\s+in\s+\d+\)\s+\[\w+\]\, result of\:\z/
|
33
|
+
type = "weight"
|
34
|
+
operation = "weight"
|
35
|
+
operator = "x"
|
36
|
+
field = $1
|
37
|
+
value = $2
|
38
|
+
when /\Aidf\(docFreq\=(\d+)\, maxDocs\=(\d+)\)\z/
|
39
|
+
type = "idf"
|
40
|
+
operation = "idf(#{$1}/#{$2})"
|
41
|
+
when /\Atf\(freq\=([\d.]+)\)\, with freq of\:\z/
|
42
|
+
type = "tf"
|
43
|
+
operation = "tf(#{$1})"
|
44
|
+
when /\Ascore\(doc\=\d+\,freq=[\d\.]+\)\, product of\:\z/
|
45
|
+
type = "score"
|
46
|
+
operation = "score"
|
47
|
+
operator = "x"
|
48
|
+
when /\Amatch filter\: (?:cache\()?(?:(?<op>\w+)\()*(?<f>[\w\.\*]+)\:(?<v>[^\)]+)\)*\z/
|
49
|
+
type = "match"
|
50
|
+
operation = "match"
|
51
|
+
operation += ".#{$~[:op]}" if $~[:op] && !%w[QueryWrapperFilter].include?($~[:op])
|
52
|
+
field = $~[:f]
|
53
|
+
value = $~[:v]
|
54
|
+
when /\AFunction for field ([\w\_]+)\:\z/
|
55
|
+
type = "func"
|
56
|
+
operation = "func"
|
57
|
+
field = $1
|
58
|
+
when /\AqueryWeight\, product of\:\z/
|
59
|
+
type = "queryWeight"
|
60
|
+
operation = "queryWeight"
|
61
|
+
operator = "x"
|
62
|
+
when /\AfieldWeight in \d+\, product of\:\z/
|
63
|
+
type = "fieldWeight"
|
64
|
+
operation = "fieldWeight"
|
65
|
+
operator = "x"
|
66
|
+
when /\AqueryNorm/
|
67
|
+
type = "queryNorm"
|
68
|
+
operation = "queryNorm"
|
69
|
+
when /\Afunction score\, product of\:\z/,
|
70
|
+
/\Afunction score\, score mode \[multiply\]\z/
|
71
|
+
type = "func score"
|
72
|
+
operator = "x"
|
73
|
+
when "static boost factor", "boostFactor"
|
74
|
+
type = "boost"
|
75
|
+
operation = "boost"
|
76
|
+
when "product of:", "[multiply]"
|
77
|
+
type = "product"
|
78
|
+
operation = "product"
|
79
|
+
operator = "x"
|
80
|
+
when "Math.min of"
|
81
|
+
type = "min"
|
82
|
+
operator = "min"
|
83
|
+
when "Math.max of"
|
84
|
+
type = "max"
|
85
|
+
operator = "max"
|
86
|
+
when "sum of:"
|
87
|
+
type = "sum"
|
88
|
+
operator = "+"
|
89
|
+
when "maxBoost"
|
90
|
+
type = "maxBoost"
|
91
|
+
else
|
92
|
+
type = description
|
93
|
+
operation = description
|
94
|
+
end
|
95
|
+
|
96
|
+
# binding.pry if operator.nil?
|
97
|
+
|
98
|
+
Description.new(
|
99
|
+
raw: description,
|
100
|
+
type: type,
|
101
|
+
operator: operator,
|
102
|
+
operation: operation,
|
103
|
+
field: field,
|
104
|
+
value: value,
|
105
|
+
)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "elasticsearch/api/response/color_helper"
|
2
|
+
|
3
|
+
module Elasticsearch
|
4
|
+
module API
|
5
|
+
module Response
|
6
|
+
class ExplainRenderer
|
7
|
+
include ColorHelper
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
disable_colorization if options[:colorize] == false
|
11
|
+
@max = options[:max] || 3
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(tree)
|
15
|
+
@buffer = []
|
16
|
+
recursive_render(tree)
|
17
|
+
@buffer.join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_in_line(tree)
|
21
|
+
[render_score(tree.score), "=", recursive_render_details(tree)].flatten.join(" ")
|
22
|
+
end
|
23
|
+
|
24
|
+
def recursive_render(node)
|
25
|
+
return if node.level > @max
|
26
|
+
render_result(node) if node.details.any?
|
27
|
+
node.children.each do |child|
|
28
|
+
recursive_render(child)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def render_result(node)
|
35
|
+
@buffer << " " * node.level * 2 + [render_score(node.score), "=", render_details(node)].flatten.join(" ")
|
36
|
+
end
|
37
|
+
|
38
|
+
def render_score(score)
|
39
|
+
value = if score > 1_000
|
40
|
+
sprintf("%1.2g", score.round(2))
|
41
|
+
else
|
42
|
+
score.round(2).to_s
|
43
|
+
end
|
44
|
+
ansi(value, :magenta, :bright)
|
45
|
+
end
|
46
|
+
|
47
|
+
def render_details(node)
|
48
|
+
case node.type
|
49
|
+
when "func score"
|
50
|
+
render_boost_match_details(node)
|
51
|
+
else
|
52
|
+
render_node_details(node)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_node_details(node)
|
57
|
+
node.children.map do |child|
|
58
|
+
check_node = block_given? ? yield(child) : true
|
59
|
+
check_node && render_node(child) || nil
|
60
|
+
end.compact.join(" #{node.operator} ")
|
61
|
+
end
|
62
|
+
|
63
|
+
def render_boost_match_details(node)
|
64
|
+
match = node.children.find {|c| c.type == "match" }
|
65
|
+
boost = node.children.find {|c| c.type == "boost" }
|
66
|
+
if match && boost
|
67
|
+
[boost.score, render_node(match)].join(" x ")
|
68
|
+
else
|
69
|
+
render_node_details(node) {|n| !n.match_all? }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def recursive_render_details(node)
|
74
|
+
details = node.children.map do |child|
|
75
|
+
if child.children.any? && child.level <= @max
|
76
|
+
if child.func?
|
77
|
+
render_node(child)
|
78
|
+
elsif child.children[0].match? && child.children[1].boost?
|
79
|
+
match = child.children[0]
|
80
|
+
boost = child.children[1]
|
81
|
+
"#{render_score(boost.score)}(#{render_description(match.description)})"
|
82
|
+
else
|
83
|
+
recursive_render_details(child)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
if !child.match_all?
|
87
|
+
render_node(child)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end.compact
|
91
|
+
|
92
|
+
if details.size > 1
|
93
|
+
wrap_paren(details.join(" #{node.operator} "))
|
94
|
+
else
|
95
|
+
details[0]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def render_node(node)
|
100
|
+
text = render_score(node.score)
|
101
|
+
desc = render_description(node.description)
|
102
|
+
text = "#{text}(#{desc})" unless desc.empty?
|
103
|
+
text
|
104
|
+
end
|
105
|
+
|
106
|
+
def render_description(description)
|
107
|
+
text = ''
|
108
|
+
text = description.operation if description.operation
|
109
|
+
if description.field && description.value
|
110
|
+
text += "(#{field(description.field)}:#{value(description.value)})"
|
111
|
+
elsif description.field
|
112
|
+
text += "(#{field(description.field)})"
|
113
|
+
end
|
114
|
+
text
|
115
|
+
end
|
116
|
+
|
117
|
+
def field(str)
|
118
|
+
ansi(str, :blue ,:bright)
|
119
|
+
end
|
120
|
+
|
121
|
+
def value(str)
|
122
|
+
ansi(str, :green)
|
123
|
+
end
|
124
|
+
|
125
|
+
def wrap_paren(string)
|
126
|
+
if string.start_with?("(") && string.end_with?(")")
|
127
|
+
string
|
128
|
+
else
|
129
|
+
"(" + string + ")"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
|
-
require "elasticsearch/api/response/
|
1
|
+
require "elasticsearch/api/response/explain_node"
|
2
|
+
require "elasticsearch/api/response/description"
|
3
|
+
require "elasticsearch/api/response/explain_parser"
|
4
|
+
require "elasticsearch/api/response/explain_renderer"
|
2
5
|
|
3
6
|
module Elasticsearch
|
4
7
|
module API
|
@@ -12,8 +15,6 @@ module Elasticsearch
|
|
12
15
|
# Elasticsearch::API::Response::ExplainResponse.new(result["explanation"]).render_in_line
|
13
16
|
# #=> "1.0 = (1.0(termFreq=1.0)) x 1.0(idf(2/3)) x 1.0(fieldNorm)"
|
14
17
|
class ExplainResponse
|
15
|
-
include ColorHelper
|
16
|
-
|
17
18
|
class << self
|
18
19
|
# Show scoring as a simple math formula
|
19
20
|
# @example
|
@@ -30,148 +31,32 @@ module Elasticsearch
|
|
30
31
|
# 54.3 = 54.3 min 3.4028234999999995e+38(maxBoost)
|
31
32
|
# 54.3 = 2.0 x 10.0 x 3.0 x 0.91
|
32
33
|
def render(result, max: nil)
|
33
|
-
|
34
|
-
parser.parse
|
35
|
-
parser.render
|
34
|
+
new(result["explanation"], max: max).render
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
38
|
attr_reader :explain
|
40
39
|
|
41
|
-
def initialize(explain, max: nil)
|
40
|
+
def initialize(explain, max: nil, colorize: true)
|
42
41
|
@explain = explain
|
43
42
|
@indent = 0
|
44
|
-
@
|
43
|
+
@renderer = ExplainRenderer.new(max: max, colorize: colorize)
|
45
44
|
end
|
46
45
|
|
47
46
|
def render
|
48
|
-
|
49
|
-
|
50
|
-
@buffer.map do |buf|
|
51
|
-
render_result(buf[:score], render_details(buf[:details], indent: @max + 1), indent: buf[:indent])
|
52
|
-
end
|
47
|
+
parse_details
|
48
|
+
@renderer.render(@root)
|
53
49
|
end
|
54
50
|
|
55
51
|
def render_in_line
|
56
|
-
|
57
|
-
|
58
|
-
render_result(score, render_details(details, indent: 0))
|
52
|
+
parse_details
|
53
|
+
@renderer.render_in_line(@root)
|
59
54
|
end
|
60
55
|
|
61
56
|
private
|
62
57
|
|
63
|
-
def
|
64
|
-
|
65
|
-
when description.include?("product of")
|
66
|
-
"x"
|
67
|
-
when description.include?("[multiply]")
|
68
|
-
"x"
|
69
|
-
when description.include?("sum of")
|
70
|
-
"+"
|
71
|
-
when description.include?("Math.min of")
|
72
|
-
"min"
|
73
|
-
else
|
74
|
-
" "
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def extract_description(description)
|
79
|
-
case description
|
80
|
-
when /\Aweight\((\w+)\:(\w+)\s+in\s+\d+\)\s+\[\w+\]\,\s+result\s+of\:\z/
|
81
|
-
[field($1), value($2)].join(':')
|
82
|
-
when /\Aidf\(docFreq\=(\d+)\,\s+maxDocs\=(\d+)\)\z/
|
83
|
-
"idf(#{$1}/#{$2})"
|
84
|
-
when /\Atf\(freq\=([\d.]+)\)\,\swith\sfreq\sof\:\z/
|
85
|
-
"tf(#{$1})"
|
86
|
-
when /\Ascore\(doc\=\d+\,freq=[\d\.]+\)\,\sproduct\sof\:\z/
|
87
|
-
"score"
|
88
|
-
when /\Amatch\sfilter\:\s(?:cache\()?(?:\w+\()?([\w\*]+)\:([\w\*]+)\)*\z/
|
89
|
-
"match(#{field($1)}:#{value($2)})"
|
90
|
-
when /\AFunction\sfor\sfield\s([\w\_]+)\:\z/
|
91
|
-
"func(#{field($1)})"
|
92
|
-
when /\A(queryWeight|fieldWeight|fieldNorm)/
|
93
|
-
$1
|
94
|
-
when /\Afunction\sscore/
|
95
|
-
nil
|
96
|
-
when "static boost factor"
|
97
|
-
"boost"
|
98
|
-
when "Math.min of", "sum of:", "product of:"
|
99
|
-
nil
|
100
|
-
else
|
101
|
-
description
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def field(str)
|
106
|
-
ansi(str, :blue ,:bright)
|
107
|
-
end
|
108
|
-
|
109
|
-
def value(str)
|
110
|
-
ansi(str, :green)
|
111
|
-
end
|
112
|
-
|
113
|
-
def render_details(details, indent:)
|
114
|
-
details[:details].map do |de|
|
115
|
-
case de
|
116
|
-
when Hash
|
117
|
-
if indent < @max
|
118
|
-
detail = render_details(de, indent: indent.succ)
|
119
|
-
wrap_paren(detail)
|
120
|
-
else
|
121
|
-
de[:description]
|
122
|
-
end
|
123
|
-
else
|
124
|
-
de
|
125
|
-
end
|
126
|
-
end.join(" #{details[:symbol]} ")
|
127
|
-
end
|
128
|
-
|
129
|
-
def parse_for_oneline(explain)
|
130
|
-
symbol = get_score_type(explain["description"])
|
131
|
-
description = parse_description(explain)
|
132
|
-
details = explain["details"].map do |de|
|
133
|
-
if de["details"]
|
134
|
-
parse_for_oneline(de)
|
135
|
-
else
|
136
|
-
parse_description(de)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
{details: details, symbol: symbol, description: description}
|
140
|
-
end
|
141
|
-
|
142
|
-
def parse_description(detail)
|
143
|
-
text = render_score(detail["value"])
|
144
|
-
description = extract_description(detail["description"])
|
145
|
-
text += "(#{description})" if description
|
146
|
-
text
|
147
|
-
end
|
148
|
-
|
149
|
-
def parse_explain(explain, indent:, max: )
|
150
|
-
return if indent > max
|
151
|
-
score = explain["value"]
|
152
|
-
symbol = get_score_type(explain["description"])
|
153
|
-
details = explain["details"].map(&method(:parse_description))
|
154
|
-
@buffer << {score: score, details: {details: details, symbol: symbol}, indent: indent}
|
155
|
-
|
156
|
-
explain["details"].each do |de|
|
157
|
-
parse_explain(de, indent: indent.succ, max: max) if de["details"]
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def render_score(score)
|
162
|
-
ansi(score.round(2).to_s, :magenta, :bright)
|
163
|
-
end
|
164
|
-
|
165
|
-
def render_result(score, details, indent: 0)
|
166
|
-
" " * indent * 2 + [render_score(score), "=", details].flatten.join(" ")
|
167
|
-
end
|
168
|
-
|
169
|
-
def wrap_paren(string)
|
170
|
-
if string.start_with?("(") && string.end_with?(")")
|
171
|
-
string
|
172
|
-
else
|
173
|
-
"(" + string + ")"
|
174
|
-
end
|
58
|
+
def parse_details
|
59
|
+
@root ||= ExplainParser.new.parse(explain)
|
175
60
|
end
|
176
61
|
end
|
177
62
|
end
|
@@ -1,49 +1,92 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require "pry"
|
2
3
|
|
3
4
|
describe Elasticsearch::API::Response::ExplainResponse do
|
4
5
|
let(:fake_response) do
|
5
6
|
fixture_load(:response1)
|
6
7
|
end
|
7
8
|
|
9
|
+
describe '.render_in_line' do
|
10
|
+
subject do
|
11
|
+
described_class.render_in_line(fake_response)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns summary" do
|
15
|
+
expect(subject).not_to be_empty
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.render' do
|
20
|
+
subject do
|
21
|
+
described_class.render(fake_response)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns summary" do
|
25
|
+
expect(subject).not_to be_empty
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
8
29
|
describe "#render_in_line" do
|
9
30
|
let(:response) do
|
10
|
-
described_class.new(fake_response["explanation"])
|
31
|
+
described_class.new(fake_response["explanation"], colorize: false, max: 4)
|
11
32
|
end
|
12
33
|
|
13
34
|
subject do
|
14
35
|
response.render_in_line
|
15
36
|
end
|
16
37
|
|
17
|
-
|
18
|
-
|
38
|
+
it "returns summary of explain in line" do
|
39
|
+
expect(subject).to eq("0.05 = ((0.43(queryWeight) x 0.25(fieldWeight) x 10.0 x 0.99) min 3.4e+38) x 0.5(coord(1/2)) x 1.0(queryBoost)")
|
19
40
|
end
|
20
41
|
|
21
|
-
|
22
|
-
|
42
|
+
context "with fake_response2" do
|
43
|
+
let(:fake_response) do
|
44
|
+
fixture_load(:response2)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns summary of explain in line" do
|
48
|
+
expect(subject).to eq("887.19 = ((10.0(match(name:hawaii)) x 10.0(match(name:guam)) 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)")
|
49
|
+
end
|
23
50
|
end
|
24
51
|
end
|
25
52
|
|
26
53
|
describe "#render" do
|
27
54
|
let(:response) do
|
28
|
-
described_class.new(fake_response["explanation"])
|
55
|
+
described_class.new(fake_response["explanation"], max: max, colorize: false)
|
29
56
|
end
|
30
57
|
|
31
|
-
|
32
|
-
response.render
|
33
|
-
end
|
58
|
+
let(:max) { nil }
|
34
59
|
|
35
|
-
|
36
|
-
response.
|
60
|
+
subject do
|
61
|
+
response.render.lines.map(&:rstrip)
|
37
62
|
end
|
38
63
|
|
39
64
|
it "returns summary of explain in lines" do
|
40
|
-
expect(subject).to
|
41
|
-
"0.05 = 0.11 x 0.5(coord(1/2))",
|
42
|
-
" 0.11 = 0.11
|
43
|
-
" 0.11 = 0.11(
|
44
|
-
" 0.11 = 0.
|
65
|
+
expect(subject).to match_array [
|
66
|
+
"0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
|
67
|
+
" 0.11 = 0.11 min 3.4e+38",
|
68
|
+
" 0.11 = 0.11(weight(_all:smith))",
|
69
|
+
" 0.11 = 0.11(score)"
|
45
70
|
]
|
46
71
|
end
|
72
|
+
|
73
|
+
context "with max = 5" do
|
74
|
+
let(:max) { 5 }
|
75
|
+
|
76
|
+
it "returns summary of explain in lines" do
|
77
|
+
expect(subject).to match_array([
|
78
|
+
"0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
|
79
|
+
" 0.11 = 0.11 min 3.4e+38",
|
80
|
+
" 0.11 = 0.11(weight(_all:smith))",
|
81
|
+
" 0.11 = 0.11(score)",
|
82
|
+
" 0.11 = 0.43(queryWeight) x 0.25(fieldWeight) x 10.0 x 0.99",
|
83
|
+
" 0.43 = 1.0(idf(2/3)) x 0.43(queryNorm)",
|
84
|
+
" 0.25 = 1.0(tf(1.0)) x 1.0(idf(2/3)) x 0.25(fieldNorm(doc=0))",
|
85
|
+
" 10.0 = 10.0 x 1.0(match(name.raw:smith))",
|
86
|
+
" 0.99 = 0.99(func(updated_at))"
|
87
|
+
])
|
88
|
+
end
|
89
|
+
end
|
47
90
|
end
|
48
91
|
|
49
92
|
describe "colorization" do
|
@@ -52,7 +95,7 @@ describe Elasticsearch::API::Response::ExplainResponse do
|
|
52
95
|
end
|
53
96
|
|
54
97
|
subject do
|
55
|
-
response.
|
98
|
+
response.render
|
56
99
|
end
|
57
100
|
|
58
101
|
it "includes ansi color codes" do
|
data/spec/fixtures/response1.yml
CHANGED
@@ -8,32 +8,60 @@ explanation:
|
|
8
8
|
description: "product of:"
|
9
9
|
details:
|
10
10
|
- value: 0.107541315
|
11
|
-
description: "
|
11
|
+
description: "Math.min of"
|
12
12
|
details:
|
13
13
|
- value: 0.107541315
|
14
|
-
description: "
|
14
|
+
description: "sum of:"
|
15
15
|
details:
|
16
16
|
- value: 0.107541315
|
17
|
-
description: "
|
17
|
+
description: "weight(_all:smith in 0) [PerFieldSimilarity], result of:"
|
18
18
|
details:
|
19
|
-
- value: 0.
|
20
|
-
description: "
|
19
|
+
- value: 0.107541315
|
20
|
+
description: "score(doc=0,freq=1.0), product of:"
|
21
21
|
details:
|
22
|
-
- value: 1.0
|
23
|
-
description: "idf(docFreq=2, maxDocs=3)"
|
24
22
|
- value: 0.43016526
|
25
|
-
description: "
|
26
|
-
- value: 0.25
|
27
|
-
description: "fieldWeight in 0, product of:"
|
28
|
-
details:
|
29
|
-
- value: 1.0
|
30
|
-
description: "tf(freq=1.0), with freq of:"
|
23
|
+
description: "queryWeight, product of:"
|
31
24
|
details:
|
32
25
|
- value: 1.0
|
33
|
-
description: "
|
34
|
-
|
35
|
-
|
26
|
+
description: "idf(docFreq=2, maxDocs=3)"
|
27
|
+
- value: 0.43016526
|
28
|
+
description: "queryNorm"
|
36
29
|
- value: 0.25
|
37
|
-
description: "
|
30
|
+
description: "fieldWeight in 0, product of:"
|
31
|
+
details:
|
32
|
+
- value: 1.0
|
33
|
+
description: "tf(freq=1.0), with freq of:"
|
34
|
+
details:
|
35
|
+
- value: 1.0
|
36
|
+
description: "termFreq=1.0"
|
37
|
+
- value: 1.0
|
38
|
+
description: "idf(docFreq=2, maxDocs=3)"
|
39
|
+
- value: 0.25
|
40
|
+
description: "fieldNorm(doc=0)"
|
41
|
+
- value: 10.0
|
42
|
+
description: "function score, product of:"
|
43
|
+
details:
|
44
|
+
- value: 1.0
|
45
|
+
description: "match filter: QueryWrapperFilter(name.raw:smith)"
|
46
|
+
- value: 10.0
|
47
|
+
description: "static boost factor"
|
48
|
+
details:
|
49
|
+
- value: 10.0
|
50
|
+
description: "boostFactor"
|
51
|
+
- value: 0.9857722
|
52
|
+
description: "function score, product of:"
|
53
|
+
details:
|
54
|
+
- value: 1.0
|
55
|
+
description: "match filter: *:*"
|
56
|
+
- value: 0.9857722
|
57
|
+
description: "Function for field updated_at:"
|
58
|
+
details:
|
59
|
+
- value: 0.9857722
|
60
|
+
description: "exp(- MIN[Math.max(Math.abs(1.424917503E12(=doc value)\
|
61
|
+
\ - 1.432308964176E12(=origin))) - 0.0(=offset), 0)] * 1.938722193962471E-12)"
|
62
|
+
- value: 3.4028235E+38
|
63
|
+
description: "maxBoost"
|
38
64
|
- value: 0.5
|
39
65
|
description: "coord(1/2)"
|
66
|
+
- value: 1.0
|
67
|
+
description: "queryBoost"
|
@@ -0,0 +1,70 @@
|
|
1
|
+
---
|
2
|
+
_index: "travel"
|
3
|
+
_type: "cities"
|
4
|
+
_id: "1"
|
5
|
+
matched: true
|
6
|
+
explanation:
|
7
|
+
value: 887.19495
|
8
|
+
description: "function score, product of:"
|
9
|
+
details:
|
10
|
+
- value: 887.19495
|
11
|
+
description: "Math.min of"
|
12
|
+
details:
|
13
|
+
- value: 887.19495
|
14
|
+
description: "function score, score mode [multiply]"
|
15
|
+
details:
|
16
|
+
- value: 10.0
|
17
|
+
description: "function score, product of:"
|
18
|
+
details:
|
19
|
+
- value: 1.0
|
20
|
+
description: "match filter: QueryWrapperFilter(name:hawaii)"
|
21
|
+
- value: 10.0
|
22
|
+
description: "static boost factor"
|
23
|
+
details:
|
24
|
+
- value: 10.0
|
25
|
+
description: "boostFactor"
|
26
|
+
- value: 10.0
|
27
|
+
description: "function score, product of:"
|
28
|
+
details:
|
29
|
+
- value: 1.0
|
30
|
+
description: "match filter: QueryWrapperFilter(name:guam)"
|
31
|
+
- value: 10.0
|
32
|
+
description: "static boost factor"
|
33
|
+
details:
|
34
|
+
- value: 10.0
|
35
|
+
description: "boostFactor"
|
36
|
+
- value: 3.0
|
37
|
+
description: "function score, product of:"
|
38
|
+
details:
|
39
|
+
- value: 1.0
|
40
|
+
description: "match filter: cache(with_beach:T)"
|
41
|
+
- value: 3.0
|
42
|
+
description: "static boost factor"
|
43
|
+
details:
|
44
|
+
- value: 3.0
|
45
|
+
description: "boostFactor"
|
46
|
+
- value: 0.9857722
|
47
|
+
description: "function score, product of:"
|
48
|
+
details:
|
49
|
+
- value: 1.0
|
50
|
+
description: "match filter: *:*"
|
51
|
+
- value: 0.9857722
|
52
|
+
description: "Function for field updated_at:"
|
53
|
+
details:
|
54
|
+
- value: 0.9857722
|
55
|
+
description: "exp(- MIN[Math.max(Math.abs(1.424917503E12(=doc value)\
|
56
|
+
\ - 1.432308964176E12(=origin))) - 0.0(=offset), 0)] * 1.938722193962471E-12)"
|
57
|
+
- value: 3.0
|
58
|
+
description: "function score, product of:"
|
59
|
+
details:
|
60
|
+
- value: 1.0
|
61
|
+
description: "match filter: cache(region_id:[3 TO 3])"
|
62
|
+
- value: 3.0
|
63
|
+
description: "static boost factor"
|
64
|
+
details:
|
65
|
+
- value: 3.0
|
66
|
+
description: "boostFactor"
|
67
|
+
- value: 3.4028235E+38
|
68
|
+
description: "maxBoost"
|
69
|
+
- value: 1.0
|
70
|
+
description: "queryBoost"
|
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.0.3
|
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-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -40,6 +40,20 @@ dependencies:
|
|
40
40
|
version: '10.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
@@ -88,17 +102,22 @@ extensions: []
|
|
88
102
|
extra_rdoc_files: []
|
89
103
|
files:
|
90
104
|
- ".gitignore"
|
91
|
-
- ".travis.yml"
|
92
105
|
- Gemfile
|
93
106
|
- LICENSE.txt
|
94
107
|
- README.md
|
95
108
|
- Rakefile
|
109
|
+
- circle.yml
|
96
110
|
- elasticsearch-explain-response.gemspec
|
97
111
|
- lib/elasticsearch-explain-response.rb
|
98
112
|
- lib/elasticsearch/api/response/color_helper.rb
|
113
|
+
- lib/elasticsearch/api/response/description.rb
|
114
|
+
- lib/elasticsearch/api/response/explain_node.rb
|
115
|
+
- lib/elasticsearch/api/response/explain_parser.rb
|
116
|
+
- lib/elasticsearch/api/response/explain_renderer.rb
|
99
117
|
- lib/elasticsearch/api/response/explain_response.rb
|
100
118
|
- spec/elasticsearch/api/response/explain_response_spec.rb
|
101
119
|
- spec/fixtures/response1.yml
|
120
|
+
- spec/fixtures/response2.yml
|
102
121
|
- spec/spec_helper.rb
|
103
122
|
- spec/support/fixture_helper.rb
|
104
123
|
homepage: http://github.com/tomoya55/elasticsearch-explain-response
|
@@ -128,5 +147,6 @@ summary: Parser for Elasticserach Explain response
|
|
128
147
|
test_files:
|
129
148
|
- spec/elasticsearch/api/response/explain_response_spec.rb
|
130
149
|
- spec/fixtures/response1.yml
|
150
|
+
- spec/fixtures/response2.yml
|
131
151
|
- spec/spec_helper.rb
|
132
152
|
- spec/support/fixture_helper.rb
|
data/.travis.yml
DELETED