elastic-rails 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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