elastic-rails 0.8.5 → 0.8.6
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 +4 -4
- data/.travis.yml +6 -3
- data/README.md +10 -0
- data/lib/elastic.rb +4 -1
- data/lib/elastic/core/query_assembler.rb +15 -6
- data/lib/elastic/dsl/metric_builder.rb +24 -7
- data/lib/elastic/dsl/result_composer.rb +15 -0
- data/lib/elastic/nodes/agg/top.rb +42 -0
- data/lib/elastic/nodes/base.rb +1 -1
- data/lib/elastic/nodes/concerns/sortable.rb +54 -0
- data/lib/elastic/nodes/sort.rb +2 -39
- data/lib/elastic/query.rb +10 -17
- data/lib/elastic/shims/multiple_aggregation.rb +16 -0
- data/lib/elastic/shims/{reducing.rb → single_aggregation.rb} +1 -1
- data/lib/elastic/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0035b5db3d49f0a734e2f8a2d77162ac50867cc
|
4
|
+
data.tar.gz: 0223a10c669b8f637b14c50afe88b73ef4714a17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f95a7f7abbaae72da092ec05cbfc2fc5cdec442438d48acbcfab152a716a84c0deb1f3e2e1ae60196a756608d73753203861f8337ccabd9a1353110f720fb19d
|
7
|
+
data.tar.gz: 4f3fe1093d986f8fcda505c37623a5da5398a93bb0a407408c32863ffe33e14d161244617fa0b6247e4dfc1b946695d23931136ab9b9512d437e8db82aa28dfd
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Elastic Rails
|
2
2
|
|
3
|
+
[](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/
|
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 =
|
34
|
-
|
33
|
+
query = assemble_aggregated([_node])
|
34
|
+
single_aggregation_query query
|
35
35
|
end
|
36
36
|
|
37
|
-
def assemble_metrics(
|
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 =
|
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
|
97
|
-
Elastic::Shims::
|
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
|
-
|
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
|
-
|
26
|
-
|
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
|
data/lib/elastic/nodes/base.rb
CHANGED
@@ -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
|
data/lib/elastic/nodes/sort.rb
CHANGED
@@ -1,47 +1,19 @@
|
|
1
1
|
module Elastic::Nodes
|
2
2
|
class Sort < Base
|
3
|
-
|
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
|
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
|
data/lib/elastic/version.rb
CHANGED
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.
|
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-
|
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/
|
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
|