elasticsearch-explain-response 0.1.1 → 0.2.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 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