elastic-rails 0.8.5 → 0.8.6

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: 4479b2e301ba75acb8449d5afb042efaf249511c
4
- data.tar.gz: 6f456debe94dcaa755a3a5e8e45e1de0ed53264a
3
+ metadata.gz: c0035b5db3d49f0a734e2f8a2d77162ac50867cc
4
+ data.tar.gz: 0223a10c669b8f637b14c50afe88b73ef4714a17
5
5
  SHA512:
6
- metadata.gz: c4015950ebd08be302993e16ca68ddc8641e9038fb9198a661d670569642ceae4324a978c9deb8c5cd410443d5fa0b3c6d69dacac3b9c1670fb66f5a4ed68e98
7
- data.tar.gz: b929a60ed947548975264fd69c68935e7cb3dca1a0dea9372aa8986d1f372979a58f27ba28318841308c7d4444a6ea399793f490d6c0829bb3954ed90329be80
6
+ metadata.gz: f95a7f7abbaae72da092ec05cbfc2fc5cdec442438d48acbcfab152a716a84c0deb1f3e2e1ae60196a756608d73753203861f8337ccabd9a1353110f720fb19d
7
+ data.tar.gz: 4f3fe1093d986f8fcda505c37623a5da5398a93bb0a407408c32863ffe33e14d161244617fa0b6247e4dfc1b946695d23931136ab9b9512d437e8db82aa28dfd
data/.travis.yml CHANGED
@@ -1,5 +1,8 @@
1
- sudo: false
2
1
  language: ruby
3
2
  rvm:
4
- - 2.3.1
5
- before_install: gem install bundler -v 1.12.4
3
+ - 2.4.0
4
+ - 2.3.3
5
+ - 2.2.6
6
+ services:
7
+ - elasticsearch
8
+ before_install: gem install bundler -v 1.12.5
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Elastic Rails
2
2
 
3
+ [![Build Status](https://travis-ci.org/platanus/elastic-rails.svg?branch=master)](https://travis-ci.org/platanus/elastic-rails)
4
+
3
5
  Elasticsearch + Ruby on Rails made easy.
4
6
 
5
7
  ## Features
@@ -119,6 +121,14 @@ BikeIndex
119
121
  .average(:price)
120
122
  .each { |keys, price| puts "#{keys[:year]}/#{keys[:category]} => #{price}" }
121
123
 
124
+ # Get average and maximum bike price for bikes newer than 2014
125
+ BikeIndex
126
+ .must(year: { gte: 2014 })
127
+ .compose do |c|
128
+ c.average(:price)
129
+ c.maximum(:price)
130
+ end
131
+
122
132
  # Search bikes ids that have shimano parts:
123
133
  BikeIndex.must(parts: { brand: 'shimano' }).ids
124
134
  ```
data/lib/elastic.rb CHANGED
@@ -45,6 +45,7 @@ require "elastic/nodes/agg/average"
45
45
  require "elastic/nodes/agg/minimum"
46
46
  require "elastic/nodes/agg/maximum"
47
47
  require "elastic/nodes/agg/sum"
48
+ require "elastic/nodes/agg/top"
48
49
  require "elastic/nodes/agg/terms"
49
50
  require "elastic/nodes/agg/date_histogram"
50
51
  require "elastic/nodes/agg/top_hits"
@@ -52,7 +53,8 @@ require "elastic/nodes/agg/top_hits"
52
53
  require "elastic/shims/base"
53
54
  require "elastic/shims/populating"
54
55
  require "elastic/shims/grouping"
55
- require "elastic/shims/reducing"
56
+ require "elastic/shims/single_aggregation"
57
+ require "elastic/shims/multiple_aggregation"
56
58
  require "elastic/shims/total_picking"
57
59
  require "elastic/shims/id_picking"
58
60
  require "elastic/shims/field_picking"
@@ -79,6 +81,7 @@ require "elastic/core/query_assembler"
79
81
  require "elastic/dsl/bool_query_builder"
80
82
  require "elastic/dsl/bool_query_context"
81
83
  require "elastic/dsl/metric_builder"
84
+ require "elastic/dsl/result_composer"
82
85
 
83
86
  require "elastic/types/base_type"
84
87
  require "elastic/types/faceted_type"
@@ -30,11 +30,16 @@ module Elastic::Core
30
30
  end
31
31
 
32
32
  def assemble_metric(_node)
33
- query = assemble_metrics([_node])
34
- reduced_query query
33
+ query = assemble_aggregated([_node])
34
+ single_aggregation_query query
35
35
  end
36
36
 
37
- def assemble_metrics(_aggs)
37
+ def assemble_metrics(_nodes)
38
+ query = assemble_aggregated(_nodes)
39
+ multiple_aggregation_query query
40
+ end
41
+
42
+ def assemble_aggregated(_aggs)
38
43
  query = build_base_query
39
44
  query.size = 0
40
45
 
@@ -64,7 +69,7 @@ module Elastic::Core
64
69
  last.aggregate sort_node Elastic::Nodes::TopHits.build('default')
65
70
 
66
71
  query = grouped_query query
67
- query = reduced_query query
72
+ query = single_aggregation_query query
68
73
  end
69
74
 
70
75
  query
@@ -93,8 +98,12 @@ module Elastic::Core
93
98
  Elastic::Shims::Grouping.new(_query)
94
99
  end
95
100
 
96
- def reduced_query(_query)
97
- Elastic::Shims::Reducing.new(_query)
101
+ def single_aggregation_query(_query)
102
+ Elastic::Shims::SingleAggregation.new(_query)
103
+ end
104
+
105
+ def multiple_aggregation_query(_query)
106
+ Elastic::Shims::MultipleAggregation.new(_query)
98
107
  end
99
108
 
100
109
  def populated_query(_query)
@@ -20,15 +20,32 @@ module Elastic::Dsl
20
20
  aggregate_metric(Elastic::Nodes::Agg::Stats, _field, _options, '%s_stats')
21
21
  end
22
22
 
23
- private
23
+ def opening(_field, _options = {})
24
+ by = _options.delete :by
25
+ raise ArgumentError, 'must provide a sorting column' if by.nil?
26
+
27
+ aggregate_metric(Elastic::Nodes::Agg::Top, _field, _options, 'open_%s') do |node|
28
+ node.add_sort(by, order: :asc)
29
+ end
30
+ end
31
+
32
+ def closing(_field, _options = {})
33
+ by = _options.delete :by
34
+ raise ArgumentError, 'must provide a sorting column' if by.nil?
24
35
 
25
- def aggregate_metric(_klass, _field, _options, _default_name)
26
- with_aggregable_for_metric do |agg|
27
- # TODO: detect nested name and wrap node
28
- name = _options[:as] || sprintf(_default_name, _field)
29
- node = _klass.build(name, _field, missing: _options[:missing])
30
- agg.aggregate node
36
+ aggregate_metric(Elastic::Nodes::Agg::Top, _field, _options, 'close_%s') do |node|
37
+ node.add_sort(by, order: :desc)
31
38
  end
32
39
  end
40
+
41
+ private
42
+
43
+ def aggregate_metric(_klass, _field, _options, _default_name, &_block)
44
+ # TODO: detect nested name and wrap node
45
+ name = _options.delete(:as) || sprintf(_default_name, _field)
46
+ node = _klass.build(name, _field, _options)
47
+ _block.call node unless _block.nil?
48
+ aggregate node
49
+ end
33
50
  end
34
51
  end
@@ -0,0 +1,15 @@
1
+ module Elastic::Dsl
2
+ class ResultComposer
3
+ include MetricBuilder
4
+
5
+ def initialize(_aggs)
6
+ @aggs = _aggs
7
+ end
8
+
9
+ def aggregate(_node)
10
+ raise ArgumentError, 'node must provide a name' unless _node.name
11
+ @aggs << _node
12
+ nil
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ module Elastic::Nodes::Agg
2
+ class Top < Elastic::Nodes::BaseAgg
3
+ include Elastic::Nodes::Concerns::Sortable
4
+
5
+ def self.build(_name, _field, _options = {})
6
+ super(_name).tap do |node|
7
+ node.field = _field
8
+ end
9
+ end
10
+
11
+ attr_accessor :field
12
+
13
+ def clone
14
+ prepare_clone super
15
+ end
16
+
17
+ def simplify
18
+ prepare_clone super
19
+ end
20
+
21
+ def render(_options = {})
22
+ top_hit_config = { '_source' => { 'include' => [@field.to_s] }, 'size' => 1 }
23
+ top_hit_config['sort'] = render_sorts if registered_sorts.count > 0
24
+
25
+ { 'top_hits' => top_hit_config }
26
+ end
27
+
28
+ def handle_result(_raw, _formatter)
29
+ raw_value = _raw['hits'] ? _raw['hits']['hits'].first['_source'][@field.to_s] : nil
30
+
31
+ # TODO: apply formatter to value
32
+ Elastic::Results::Metric.new raw_value
33
+ end
34
+
35
+ private
36
+
37
+ def prepare_clone(_clone)
38
+ _clone.field = @field
39
+ _clone
40
+ end
41
+ end
42
+ end
@@ -33,4 +33,4 @@ require "elastic/nodes/concerns/aggregable"
33
33
  require "elastic/nodes/concerns/bucketed"
34
34
  require "elastic/nodes/concerns/boostable"
35
35
  require "elastic/nodes/concerns/field_query"
36
-
36
+ require "elastic/nodes/concerns/sortable"
@@ -0,0 +1,54 @@
1
+ module Elastic::Nodes::Concerns
2
+ module Sortable
3
+ ORDER = [:asc, :desc]
4
+ MODES = [:min, :max, :sum, :avg, :median]
5
+
6
+ def clone
7
+ copy_sorts super
8
+ end
9
+
10
+ def simplify
11
+ copy_sorts super
12
+ end
13
+
14
+ def sorts
15
+ registered_sorts.dup
16
+ end
17
+
18
+ def add_sort(_field, order: :asc, mode: nil, missing: nil)
19
+ raise ArgumentError, "invalid sort order #{order}" unless ORDER.include?(order.to_sym)
20
+ raise ArgumentError, "invalid sort mode #{mode}" if mode && !MODES.include?(mode.to_sym)
21
+
22
+ options = { 'order' => order.to_s }
23
+ options['mode'] = mode.to_s if mode.present?
24
+ options['missing'] = missing if missing.present?
25
+
26
+ registered_sorts << { _field.to_s => options.freeze }.freeze
27
+ self
28
+ end
29
+
30
+ def reset_sorts
31
+ @registered_sorts = nil
32
+ self
33
+ end
34
+
35
+ protected
36
+
37
+ attr_writer :registered_sorts
38
+
39
+ private
40
+
41
+ def copy_sorts(_clone)
42
+ _clone.registered_sorts = sorts
43
+ _clone
44
+ end
45
+
46
+ def render_sorts
47
+ sorts
48
+ end
49
+
50
+ def registered_sorts
51
+ @registered_sorts ||= []
52
+ end
53
+ end
54
+ end
@@ -1,47 +1,19 @@
1
1
  module Elastic::Nodes
2
2
  class Sort < Base
3
- ORDER = [:asc, :desc]
4
- MODES = [:min, :max, :sum, :avg, :median]
3
+ include Concerns::Sortable
5
4
 
6
5
  attr_accessor :child
7
6
 
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
7
  def add_score_sort(order: :desc)
29
- raise ArgumentError, "invalid sort order #{order}" unless ORDER.include?(order.to_sym)
30
-
31
8
  add_sort('_score', order: order)
32
9
  end
33
10
 
34
- def reset_sorts
35
- @sorts = []
36
- self
37
- end
38
-
39
11
  def clone
40
12
  prepare_clone(super, child.try(:clone))
41
13
  end
42
14
 
43
15
  def simplify
44
- if @sorts.empty?
16
+ if registered_sorts.empty?
45
17
  child.try(:simplify)
46
18
  else
47
19
  prepare_clone(super, child.try(:simplify))
@@ -63,20 +35,11 @@ module Elastic::Nodes
63
35
  @child.traverse(&_block)
64
36
  end
65
37
 
66
- protected
67
-
68
- attr_writer :sorts
69
-
70
38
  private
71
39
 
72
40
  def prepare_clone(_clone, _child)
73
41
  _clone.child = _child
74
- _clone.sorts = @sorts.dup
75
42
  _clone
76
43
  end
77
-
78
- def render_sorts
79
- @sorts.dup
80
- end
81
44
  end
82
45
  end
data/lib/elastic/query.rb CHANGED
@@ -43,10 +43,6 @@ module Elastic
43
43
  execute assembler.assemble_pick(_field)
44
44
  end
45
45
 
46
- def aggregate(_name = nil, _node = nil, &_block)
47
- # TODO
48
- end
49
-
50
46
  def total
51
47
  execute assembler.assemble_total
52
48
  end
@@ -64,6 +60,16 @@ module Elastic
64
60
  assembler.assemble.render
65
61
  end
66
62
 
63
+ def compose(&_block)
64
+ agg_nodes = []
65
+ Dsl::ResultComposer.new(agg_nodes).tap(&_block)
66
+ execute assembler.assemble_metrics agg_nodes
67
+ end
68
+
69
+ def aggregate(_node)
70
+ execute assembler.assemble_metric _node
71
+ end
72
+
67
73
  private
68
74
 
69
75
  def with_clone(&_block)
@@ -76,11 +82,6 @@ module Elastic
76
82
  with_clone { |config| _block.call(config.query) }
77
83
  end
78
84
 
79
- def with_aggregable_for_metric(&_block)
80
- adaptor = AggregableAdaptor.new.tap(&_block)
81
- execute assembler.assemble_metric(adaptor.agg)
82
- end
83
-
84
85
  def build_base_config
85
86
  Core::QueryConfig.initial_config
86
87
  end
@@ -101,13 +102,5 @@ module Elastic
101
102
  def formatter
102
103
  Core::SourceFormatter.new(@index.definition)
103
104
  end
104
-
105
- class AggregableAdaptor
106
- attr_accessor :agg
107
-
108
- def aggregate(_node)
109
- self.agg = _node
110
- end
111
- end
112
105
  end
113
106
  end
@@ -0,0 +1,16 @@
1
+ module Elastic::Shims
2
+ class MultipleAggregation < Base
3
+ def handle_result(_raw, _formatter)
4
+ result = super
5
+
6
+ case result
7
+ when Elastic::Results::Root
8
+ result.aggregations
9
+ when Elastic::Results::GroupedResult
10
+ result
11
+ else
12
+ raise "unable to reduce result of type #{result.class}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  module Elastic::Shims
2
- class Reducing < Base
2
+ class SingleAggregation < Base
3
3
  def handle_result(_raw, _formatter)
4
4
  result = super
5
5
 
@@ -1,3 +1,3 @@
1
1
  module Elastic
2
- VERSION = "0.8.5"
2
+ VERSION = "0.8.6"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.5
4
+ version: 0.8.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignacio Baixas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-17 00:00:00.000000000 Z
11
+ date: 2017-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elasticsearch
@@ -248,6 +248,7 @@ files:
248
248
  - lib/elastic/dsl/bool_query_builder.rb
249
249
  - lib/elastic/dsl/bool_query_context.rb
250
250
  - lib/elastic/dsl/metric_builder.rb
251
+ - lib/elastic/dsl/result_composer.rb
251
252
  - lib/elastic/errors.rb
252
253
  - lib/elastic/fields/nested.rb
253
254
  - lib/elastic/fields/value.rb
@@ -261,6 +262,7 @@ files:
261
262
  - lib/elastic/nodes/agg/stats.rb
262
263
  - lib/elastic/nodes/agg/sum.rb
263
264
  - lib/elastic/nodes/agg/terms.rb
265
+ - lib/elastic/nodes/agg/top.rb
264
266
  - lib/elastic/nodes/agg/top_hits.rb
265
267
  - lib/elastic/nodes/and.rb
266
268
  - lib/elastic/nodes/base.rb
@@ -271,6 +273,7 @@ files:
271
273
  - lib/elastic/nodes/concerns/bucketed.rb
272
274
  - lib/elastic/nodes/concerns/field_query.rb
273
275
  - lib/elastic/nodes/concerns/hit_provider.rb
276
+ - lib/elastic/nodes/concerns/sortable.rb
274
277
  - lib/elastic/nodes/function_score.rb
275
278
  - lib/elastic/nodes/match.rb
276
279
  - lib/elastic/nodes/nested.rb
@@ -309,8 +312,9 @@ files:
309
312
  - lib/elastic/shims/field_picking.rb
310
313
  - lib/elastic/shims/grouping.rb
311
314
  - lib/elastic/shims/id_picking.rb
315
+ - lib/elastic/shims/multiple_aggregation.rb
312
316
  - lib/elastic/shims/populating.rb
313
- - lib/elastic/shims/reducing.rb
317
+ - lib/elastic/shims/single_aggregation.rb
314
318
  - lib/elastic/shims/total_picking.rb
315
319
  - lib/elastic/support/command.rb
316
320
  - lib/elastic/support/transform.rb