elastic-rails 0.5.0 → 0.6.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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/lib/elastic/commands/build_agg_from_params.rb +37 -20
  3. data/lib/elastic/commands/build_query_from_params.rb +109 -79
  4. data/lib/elastic/commands/build_sort_from_params.rb +41 -0
  5. data/lib/elastic/commands/import_index_documents.rb +1 -1
  6. data/lib/elastic/configuration.rb +14 -11
  7. data/lib/elastic/core/adaptor.rb +0 -1
  8. data/lib/elastic/core/base_middleware.rb +2 -2
  9. data/lib/elastic/core/default_middleware.rb +1 -1
  10. data/lib/elastic/core/definition.rb +53 -34
  11. data/lib/elastic/core/query_assembler.rb +51 -19
  12. data/lib/elastic/core/query_config.rb +4 -5
  13. data/lib/elastic/core/source_formatter.rb +12 -31
  14. data/lib/elastic/datatypes/date.rb +32 -0
  15. data/lib/elastic/datatypes/default.rb +74 -0
  16. data/lib/elastic/datatypes/string.rb +7 -0
  17. data/lib/elastic/datatypes/term.rb +10 -0
  18. data/lib/elastic/datatypes/time.rb +29 -0
  19. data/lib/elastic/dsl/bool_query_builder.rb +4 -0
  20. data/lib/elastic/fields/nested.rb +24 -6
  21. data/lib/elastic/fields/value.rb +69 -25
  22. data/lib/elastic/nested_query.rb +34 -0
  23. data/lib/elastic/nested_type.rb +10 -0
  24. data/lib/elastic/nodes/agg/average.rb +3 -1
  25. data/lib/elastic/nodes/agg/base_metric.rb +6 -5
  26. data/lib/elastic/nodes/agg/date_histogram.rb +4 -4
  27. data/lib/elastic/nodes/agg/maximum.rb +3 -1
  28. data/lib/elastic/nodes/agg/minimum.rb +3 -1
  29. data/lib/elastic/nodes/agg/stats.rb +3 -1
  30. data/lib/elastic/nodes/agg/sum.rb +3 -1
  31. data/lib/elastic/nodes/agg/terms.rb +4 -4
  32. data/lib/elastic/nodes/agg/top_hits.rb +6 -6
  33. data/lib/elastic/nodes/and.rb +2 -2
  34. data/lib/elastic/nodes/base.rb +5 -3
  35. data/lib/elastic/nodes/base_agg.rb +2 -2
  36. data/lib/elastic/nodes/boolean.rb +34 -14
  37. data/lib/elastic/nodes/concerns/aggregable.rb +12 -8
  38. data/lib/elastic/nodes/concerns/bucketed.rb +4 -7
  39. data/lib/elastic/nodes/concerns/field_query.rb +10 -0
  40. data/lib/elastic/nodes/concerns/hit_provider.rb +11 -0
  41. data/lib/elastic/nodes/function_score.rb +8 -7
  42. data/lib/elastic/nodes/match.rb +6 -5
  43. data/lib/elastic/nodes/nested.rb +28 -7
  44. data/lib/elastic/nodes/range.rb +9 -8
  45. data/lib/elastic/nodes/search.rb +11 -10
  46. data/lib/elastic/nodes/sort.rb +82 -0
  47. data/lib/elastic/nodes/term.rb +7 -6
  48. data/lib/elastic/query.rb +24 -12
  49. data/lib/elastic/railtie.rb +7 -0
  50. data/lib/elastic/railties/ar_helpers.rb +2 -1
  51. data/lib/elastic/railties/configuration_extensions.rb +13 -0
  52. data/lib/elastic/railties/indexable_record.rb +1 -2
  53. data/lib/elastic/railties/indexing_job.rb +8 -0
  54. data/lib/elastic/railties/type_extensions.rb +1 -1
  55. data/lib/elastic/results/aggregations.rb +1 -1
  56. data/lib/elastic/results/bucket.rb +3 -2
  57. data/lib/elastic/results/grouped_result.rb +31 -1
  58. data/lib/elastic/results/hit.rb +8 -20
  59. data/lib/elastic/results/hit_collection.rb +2 -33
  60. data/lib/elastic/results/root.rb +3 -2
  61. data/lib/elastic/results/scored_collection.rb +44 -0
  62. data/lib/elastic/results/scored_item.rb +10 -0
  63. data/lib/elastic/shims/base.rb +6 -4
  64. data/lib/elastic/shims/concerns/hit_picker.rb +41 -0
  65. data/lib/elastic/shims/field_picking.rb +20 -0
  66. data/lib/elastic/shims/grouping.rb +17 -8
  67. data/lib/elastic/shims/id_picking.rb +15 -0
  68. data/lib/elastic/shims/populating.rb +4 -11
  69. data/lib/elastic/shims/reducing.rb +3 -7
  70. data/lib/elastic/shims/total_picking.rb +16 -0
  71. data/lib/elastic/type.rb +6 -3
  72. data/lib/elastic/types/base_type.rb +16 -9
  73. data/lib/elastic/types/faceted_type.rb +1 -1
  74. data/lib/elastic/types/nestable_type.rb +2 -2
  75. data/lib/elastic/version.rb +1 -1
  76. data/lib/elastic.rb +15 -0
  77. data/lib/generators/elastic/index_generator.rb +1 -1
  78. data/lib/generators/elastic/init_generator.rb +10 -0
  79. data/lib/generators/elastic/templates/elastic.yml +14 -0
  80. data/lib/generators/elastic/templates/index.rb +0 -1
  81. metadata +21 -2
@@ -20,11 +20,11 @@ module Elastic::Nodes::Agg
20
20
  prepare_clone(super)
21
21
  end
22
22
 
23
- def render
24
- options = { 'field' => @field.to_s }
25
- options['size'] = @size if @size
23
+ def render(_options = {})
24
+ hash = { 'field' => @field.to_s }
25
+ hash['size'] = @size if @size
26
26
 
27
- render_aggs 'terms' => options
27
+ render_aggs({ 'terms' => hash }, _options)
28
28
  end
29
29
 
30
30
  private
@@ -2,15 +2,15 @@ module Elastic::Nodes
2
2
  class TopHits < BaseAgg
3
3
  include Concerns::HitProvider
4
4
 
5
- def render
6
- options = {}
7
- render_hit_options options
5
+ def render(_options = {})
6
+ hash = {}
7
+ render_hit_options hash
8
8
 
9
- { 'top_hits' => options }
9
+ { 'top_hits' => hash }
10
10
  end
11
11
 
12
- def handle_result(_raw)
13
- hits = _raw['hits'] ? _raw['hits']['hits'].map { |h| Elastic::Results::Hit.new h } : []
12
+ def handle_result(_raw, _formatter)
13
+ hits = _raw['hits'] ? prepare_hits(_raw['hits']['hits'], _formatter) : []
14
14
  Elastic::Results::HitCollection.new(hits)
15
15
  end
16
16
  end
@@ -27,8 +27,8 @@ module Elastic::Nodes
27
27
  prepare_clone(super, new_children)
28
28
  end
29
29
 
30
- def render
31
- { operation => @children.map(&:render) }
30
+ def render(_options = {})
31
+ { operation => @children.map { |c| c.render(_options) } }
32
32
  end
33
33
 
34
34
  private
@@ -10,7 +10,7 @@ module Elastic::Nodes
10
10
  _block.call(self)
11
11
  end
12
12
 
13
- def render
13
+ def render(_options = {})
14
14
  raise NotImplementedError, 'render must be implemented by each node'
15
15
  end
16
16
 
@@ -22,8 +22,8 @@ module Elastic::Nodes
22
22
  self.class.new
23
23
  end
24
24
 
25
- def handle_result(_raw)
26
- nil
25
+ def handle_result(_raw, _formatter)
26
+ _raw
27
27
  end
28
28
  end
29
29
  end
@@ -32,3 +32,5 @@ require "elastic/nodes/concerns/hit_provider"
32
32
  require "elastic/nodes/concerns/aggregable"
33
33
  require "elastic/nodes/concerns/bucketed"
34
34
  require "elastic/nodes/concerns/boostable"
35
+ require "elastic/nodes/concerns/field_query"
36
+
@@ -7,11 +7,11 @@ module Elastic::Nodes
7
7
  end
8
8
 
9
9
  def initialize
10
- @name = 'default'
10
+ @name = :default
11
11
  end
12
12
 
13
13
  def name=(_value)
14
- @name = _value.to_s
14
+ @name = _value.to_sym
15
15
  end
16
16
 
17
17
  def clone
@@ -16,6 +16,8 @@ module Elastic::Nodes
16
16
  super
17
17
  @musts = []
18
18
  @shoulds = []
19
+ @filters = []
20
+ @disable_coord = !Elastic::Configuration.coord_similarity
19
21
  end
20
22
 
21
23
  def must(_node)
@@ -26,6 +28,10 @@ module Elastic::Nodes
26
28
  @shoulds << _node
27
29
  end
28
30
 
31
+ def filter(_node)
32
+ @filters << _node
33
+ end
34
+
29
35
  def musts=(_nodes)
30
36
  @musts = _nodes.dup.to_a
31
37
  end
@@ -42,43 +48,57 @@ module Elastic::Nodes
42
48
  @shoulds.each
43
49
  end
44
50
 
51
+ def filters=(_nodes)
52
+ @filters = _nodes.dup.to_a
53
+ end
54
+
55
+ def filters
56
+ @filters.each
57
+ end
58
+
45
59
  def traverse(&_block)
46
60
  super
47
61
  @shoulds.each { |c| c.traverse(&_block) }
48
62
  @musts.each { |c| c.traverse(&_block) }
49
63
  end
50
64
 
51
- def render
52
- options = {}.tap do |boolean|
53
- boolean['must'] = @musts.map(&:render) if !@musts.empty?
54
- boolean['should'] = @shoulds.map(&:render) if !@shoulds.empty?
55
- boolean['minimum_should_match'] = minimum_should_match unless minimum_should_match.nil?
56
- boolean['disable_coord'] = true if disable_coord
57
- render_boost(boolean)
58
- end
65
+ def render(_options = {})
66
+ hash = {}
67
+ hash['must'] = @musts.map { |n| n.render(_options) } if !@musts.empty?
68
+ hash['should'] = @shoulds.map { |n| n.render(_options) } if !@shoulds.empty?
69
+ hash['filters'] = @filters.map { |n| n.render(_options) } if !@filters.empty?
70
+ hash['minimum_should_match'] = minimum_should_match unless minimum_should_match.nil?
71
+ hash['disable_coord'] = true if disable_coord
72
+ render_boost(hash)
59
73
 
60
- { "bool" => options }
74
+ { "bool" => hash }
61
75
  end
62
76
 
63
77
  def clone
64
- prepare_clone super, @musts.map(&:clone), @shoulds.map(&:clone)
78
+ prepare_clone super, @musts.map(&:clone), @shoulds.map(&:clone), @filters.map(&:clone)
65
79
  end
66
80
 
67
81
  def simplify
68
82
  new_must = @musts.map(&:simplify)
69
83
  new_should = @shoulds.map(&:simplify)
84
+ new_filter = @filters.map(&:simplify)
70
85
 
71
- return new_must.first if new_must.length == 1 && new_should.empty?
72
- return new_should.first if new_should.length == 1 && new_must.empty? # at least 1 should match
86
+ # TODO: detect must elements with boost = 0 and move them to "filter"
87
+
88
+ if boost.nil? && (new_must.length + new_should.length + new_filter.length) == 1
89
+ return new_must.first unless new_must.empty?
90
+ return new_should.first unless new_should.empty? # at least 1 should match
91
+ end
73
92
 
74
- prepare_clone(super, new_must, new_should)
93
+ prepare_clone(super, new_must, new_should, new_filter)
75
94
  end
76
95
 
77
96
  private
78
97
 
79
- def prepare_clone(_clone, _musts, _shoulds)
98
+ def prepare_clone(_clone, _musts, _shoulds, _filters)
80
99
  _clone.musts = _musts
81
100
  _clone.shoulds = _shoulds
101
+ _clone.filters = _filters
82
102
  _clone.minimum_should_match = @minimum_should_match
83
103
  _clone.disable_coord = @disable_coord
84
104
  _clone
@@ -1,13 +1,17 @@
1
1
  module Elastic::Nodes::Concerns
2
2
  module Aggregable
3
- def has_aggs?
3
+ def has_aggregations?
4
4
  aggs.count > 0
5
5
  end
6
6
 
7
- def aggs=(_aggs)
7
+ def aggregations=(_aggs)
8
8
  @aggs = _aggs.dup.to_a
9
9
  end
10
10
 
11
+ def aggregations
12
+ @aggs.each
13
+ end
14
+
11
15
  def aggregate(_node)
12
16
  raise ArgumentError, 'node must provide a name' unless _node.name
13
17
  aggs << _node
@@ -20,13 +24,13 @@ module Elastic::Nodes::Concerns
20
24
 
21
25
  def clone
22
26
  node = super
23
- node.aggs = aggs.map(&:clone)
27
+ node.aggregations = aggs.map(&:clone)
24
28
  node
25
29
  end
26
30
 
27
31
  def simplify
28
32
  node = super
29
- node.aggs = aggs.map(&:simplify)
33
+ node.aggregations = aggs.map(&:simplify)
30
34
  node
31
35
  end
32
36
 
@@ -36,15 +40,15 @@ module Elastic::Nodes::Concerns
36
40
  @aggs ||= []
37
41
  end
38
42
 
39
- def render_aggs(_into)
40
- _into['aggs'] = Hash[aggs.map { |a| [a.name, a.render] }] if has_aggs?
43
+ def render_aggs(_into, _options)
44
+ _into['aggs'] = Hash[aggs.map { |a| [a.name.to_s, a.render(_options)] }] if has_aggregations?
41
45
  _into
42
46
  end
43
47
 
44
- def load_aggs_results(_raw)
48
+ def load_aggs_results(_raw, _formatter)
45
49
  {}.tap do |result|
46
50
  aggs.each do |node|
47
- result[node.name] = node.handle_result(_raw[node.name])
51
+ result[node.name] = node.handle_result(_raw[node.name.to_s], _formatter)
48
52
  end
49
53
  end
50
54
  end
@@ -1,14 +1,11 @@
1
1
  module Elastic::Nodes::Concerns
2
2
  module Bucketed
3
- def handle_result(_raw)
3
+ def handle_result(_raw, _formatter)
4
4
  buckets = _raw['buckets'].map do |raw_bucket|
5
- aggs = load_aggs_results(raw_bucket)
5
+ key = _formatter.format_field(field, raw_bucket['key'])
6
+ aggs = load_aggs_results(raw_bucket, _formatter)
6
7
 
7
- # TODO: allow bucket aggregation to return single nested aggregation if node is
8
- # configured that way
9
- # return Elastic::Results::SimpleBucket.new(raw_bucket['key'], aggs.first) if blebliblu
10
-
11
- Elastic::Results::Bucket.new(raw_bucket['key'], aggs)
8
+ Elastic::Results::Bucket.new(key, raw_bucket['doc_count'], aggs)
12
9
  end
13
10
 
14
11
  Elastic::Results::BucketCollection.new buckets
@@ -0,0 +1,10 @@
1
+ module Elastic::Nodes::Concerns
2
+ module FieldQuery
3
+ attr_accessor :field
4
+
5
+ def render_field(_options)
6
+ return "#{_options[:query_path]}.#{@field}" if _options.key? :query_path
7
+ @field.to_s
8
+ end
9
+ end
10
+ end
@@ -24,6 +24,17 @@ module Elastic::Nodes::Concerns
24
24
 
25
25
  private
26
26
 
27
+ def prepare_hits(_hits, _formatter)
28
+ _hits.map do |raw_hit|
29
+ Elastic::Results::Hit.new(
30
+ raw_hit['_type'],
31
+ raw_hit['_id'],
32
+ raw_hit['_score'],
33
+ raw_hit['_source'] ? _formatter.format(raw_hit['_source']) : nil
34
+ )
35
+ end
36
+ end
37
+
27
38
  def copy_hit_options(_clone)
28
39
  _clone.size = @size
29
40
  _clone.source = @source
@@ -54,18 +54,19 @@ module Elastic::Nodes
54
54
  @query.traverse(&_block)
55
55
  end
56
56
 
57
- def render
58
- function_score = { 'query' => @query.render }
59
- function_score['boost_mode'] = @boost_mode.to_s if @boost_mode && @boost_mode != :multiply
57
+ def render(_options = {})
58
+ hash = { 'query' => @query.render(_options) }
59
+ hash['boost_mode'] = @boost_mode.to_s if @boost_mode && @boost_mode != :multiply
60
60
 
61
+ # TODO: add support for the query_path option
61
62
  if @functions.length > 1
62
- function_score['score_mode'] = @score_mode.to_s if @score_mode && @score_mode != :multiply
63
- function_score['functions'] = @functions
63
+ hash['score_mode'] = @score_mode.to_s if @score_mode && @score_mode != :multiply
64
+ hash['functions'] = @functions
64
65
  elsif @functions.length == 1
65
- function_score.merge! @functions.first
66
+ hash.merge! @functions.first
66
67
  end
67
68
 
68
- { 'function_score' => render_boost(function_score) }
69
+ { 'function_score' => render_boost(hash) }
69
70
  end
70
71
 
71
72
  alias :super_clone :clone
@@ -1,10 +1,11 @@
1
1
  module Elastic::Nodes
2
2
  class Match < Base
3
3
  include Concerns::Boostable
4
+ include Concerns::FieldQuery
4
5
 
5
6
  MATCH_MODES = [:boolean, :phrase, :phrase_prefix]
6
7
 
7
- attr_accessor :field, :query
8
+ attr_accessor :query
8
9
  attr_reader :mode
9
10
 
10
11
  def query=(_query)
@@ -26,11 +27,11 @@ module Elastic::Nodes
26
27
  prepare_clone(super)
27
28
  end
28
29
 
29
- def render
30
- query_options = { 'query' => @query }
31
- query_options['type'] = @mode.to_s unless @mode.nil? || @mode == :boolean
30
+ def render(_options = {})
31
+ hash = { 'query' => @query }
32
+ hash['type'] = @mode.to_s unless @mode.nil? || @mode == :boolean
32
33
 
33
- { "match" => { @field.to_s => render_boost(query_options) } }
34
+ { "match" => { render_field(_options) => render_boost(hash) } }
34
35
  end
35
36
 
36
37
  private
@@ -1,5 +1,7 @@
1
1
  module Elastic::Nodes
2
2
  class Nested < Base
3
+ SCORE_MODES = [:avg, :sum, :min, :max, :none]
4
+
3
5
  def self.build(_path, _child)
4
6
  new.tap do |node|
5
7
  node.path = _path
@@ -8,6 +10,7 @@ module Elastic::Nodes
8
10
  end
9
11
 
10
12
  attr_accessor :path, :child
13
+ attr_reader :score_mode
11
14
 
12
15
  def traverse(&_block)
13
16
  super
@@ -18,17 +21,34 @@ module Elastic::Nodes
18
21
  prepare_clone super, @child.clone
19
22
  end
20
23
 
24
+ def score_mode=(_value)
25
+ raise ArgumentError, "invalid score mode #{_value}" if _value && !SCORE_MODES.include?(_value)
26
+ @score_mode = _value
27
+ end
28
+
21
29
  def simplify
22
- prepare_clone super, @child.simplify
30
+ new_child = @child.simplify
31
+ if new_child.is_a? Nested
32
+ prepare_clone(super, new_child.child).tap do |clone|
33
+ clone.path = "#{clone.path}.#{new_child.path}"
34
+ end
35
+ else
36
+ prepare_clone super, new_child
37
+ end
23
38
  end
24
39
 
25
- def render
26
- {
27
- "nested" => {
28
- "path" => @path,
29
- "query" => @child.render
30
- }
40
+ def render(_options = {})
41
+ path = @path
42
+ path = "#{_options[:query_path]}.#{path}" if _options.key? :query_path
43
+
44
+ hash = {
45
+ 'path' => path,
46
+ 'query' => @child.render(_options.merge(query_path: path))
31
47
  }
48
+
49
+ hash['score_mode'] = @score_mode.to_s if @score_mode && @score_mode != :avg
50
+
51
+ { "nested" => hash }
32
52
  end
33
53
 
34
54
  private
@@ -36,6 +56,7 @@ module Elastic::Nodes
36
56
  def prepare_clone(_clone, _child)
37
57
  _clone.path = @path
38
58
  _clone.child = _child
59
+ _clone.score_mode = @score_mode
39
60
  _clone
40
61
  end
41
62
  end
@@ -1,8 +1,9 @@
1
1
  module Elastic::Nodes
2
2
  class Range < Base
3
3
  include Concerns::Boostable
4
+ include Concerns::FieldQuery
4
5
 
5
- attr_accessor :field, :gte, :gt, :lte, :lt
6
+ attr_accessor :gte, :gt, :lte, :lt
6
7
 
7
8
  def clone
8
9
  prepare_clone(super)
@@ -12,14 +13,14 @@ module Elastic::Nodes
12
13
  prepare_clone(super)
13
14
  end
14
15
 
15
- def render
16
- options = {}
17
- options['gte'] = @gte unless @gte.nil?
18
- options['gt'] = @gt unless @gt.nil?
19
- options['lte'] = @lte unless @lte.nil?
20
- options['lt'] = @lt unless @lt.nil?
16
+ def render(_options = {})
17
+ hash = {}
18
+ hash['gte'] = @gte unless @gte.nil?
19
+ hash['gt'] = @gt unless @gt.nil?
20
+ hash['lte'] = @lte unless @lte.nil?
21
+ hash['lt'] = @lt unless @lt.nil?
21
22
 
22
- { "range" => { @field.to_s => render_boost(options) } }
23
+ { "range" => { render_field(_options) => render_boost(hash) } }
23
24
  end
24
25
 
25
26
  private
@@ -14,11 +14,11 @@ module Elastic::Nodes
14
14
  @query.traverse(&_block)
15
15
  end
16
16
 
17
- def render
18
- { "query" => @query.render }.tap do |options|
19
- options["from"] = @offset if offset && offset > 0
20
- render_hit_options(options)
21
- render_aggs(options)
17
+ def render(_options = {})
18
+ { "query" => @query.render(_options) }.tap do |hash|
19
+ hash["from"] = @offset if offset && offset > 0
20
+ render_hit_options(hash)
21
+ render_aggs(hash, _options)
22
22
  end
23
23
  end
24
24
 
@@ -30,11 +30,12 @@ module Elastic::Nodes
30
30
  prepare_clone(super, @query.simplify)
31
31
  end
32
32
 
33
- def handle_result(_raw)
34
- hits = _raw['hits'] ? _raw['hits']['hits'].map { |h| Elastic::Results::Hit.new h } : []
35
- aggs = _raw['aggregations'] ? load_aggs_results(_raw['aggregations']) : {}
36
-
37
- Elastic::Results::Root.new(hits, aggs)
33
+ def handle_result(_raw, _formatter)
34
+ Elastic::Results::Root.new(
35
+ _raw['hits'] ? prepare_hits(_raw['hits']['hits'], _formatter) : [],
36
+ _raw['hits'] ? _raw['hits']['total'] : 0,
37
+ _raw['aggregations'] ? load_aggs_results(_raw['aggregations'], _formatter) : {}
38
+ )
38
39
  end
39
40
 
40
41
  private
@@ -0,0 +1,82 @@
1
+ module Elastic::Nodes
2
+ class Sort < Base
3
+ ORDER = [:asc, :desc]
4
+ MODES = [:min, :max, :sum, :avg, :median]
5
+
6
+ attr_accessor :child
7
+
8
+ def initialize
9
+ @sorts = []
10
+ end
11
+
12
+ def sorts
13
+ @sorts.dup
14
+ end
15
+
16
+ def add_sort(_field, order: :asc, mode: nil, missing: nil)
17
+ raise ArgumentError, "invalid sort order #{order}" unless ORDER.include?(order.to_sym)
18
+ raise ArgumentError, "invalid sort mode #{mode}" if mode && !MODES.include?(mode.to_sym)
19
+
20
+ options = { 'order' => order.to_s }
21
+ options['mode'] = mode.to_s if mode.present?
22
+ options['missing'] = missing if missing.present?
23
+
24
+ @sorts << { _field => options.freeze }.freeze
25
+ self
26
+ end
27
+
28
+ def add_score_sort(order: :desc)
29
+ raise ArgumentError, "invalid sort order #{order}" unless ORDER.include?(order.to_sym)
30
+
31
+ add_sort('_score', order: order)
32
+ end
33
+
34
+ def reset_sorts
35
+ @sorts = []
36
+ self
37
+ end
38
+
39
+ def clone
40
+ prepare_clone(super, child.try(:clone))
41
+ end
42
+
43
+ def simplify
44
+ if @sorts.empty?
45
+ child.try(:simplify)
46
+ else
47
+ prepare_clone(super, child.try(:simplify))
48
+ end
49
+ end
50
+
51
+ def render(_options = {})
52
+ hash = child.render(_options)
53
+ hash['sort'] = render_sorts
54
+ hash
55
+ end
56
+
57
+ def handle_result(_raw, _formatter)
58
+ @child.handle_result(_raw, _formatter)
59
+ end
60
+
61
+ def traverse(&_block)
62
+ super
63
+ @child.traverse(&_block)
64
+ end
65
+
66
+ protected
67
+
68
+ attr_writer :sorts
69
+
70
+ private
71
+
72
+ def prepare_clone(_clone, _child)
73
+ _clone.child = _child
74
+ _clone.sorts = @sorts.dup
75
+ _clone
76
+ end
77
+
78
+ def render_sorts
79
+ @sorts.dup
80
+ end
81
+ end
82
+ end
@@ -1,6 +1,7 @@
1
1
  module Elastic::Nodes
2
2
  class Term < Base
3
3
  include Concerns::Boostable
4
+ include Concerns::FieldQuery
4
5
 
5
6
  BOOLEAN_MODE = [:any, :all]
6
7
 
@@ -30,19 +31,19 @@ module Elastic::Nodes
30
31
  prepare_clone(super)
31
32
  end
32
33
 
33
- def render
34
- raise ArgumentError, 'must provide at least one term' if !@terms || @terms.empty?
34
+ def render(_options = {})
35
+ raise ArgumentError, "terms not provided for #{@field}" if !@terms
35
36
 
36
37
  if @terms.length == 1
37
- { 'term' => { @field.to_s => render_boost('value' => @terms.first) } }
38
- elsif @mode == :all
38
+ { 'term' => { render_field(_options) => render_boost('value' => @terms.first) } }
39
+ elsif @mode == :all && !@terms.empty?
39
40
  {
40
41
  'bool' => render_boost(
41
- 'must' => @terms.map { |t| { 'term' => { @field.to_s => t } } }
42
+ 'must' => @terms.map { |t| { 'term' => { render_field(_options) => t } } }
42
43
  )
43
44
  }
44
45
  else
45
- { 'terms' => render_boost(@field.to_s => @terms) }
46
+ { 'terms' => render_boost(render_field(_options) => @terms) }
46
47
  end
47
48
  end
48
49
 
data/lib/elastic/query.rb CHANGED
@@ -14,10 +14,6 @@ module Elastic
14
14
  @config = _query_config || build_base_config
15
15
  end
16
16
 
17
- def coord_similarity(_enable)
18
- with_clone { |config| config.root.query.disable_coord = !_enable }
19
- end
20
-
21
17
  def limit(_size)
22
18
  with_clone { |config| config.limit = _size }
23
19
  end
@@ -27,6 +23,12 @@ module Elastic
27
23
  with_clone { |config| config.offset = _offset }
28
24
  end
29
25
 
26
+ def sort(*_params)
27
+ with_clone do |config|
28
+ config.sort = Commands::BuildSortFromParams.for(index: index, params: _params)
29
+ end
30
+ end
31
+
30
32
  def segment(*_params)
31
33
  with_clone do |config|
32
34
  config.groups << Commands::BuildAggFromParams.for(index: index, params: _params)
@@ -37,8 +39,8 @@ module Elastic
37
39
  execute assembler.assemble_ids
38
40
  end
39
41
 
40
- def pluck(_field)
41
- execute assembler.assemble_pluck(_field.to_s)
42
+ def pick(_field)
43
+ execute assembler.assemble_pick(_field)
42
44
  end
43
45
 
44
46
  def aggregate(_name = nil, _node = nil, &_block)
@@ -54,6 +56,10 @@ module Elastic
54
56
  @result ||= execute(assembler.assemble)
55
57
  end
56
58
 
59
+ def as_query_node
60
+ @config.query.clone
61
+ end
62
+
57
63
  def as_es_query
58
64
  assembler.assemble.render
59
65
  end
@@ -67,7 +73,7 @@ module Elastic
67
73
  end
68
74
 
69
75
  def with_bool_query(&_block)
70
- with_clone { |config| _block.call(config.root.query) }
76
+ with_clone { |config| _block.call(config.query) }
71
77
  end
72
78
 
73
79
  def with_aggregable_for_metric(&_block)
@@ -79,15 +85,21 @@ module Elastic
79
85
  Core::QueryConfig.initial_config
80
86
  end
81
87
 
82
- def assembler
83
- @assembler ||= Core::QueryAssembler.new(@index, @config)
84
- end
85
-
86
88
  def execute(_query)
87
- _query.handle_result @index.adaptor.query(
89
+ raw = @index.adaptor.query(
88
90
  type: @index.definition.types,
89
91
  query: _query.render
90
92
  )
93
+
94
+ _query.handle_result(raw, formatter)
95
+ end
96
+
97
+ def assembler
98
+ Core::QueryAssembler.new(@index, @config)
99
+ end
100
+
101
+ def formatter
102
+ Core::SourceFormatter.new(@index.definition)
91
103
  end
92
104
 
93
105
  class AggregableAdaptor