elasticsearch-explain-response 0.1.1 → 0.2.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
  SHA1:
3
- metadata.gz: 5d639bb65ed9cab1fce5676ce77a1299c520cb23
4
- data.tar.gz: 9f08ce5d4454956564f71d944d2b0dba419216c1
3
+ metadata.gz: 781deba8c0a09a57fbf21df20918be1f577bfb7c
4
+ data.tar.gz: 808cd8bd921f1a514cafd1a47457a0575bce6a74
5
5
  SHA512:
6
- metadata.gz: e39644ace8131f609212befdebb14376a1e493b6af6e876fa4a132f465b2a637a7a0380a5f4c2372c33125cc3cdc67fcdfb3b772b45040fda303d4a11570a744
7
- data.tar.gz: 69b68ad4f1266afa383d2b6e1e9ec56942790ca6ac0f498309b3405121789a13e43f13d1fe79e694e0dc277ce3586e6bafa3786b09e3e6d0c0b701666c7993ae
6
+ metadata.gz: 5af238a2068fa0aa82b168b830726674c44f0447c406220907c785f98752c23b11e68dfd6e4032859cf4bcac0683157dcdd2a8610db79f2c6d0764dd75898c8e
7
+ data.tar.gz: df4a958713b444e87804f7a7d40a1bb09814e00f562d4a0f14f948bcd0bfe76fc432aa3b2e42a7263f64eb5ff50e5687a0235dcf7071a956c3daf2b628968be2
@@ -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.1.1"
7
+ spec.version = "0.2.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,10 +1,10 @@
1
- require "elasticsearch/api/response/string_helper"
1
+ require "elasticsearch/api/response/helpers/string_helper"
2
2
 
3
3
  module Elasticsearch
4
4
  module API
5
5
  module Response
6
6
  class ExplainParser
7
- include StringHelper
7
+ include Helpers::StringHelper
8
8
 
9
9
  def parse(explain_tree)
10
10
  root = create_node(explain_tree, level: 0)
@@ -14,116 +14,116 @@ module Elasticsearch
14
14
 
15
15
  private
16
16
 
17
- def create_node(detail, level:)
18
- ExplainNode.new(
19
- score: detail["value"] || 0.0,
20
- description: parse_description(detail["description"]),
21
- details: detail["details"] || [],
22
- level: level
23
- )
24
- end
25
-
26
- def parse_details(node)
27
- node.details.each do |detail|
28
- child = create_node(detail, level: node.level.succ)
29
- node.children << child
30
- parse_details(child)
17
+ def create_node(detail, level:)
18
+ ExplainNode.new(
19
+ score: detail["value"] || 0.0,
20
+ description: parse_description(detail["description"]),
21
+ details: detail["details"] || [],
22
+ level: level
23
+ )
31
24
  end
32
- end
33
25
 
34
- def parse_description(description)
35
- case description
36
- when /\Aweight\((\w+)\:(\w+)\s+in\s+\d+\)\s+\[\w+\]\, result of\:\z/
37
- type = "weight"
38
- operation = "weight"
39
- operator = "x"
40
- field = $1
41
- value = $2
42
- when /\Aidf\(docFreq\=(\d+)\, maxDocs\=(\d+)\)\z/
43
- type = "idf"
44
- operation = "idf(#{$1}/#{$2})"
45
- when /\Atf\(freq\=([\d.]+)\)\, with freq of\:\z/
46
- type = "tf"
47
- operation = "tf(#{$1})"
48
- when /\Ascore\(doc\=\d+\,freq=[\d\.]+\)\, product of\:\z/
49
- type = "score"
50
- operation = "score"
51
- operator = "x"
52
- when /\Amatch filter\: (?:cache\()?(?:(?<op>[\w]+)\()*(?<c>.+)\)*\z/
53
- type = "match"
54
- operation = "match"
55
- operation += ".#{$~[:op]}" if $~[:op] && !%w[QueryWrapperFilter].include?($~[:op])
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(", ")
61
- when /\AFunction for field ([\w\_]+)\:\z/
62
- type = "func"
63
- operation = "func"
64
- field = $1
65
- when /\AqueryWeight\, product of\:\z/
66
- type = "queryWeight"
67
- operation = "queryWeight"
68
- operator = "x"
69
- when /\AfieldWeight in \d+\, product of\:\z/
70
- type = "fieldWeight"
71
- operation = "fieldWeight"
72
- operator = "x"
73
- when /\AqueryNorm/
74
- type = "queryNorm"
75
- operation = "queryNorm"
76
- when /\Afunction score\, product of\:\z/,
77
- /\Afunction score\, score mode \[multiply\]\z/
78
- type = "func score"
79
- operator = "x"
80
- when /\Afunction score\, score mode \[sum\]\z/
81
- type = "func score"
82
- operator = "+"
83
- when /\Ascript score function\, computed with script:\"(?<s>.+)\"\s*(?:and parameters:\s*(?<p>.+))?/m
84
- type = "script"
85
- operation = "script"
86
- script, param = $~[:s], $~[:p]
87
- script = script.gsub("\n", '')
88
- script = "\"#{script}\""
89
- param.gsub!("\n", '') if param
90
- field = script.scan(/doc\[\'([\w\.]+)\'\]/).flatten.uniq.compact.join(" ")
91
- value = [script, param].join(" ")
92
- when /\AConstantScore\(.+\), product of\:\z/
93
- type = "constant"
94
- operation = "constant"
95
- when "static boost factor", "boostFactor"
96
- type = "boost"
97
- operation = "boost"
98
- when "product of:", "[multiply]"
99
- type = "product"
100
- operation = "product"
101
- operator = "x"
102
- when "Math.min of"
103
- type = "min"
104
- operator = "min"
105
- when "Math.max of"
106
- type = "max"
107
- operator = "max"
108
- when "sum of:"
109
- type = "sum"
110
- operator = "+"
111
- when "maxBoost"
112
- type = "maxBoost"
113
- else
114
- type = description
115
- operation = description
26
+ def parse_details(node)
27
+ node.details.each do |detail|
28
+ child = create_node(detail, level: node.level.succ)
29
+ node.children << child
30
+ parse_details(child)
31
+ end
116
32
  end
117
33
 
118
- Description.new(
119
- raw: description,
120
- type: type,
121
- operator: operator,
122
- operation: operation,
123
- field: field,
124
- value: value,
125
- )
126
- end
34
+ def parse_description(description)
35
+ case description
36
+ when /\Aweight\((\w+)\:(\w+)\s+in\s+\d+\)\s+\[\w+\]\, result of\:\z/
37
+ type = "weight"
38
+ operation = "weight"
39
+ operator = "x"
40
+ field = $1
41
+ value = $2
42
+ when /\Aidf\(docFreq\=(\d+)\, maxDocs\=(\d+)\)\z/
43
+ type = "idf"
44
+ operation = "idf(#{$1}/#{$2})"
45
+ when /\Atf\(freq\=([\d.]+)\)\, with freq of\:\z/
46
+ type = "tf"
47
+ operation = "tf(#{$1})"
48
+ when /\Ascore\(doc\=\d+\,freq=[\d\.]+\)\, product of\:\z/
49
+ type = "score"
50
+ operation = "score"
51
+ operator = "x"
52
+ when /\Amatch filter\: (?:cache\()?(?:(?<op>[\w]+)\()*(?<c>.+)\)*\z/
53
+ type = "match"
54
+ operation = "match"
55
+ operation += ".#{$~[:op]}" if $~[:op] && !%w[QueryWrapperFilter].include?($~[:op])
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(", ")
61
+ when /\AFunction for field ([\w\_]+)\:\z/
62
+ type = "func"
63
+ operation = "func"
64
+ field = $1
65
+ when /\AqueryWeight\, product of\:\z/
66
+ type = "queryWeight"
67
+ operation = "queryWeight"
68
+ operator = "x"
69
+ when /\AfieldWeight in \d+\, product of\:\z/
70
+ type = "fieldWeight"
71
+ operation = "fieldWeight"
72
+ operator = "x"
73
+ when /\AqueryNorm/
74
+ type = "queryNorm"
75
+ operation = "queryNorm"
76
+ when /\Afunction score\, product of\:\z/,
77
+ /\Afunction score\, score mode \[multiply\]\z/
78
+ type = "func score"
79
+ operator = "x"
80
+ when /\Afunction score\, score mode \[sum\]\z/
81
+ type = "func score"
82
+ operator = "+"
83
+ when /\Ascript score function\, computed with script:\"(?<s>.+)\"\s*(?:and parameters:\s*(?<p>.+))?/m
84
+ type = "script"
85
+ operation = "script"
86
+ script, param = $~[:s], $~[:p]
87
+ script = script.gsub("\n", '')
88
+ script = "\"#{script}\""
89
+ param.gsub!("\n", '') if param
90
+ field = script.scan(/doc\[\'([\w\.]+)\'\]/).flatten.uniq.compact.join(" ")
91
+ value = [script, param].join(" ")
92
+ when /\AConstantScore\(.+\), product of\:\z/
93
+ type = "constant"
94
+ operation = "constant"
95
+ when "static boost factor", "boostFactor"
96
+ type = "boost"
97
+ operation = "boost"
98
+ when /product\sof\:?/, "[multiply]"
99
+ type = "product"
100
+ operation = "product"
101
+ operator = "x"
102
+ when "Math.min of"
103
+ type = "min"
104
+ operator = "min"
105
+ when "Math.max of"
106
+ type = "max"
107
+ operator = "max"
108
+ when /sum of\:?/
109
+ type = "sum"
110
+ operator = "+"
111
+ when "maxBoost"
112
+ type = "maxBoost"
113
+ else
114
+ type = description
115
+ operation = description
116
+ end
117
+
118
+ Description.new(
119
+ raw: description,
120
+ type: type,
121
+ operator: operator,
122
+ operation: operation,
123
+ field: field,
124
+ value: value,
125
+ )
126
+ end
127
127
  end
128
128
  end
129
129
  end
@@ -33,108 +33,17 @@ module Elasticsearch
33
33
 
34
34
  private
35
35
 
36
- def render_result(node)
37
- @buffer << " " * node.level * 2 + [render_score(node.score), "=", render_details(node)].flatten.join(" ")
38
- end
39
-
40
- def render_score(score)
41
- value = if !@plain_score && score > 1_000
42
- sprintf("%1.2g", score.round(2))
43
- else
44
- score.round(2).to_s
45
- end
46
- ansi(value, :magenta, :bright)
47
- end
48
-
49
- def render_details(node)
50
- case node.type
51
- when "func score"
52
- render_boost_match_details(node)
53
- else
54
- render_node_details(node)
55
- end
56
- end
57
-
58
- def render_node_details(node)
59
- node.children.map do |child|
60
- check_node = block_given? ? yield(child) : true
61
- check_node && render_node(child) || nil
62
- end.compact.join(" #{node.operator} ")
63
- end
64
-
65
- def render_boost_match_details(node)
66
- match = node.children.find {|c| c.type == "match" }
67
- boost = node.children.find {|c| c.type == "boost" }
68
- if match && boost
69
- [boost.score, render_node(match)].join(" x ")
70
- else
71
- render_node_details(node) {|n| !n.match_all? }
36
+ def render_result(node)
37
+ @buffer << " " * node.level * 2 + [render_score(node.score), "=", render_details(node)].flatten.join(" ")
72
38
  end
73
- end
74
39
 
75
- def recursive_render_details(node)
76
- details = node.children.map do |child|
77
- if child.children.any? && child.level <= @max
78
- if child.func?
79
- render_node(child)
80
- elsif child.children[0].match? && child.children[1].boost?
81
- match = child.children[0]
82
- boost = child.children[1]
83
- "#{render_score(boost.score)}(#{render_description(match.description)})"
84
- else
85
- recursive_render_details(child)
86
- end
40
+ def render_details(node)
41
+ if node.has_children?
42
+ node.children.map(&method(:render_node)).compact.join(" #{node.operator} ")
87
43
  else
88
- if !child.match_all?
89
- render_node(child)
90
- end
44
+ render_node(node)
91
45
  end
92
- end.compact
93
-
94
- if details.size > 1
95
- wrap_paren(details.join(" #{node.operator} "))
96
- else
97
- details[0]
98
46
  end
99
- end
100
-
101
- def render_node(node)
102
- text = render_score(node.score)
103
- desc = render_description(node.description)
104
- text = "#{text}(#{desc})" unless desc.empty?
105
- text
106
- end
107
-
108
- def render_description(description)
109
- text = ''
110
- text = description.operation if description.operation
111
- if description.field && description.value
112
- if @show_values
113
- text += "(#{field(description.field)}:#{value(description.value)})"
114
- else
115
- text += "(#{field(description.field)})"
116
- end
117
- elsif description.field
118
- text += "(#{field(description.field)})"
119
- end
120
- text
121
- end
122
-
123
- def field(str)
124
- ansi(str, :blue ,:bright)
125
- end
126
-
127
- def value(str)
128
- ansi(str, :green)
129
- end
130
-
131
- def wrap_paren(string)
132
- if string.start_with?("(") && string.end_with?(")")
133
- string
134
- else
135
- "(" + string + ")"
136
- end
137
- end
138
47
  end
139
48
  end
140
49
  end
@@ -1,8 +1,10 @@
1
- require "elasticsearch/api/response/renderers/hash_renderer"
2
1
  require "elasticsearch/api/response/explain_node"
3
2
  require "elasticsearch/api/response/description"
4
3
  require "elasticsearch/api/response/explain_parser"
5
- require "elasticsearch/api/response/explain_renderer"
4
+ require "elasticsearch/api/response/explain_trimmer"
5
+ require "elasticsearch/api/response/renderers/standard_renderer"
6
+ require "elasticsearch/api/response/renderers/inline_renderer"
7
+ require "elasticsearch/api/response/renderers/hash_renderer"
6
8
 
7
9
  module Elasticsearch
8
10
  module API
@@ -34,35 +36,43 @@ module Elasticsearch
34
36
  def render(result, options = {})
35
37
  new(result["explanation"], options).render
36
38
  end
39
+
40
+ def result_as_hash(result, options = {})
41
+ new(result["explanation"], options).render_as_hash
42
+ end
37
43
  end
38
44
 
39
- attr_reader :explain
45
+ attr_reader :explain, :trim, :rendering_options
40
46
 
41
47
  def initialize(explain, options = {})
42
48
  @explain = explain
43
49
  @indent = 0
44
- @renderer = ExplainRenderer.new({ colorize: true }.merge(options))
50
+ @trim = options.has_key?(:trim) ? options.delete(:trim) : true
51
+ @rendering_options = options
52
+
53
+ parse_details
45
54
  end
46
55
 
47
56
  def render
48
- parse_details
49
- @renderer.render(@root)
57
+ Renderers::StandardRenderer.new({ colorize: true }.merge(rendering_options)).render(@root)
50
58
  end
51
59
 
52
60
  def render_in_line
53
- parse_details
54
- @renderer.render_in_line(@root)
61
+ Renderers::InlineRenderer.new({ colorize: true }.merge(rendering_options)).render(@root)
55
62
  end
56
63
 
57
64
  def render_as_hash
58
- parse_details
59
65
  Renderers::HashRenderer.new.render(@root)
60
66
  end
61
67
 
62
68
  private
63
69
 
64
70
  def parse_details
65
- @root ||= ExplainParser.new.parse(explain)
71
+ @root ||= begin
72
+ tree = ExplainParser.new.parse(explain)
73
+ tree = ExplainTrimmer.new.trim(tree) if trim
74
+ tree
75
+ end
66
76
  end
67
77
  end
68
78
  end
@@ -0,0 +1,76 @@
1
+ module Elasticsearch
2
+ module API
3
+ module Response
4
+ class ExplainTrimmer
5
+ def initialize
6
+ end
7
+
8
+ def trim(tree)
9
+ recursive_trim(tree)
10
+ end
11
+
12
+ def recursive_trim(node)
13
+ trim_node(node) if node.details.any?
14
+ end
15
+
16
+ private
17
+
18
+ def trim_node(node)
19
+ case
20
+ when node.func_score?
21
+ trim_func_score_node(node)
22
+ when node.min?
23
+ trim_min_score_node(node)
24
+ else
25
+ trim_default_node(node)
26
+ end
27
+ end
28
+
29
+ def trim_func_score_node(node)
30
+ case node.children.size
31
+ when 2
32
+ match = node.children.find(&:match?)
33
+ if match
34
+ other = (node.children - [match])[0]
35
+ if match.score_one? && other.score == node.score
36
+ entity = match.field == "*" ? other : match
37
+ return merge_function_score_node(node, entity)
38
+ end
39
+ end
40
+ end
41
+
42
+ trim_default_node(node)
43
+ end
44
+
45
+ def trim_default_node(node)
46
+ if node.has_children?
47
+ node.children = node.children.map(&method(:trim_node)).compact
48
+ end
49
+ node
50
+ end
51
+
52
+ # @note show only the node with a minimum score
53
+ def trim_min_score_node(node)
54
+ child = node.children.find {|n| n.score == node.score }
55
+ trim_node(child)
56
+ end
57
+
58
+ def merge_function_score_node(current, entity)
59
+ ExplainNode.new(
60
+ score: current.score,
61
+ level: current.level,
62
+ details: current.details,
63
+ description: Description.new(
64
+ raw: current.description.raw,
65
+ type: entity.type,
66
+ operator: entity.operator,
67
+ operation: entity.operation,
68
+ field: entity.field,
69
+ value: entity.value
70
+ )
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,37 @@
1
+ module Elasticsearch
2
+ module API
3
+ module Response
4
+ module Helpers
5
+ module ColorHelper
6
+ def colorized?
7
+ unless @ansi_loaded
8
+ @colorized = load_ansi
9
+ else
10
+ !!@colorized
11
+ end
12
+ end
13
+
14
+ def disable_colorization
15
+ @ansi_loaded = true
16
+ @colorized = false
17
+ end
18
+
19
+ def load_ansi
20
+ require "ansi/core"
21
+ true
22
+ rescue LoadError
23
+ false
24
+ end
25
+
26
+ def ansi(str, *codes)
27
+ if colorized?
28
+ str.to_s.ansi(*codes)
29
+ else
30
+ str.to_s
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Elasticsearch
2
+ module API
3
+ module Response
4
+ module Helpers
5
+ module StringHelper
6
+ WORD = /[\w\.\*]+/
7
+ WITH_QUOTE = /"[^"]*"/
8
+ WITH_BRACKET = /\[[^\]]*\]/
9
+ QUOTE_TOKENIZER = /(?:(?<field>#{WORD})(\:(?<value>(#{WORD}|#{WITH_QUOTE}|#{WITH_BRACKET})))?)+/
10
+
11
+ # @return [Hash] field name as a key and values as a value
12
+ def tokenize_contents(string)
13
+ string
14
+ .scan(QUOTE_TOKENIZER)
15
+ .each_with_object(Hash.new{|h,k| h[k] = []}) { |(field, value), memo|
16
+ memo[field] << value
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,69 @@
1
+ require "elasticsearch/api/response/helpers/color_helper"
2
+
3
+ module Elasticsearch
4
+ module API
5
+ module Response
6
+ module Renderers
7
+ class BaseRenderer
8
+ include Helpers::ColorHelper
9
+
10
+ def initialize(options = {})
11
+ disable_colorization if options[:colorize] == false
12
+ @max = options[:max] || 3
13
+ @plain_score = options[:plain_score] == true
14
+ @show_values = options[:show_values] == true
15
+ end
16
+
17
+ private
18
+
19
+ def render_score(score)
20
+ value = if !@plain_score && score > 1_000
21
+ sprintf("%1.2g", score.round(2))
22
+ else
23
+ score.round(2).to_s
24
+ end
25
+ ansi(value, :magenta, :bright)
26
+ end
27
+
28
+ def render_node(node)
29
+ text = render_score(node.score)
30
+ desc = render_description(node.description)
31
+ text = "#{text}(#{desc})" unless desc.empty?
32
+ text
33
+ end
34
+
35
+ def render_description(description)
36
+ text = ''
37
+ text = description.operation if description.operation
38
+ if description.field && description.value
39
+ if @show_values
40
+ text += "(#{field(description.field)}:#{value(description.value)})"
41
+ else
42
+ text += "(#{field(description.field)})"
43
+ end
44
+ elsif description.field
45
+ text += "(#{field(description.field)})"
46
+ end
47
+ text
48
+ end
49
+
50
+ def field(str)
51
+ ansi(str, :blue ,:bright)
52
+ end
53
+
54
+ def value(str)
55
+ ansi(str, :green)
56
+ end
57
+
58
+ def wrap_paren(string)
59
+ if string.start_with?("(") && string.end_with?(")")
60
+ string
61
+ else
62
+ "(" + string + ")"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -3,8 +3,6 @@ module Elasticsearch
3
3
  module Response
4
4
  module Renderers
5
5
  class HashRenderer
6
- def initialize
7
- end
8
6
 
9
7
  def render(tree)
10
8
  recursive_render(tree)
@@ -16,24 +14,24 @@ module Elasticsearch
16
14
 
17
15
  private
18
16
 
19
- def format_node(node)
20
- node.as_json.tap do |hash|
21
- if node.has_children?
22
- children = format_children(node, hash)
23
- hash[:children] = children if children.any?
17
+ def format_node(node)
18
+ node.as_json.tap do |hash|
19
+ if node.has_children?
20
+ children = format_children(node, hash)
21
+ hash[:children] = children if children.any?
22
+ end
24
23
  end
25
24
  end
26
- end
27
25
 
28
- def format_children(node, hash)
29
- node.children.map(&method(:format_node)).compact.tap do |children|
30
- remove_dup(children, hash)
26
+ def format_children(node, hash)
27
+ node.children.map(&method(:format_node)).compact.tap do |children|
28
+ remove_dup(children, hash)
29
+ end
31
30
  end
32
- end
33
31
 
34
- def remove_dup(collection, target)
35
- collection.delete_if {|elm| elm == target }
36
- end
32
+ def remove_dup(collection, target)
33
+ collection.delete_if {|elm| elm == target }
34
+ end
37
35
  end
38
36
  end
39
37
  end
@@ -0,0 +1,36 @@
1
+ require "elasticsearch/api/response/renderers/base_renderer"
2
+
3
+ module Elasticsearch
4
+ module API
5
+ module Response
6
+ module Renderers
7
+ class InlineRenderer < BaseRenderer
8
+
9
+ def render(tree)
10
+ [render_score(tree.score), "=", recursive_render_details(tree)].flatten.join(" ")
11
+ end
12
+
13
+ private
14
+
15
+ def recursive_render_details(node)
16
+ details = node.children.map do |child|
17
+ if child.children.any? && child.level <= @max
18
+ recursive_render_details(child)
19
+ else
20
+ if !child.match_all?
21
+ render_node(child)
22
+ end
23
+ end
24
+ end.compact
25
+
26
+ if details.size > 1
27
+ wrap_paren(details.join(" #{node.operator} "))
28
+ else
29
+ details[0]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ require "elasticsearch/api/response/renderers/base_renderer"
2
+
3
+ module Elasticsearch
4
+ module API
5
+ module Response
6
+ module Renderers
7
+
8
+ class StandardRenderer < BaseRenderer
9
+
10
+ def render(tree)
11
+ @buffer = []
12
+ recursive_render(tree)
13
+ @buffer.join("\n")
14
+ end
15
+
16
+ private
17
+
18
+ def recursive_render(node)
19
+ return if node.level > @max
20
+ render_result(node) if node.details.any?
21
+ node.children.each do |child|
22
+ recursive_render(child)
23
+ end
24
+ end
25
+
26
+ def render_result(node)
27
+ @buffer << " " * node.level * 2 + [render_score(node.score), "=", render_details(node)].flatten.join(" ")
28
+ end
29
+
30
+ def render_details(node)
31
+ if node.has_children?
32
+ node.children.map(&method(:render_node)).compact.join(" #{node.operator} ")
33
+ else
34
+ render_node(node)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -41,7 +41,7 @@ describe Elasticsearch::API::Response::ExplainResponse do
41
41
  end
42
42
 
43
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})) x 1.0(constant) min 3.4e+38) x 0.5(coord(1/2)) x 1.0(queryBoost)")
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})) x 1.0(constant) x 0.5(coord(1/2)) x 1.0(queryBoost)")
45
45
  end
46
46
 
47
47
  context "with fake_response2" do
@@ -50,7 +50,7 @@ describe Elasticsearch::API::Response::ExplainResponse do
50
50
  end
51
51
 
52
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)")
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]))) x 1.0(queryBoost)")
54
54
  end
55
55
  end
56
56
  end
@@ -61,7 +61,7 @@ describe Elasticsearch::API::Response::ExplainResponse do
61
61
  end
62
62
 
63
63
  it "returns summary of explain in line" do
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)) x 1.0(constant) min 3.4e+38) x 0.5(coord(1/2)) x 1.0(queryBoost)")
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)) x 1.0(constant) x 0.5(coord(1/2)) x 1.0(queryBoost)")
65
65
  end
66
66
 
67
67
  context "with fake_response2" do
@@ -70,7 +70,7 @@ describe Elasticsearch::API::Response::ExplainResponse do
70
70
  end
71
71
 
72
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)")
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))) x 1.0(queryBoost)")
74
74
  end
75
75
  end
76
76
  end
@@ -95,7 +95,6 @@ describe Elasticsearch::API::Response::ExplainResponse do
95
95
  it "returns summary of explain in lines" do
96
96
  expect(subject).to match_array [
97
97
  "0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
98
- " 0.11 = 0.11 min 3.4e+38",
99
98
  " 0.11 = 0.11(weight(_all:smith))",
100
99
  " 0.11 = 0.11(score)"
101
100
  ]
@@ -107,13 +106,12 @@ describe Elasticsearch::API::Response::ExplainResponse do
107
106
  it "returns summary of explain in lines" do
108
107
  expect(subject).to match_array([
109
108
  "0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
110
- " 0.11 = 0.11 min 3.4e+38",
111
109
  " 0.11 = 0.11(weight(_all:smith))",
112
110
  " 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})) x 1.0(constant)",
111
+ " 0.11 = 0.43(queryWeight) x 0.25(fieldWeight) 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})) x 1.0(constant)",
114
112
  " 0.43 = 1.0(idf(2/3)) x 0.43(queryNorm)",
115
113
  " 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))",
114
+ " 10.0 = 10.0(match(name.raw:smith))",
117
115
  " 0.99 = 0.99(func(updated_at))"
118
116
  ])
119
117
  end
@@ -125,7 +123,6 @@ describe Elasticsearch::API::Response::ExplainResponse do
125
123
  it "returns summary of explain in lines" do
126
124
  expect(subject).to match_array([
127
125
  "0.05 = 0.11 x 0.5(coord(1/2)) x 1.0(queryBoost)",
128
- " 0.11 = 0.11 min 3.4028235e+38",
129
126
  " 0.11 = 0.11(weight(_all:smith))",
130
127
  " 0.11 = 0.11(score)"
131
128
  ])
@@ -33,7 +33,7 @@ explanation:
33
33
  details:
34
34
  - value: 10.0
35
35
  description: "boostFactor"
36
- - value: 1.0
36
+ - value: 0.7
37
37
  description: "function score, product of:"
38
38
  details:
39
39
  - value: 1.0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticsearch-explain-response
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomoya Hirano
@@ -109,14 +109,18 @@ files:
109
109
  - circle.yml
110
110
  - elasticsearch-explain-response.gemspec
111
111
  - lib/elasticsearch-explain-response.rb
112
- - lib/elasticsearch/api/response/color_helper.rb
113
112
  - lib/elasticsearch/api/response/description.rb
114
113
  - lib/elasticsearch/api/response/explain_node.rb
115
114
  - lib/elasticsearch/api/response/explain_parser.rb
116
115
  - lib/elasticsearch/api/response/explain_renderer.rb
117
116
  - lib/elasticsearch/api/response/explain_response.rb
117
+ - lib/elasticsearch/api/response/explain_trimmer.rb
118
+ - lib/elasticsearch/api/response/helpers/color_helper.rb
119
+ - lib/elasticsearch/api/response/helpers/string_helper.rb
120
+ - lib/elasticsearch/api/response/renderers/base_renderer.rb
118
121
  - lib/elasticsearch/api/response/renderers/hash_renderer.rb
119
- - lib/elasticsearch/api/response/string_helper.rb
122
+ - lib/elasticsearch/api/response/renderers/inline_renderer.rb
123
+ - lib/elasticsearch/api/response/renderers/standard_renderer.rb
120
124
  - spec/elasticsearch/api/response/explain_response_spec.rb
121
125
  - spec/fixtures/response1.yml
122
126
  - spec/fixtures/response2.yml
@@ -1,35 +0,0 @@
1
- module Elasticsearch
2
- module API
3
- module Response
4
- module ColorHelper
5
- def colorized?
6
- unless @ansi_loaded
7
- @colorized = load_ansi
8
- else
9
- !!@colorized
10
- end
11
- end
12
-
13
- def disable_colorization
14
- @ansi_loaded = true
15
- @colorized = false
16
- end
17
-
18
- def load_ansi
19
- require "ansi/core"
20
- true
21
- rescue LoadError
22
- false
23
- end
24
-
25
- def ansi(str, *codes)
26
- if colorized?
27
- str.to_s.ansi(*codes)
28
- else
29
- str.to_s
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,21 +0,0 @@
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