elastic-rails 0.1.0 → 0.5.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 +4 -4
- data/Guardfile +46 -0
- data/elastic.gemspec +13 -4
- data/lib/elastic/commands/build_agg_from_params.rb +63 -0
- data/lib/elastic/commands/build_query_from_params.rb +132 -0
- data/lib/elastic/commands/import_index_documents.rb +63 -0
- data/lib/elastic/configuration.rb +61 -0
- data/lib/elastic/core/adaptor.rb +102 -0
- data/lib/elastic/core/base_middleware.rb +43 -0
- data/lib/elastic/core/default_middleware.rb +64 -0
- data/lib/elastic/core/definition.rb +118 -0
- data/lib/elastic/core/mapping_manager.rb +120 -0
- data/lib/elastic/core/middleware.rb +26 -0
- data/lib/elastic/core/query_assembler.rb +84 -0
- data/lib/elastic/core/query_config.rb +25 -0
- data/lib/elastic/core/serializer.rb +45 -0
- data/lib/elastic/core/source_formatter.rb +40 -0
- data/lib/elastic/dsl/bool_query_builder.rb +52 -0
- data/lib/elastic/dsl/bool_query_context.rb +42 -0
- data/lib/elastic/dsl/metric_builder.rb +34 -0
- data/lib/elastic/fields/nested.rb +42 -0
- data/lib/elastic/fields/value.rb +60 -0
- data/lib/elastic/nested_type.rb +5 -0
- data/lib/elastic/nodes/agg/average.rb +7 -0
- data/lib/elastic/nodes/agg/base_metric.rb +42 -0
- data/lib/elastic/nodes/agg/date_histogram.rb +48 -0
- data/lib/elastic/nodes/agg/maximum.rb +7 -0
- data/lib/elastic/nodes/agg/minimum.rb +7 -0
- data/lib/elastic/nodes/agg/stats.rb +7 -0
- data/lib/elastic/nodes/agg/sum.rb +7 -0
- data/lib/elastic/nodes/agg/terms.rb +38 -0
- data/lib/elastic/nodes/agg/top_hits.rb +17 -0
- data/lib/elastic/nodes/and.rb +45 -0
- data/lib/elastic/nodes/base.rb +34 -0
- data/lib/elastic/nodes/base_agg.rb +32 -0
- data/lib/elastic/nodes/boolean.rb +87 -0
- data/lib/elastic/nodes/concerns/aggregable.rb +52 -0
- data/lib/elastic/nodes/concerns/boostable.rb +25 -0
- data/lib/elastic/nodes/concerns/bucketed.rb +17 -0
- data/lib/elastic/nodes/concerns/hit_provider.rb +39 -0
- data/lib/elastic/nodes/function_score.rb +116 -0
- data/lib/elastic/nodes/match.rb +45 -0
- data/lib/elastic/nodes/nested.rb +42 -0
- data/lib/elastic/nodes/or.rb +9 -0
- data/lib/elastic/nodes/range.rb +36 -0
- data/lib/elastic/nodes/search.rb +48 -0
- data/lib/elastic/nodes/term.rb +58 -0
- data/lib/elastic/query.rb +84 -22
- data/lib/elastic/railtie.rb +41 -0
- data/lib/elastic/railties/ar_helpers.rb +51 -0
- data/lib/elastic/railties/ar_middleware.rb +45 -0
- data/lib/elastic/{indexable_record.rb → railties/indexable_record.rb} +21 -18
- data/lib/elastic/railties/query_extensions.rb +9 -0
- data/lib/elastic/railties/rspec.rb +6 -0
- data/lib/elastic/railties/tasks/es.rake +19 -0
- data/lib/elastic/railties/type_extensions.rb +14 -0
- data/lib/elastic/railties/utils.rb +62 -0
- data/lib/elastic/results/aggregations.rb +29 -0
- data/lib/elastic/results/base.rb +13 -0
- data/lib/elastic/results/bucket.rb +15 -0
- data/lib/elastic/results/bucket_collection.rb +17 -0
- data/lib/elastic/results/grouped_result.rb +37 -0
- data/lib/elastic/results/hit.rb +25 -0
- data/lib/elastic/results/hit_collection.rb +38 -0
- data/lib/elastic/results/metric.rb +13 -0
- data/lib/elastic/results/result_group.rb +19 -0
- data/lib/elastic/results/root.rb +15 -0
- data/lib/elastic/shims/base.rb +23 -0
- data/lib/elastic/shims/grouping.rb +23 -0
- data/lib/elastic/shims/populating.rb +69 -0
- data/lib/elastic/shims/reducing.rb +20 -0
- data/lib/elastic/support/command.rb +34 -0
- data/lib/elastic/support/transform.rb +37 -0
- data/lib/elastic/support/traversable.rb +22 -0
- data/lib/elastic/type.rb +56 -84
- data/lib/elastic/types/base_type.rb +38 -0
- data/lib/elastic/types/faceted_type.rb +16 -0
- data/lib/elastic/types/nestable_type.rb +14 -0
- data/lib/elastic/version.rb +1 -1
- data/lib/elastic.rb +72 -30
- data/lib/generators/elastic/index_generator.rb +10 -0
- data/lib/generators/elastic/templates/index.rb +17 -0
- metadata +222 -16
- data/lib/elastic/capabilities/aggregation_builder.rb +0 -64
- data/lib/elastic/capabilities/bool_query_builder.rb +0 -74
- data/lib/elastic/capabilities/context_handler.rb +0 -31
- data/lib/elastic/histogram.rb +0 -49
- data/lib/elastic/index.rb +0 -113
- data/lib/elastic/indexable.rb +0 -25
- data/lib/elastic/value_transform.rb +0 -15
@@ -0,0 +1,42 @@
|
|
1
|
+
module Elastic::Nodes
|
2
|
+
class Nested < Base
|
3
|
+
def self.build(_path, _child)
|
4
|
+
new.tap do |node|
|
5
|
+
node.path = _path
|
6
|
+
node.child = _child
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :path, :child
|
11
|
+
|
12
|
+
def traverse(&_block)
|
13
|
+
super
|
14
|
+
@child.traverse(&_block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def clone
|
18
|
+
prepare_clone super, @child.clone
|
19
|
+
end
|
20
|
+
|
21
|
+
def simplify
|
22
|
+
prepare_clone super, @child.simplify
|
23
|
+
end
|
24
|
+
|
25
|
+
def render
|
26
|
+
{
|
27
|
+
"nested" => {
|
28
|
+
"path" => @path,
|
29
|
+
"query" => @child.render
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def prepare_clone(_clone, _child)
|
37
|
+
_clone.path = @path
|
38
|
+
_clone.child = _child
|
39
|
+
_clone
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Elastic::Nodes
|
2
|
+
class Range < Base
|
3
|
+
include Concerns::Boostable
|
4
|
+
|
5
|
+
attr_accessor :field, :gte, :gt, :lte, :lt
|
6
|
+
|
7
|
+
def clone
|
8
|
+
prepare_clone(super)
|
9
|
+
end
|
10
|
+
|
11
|
+
def simplify
|
12
|
+
prepare_clone(super)
|
13
|
+
end
|
14
|
+
|
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?
|
21
|
+
|
22
|
+
{ "range" => { @field.to_s => render_boost(options) } }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def prepare_clone(_clone)
|
28
|
+
_clone.field = @field
|
29
|
+
_clone.gte = @gte
|
30
|
+
_clone.gt = @gt
|
31
|
+
_clone.lte = @lte
|
32
|
+
_clone.lt = @lt
|
33
|
+
_clone
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Elastic::Nodes
|
2
|
+
class Search < Base
|
3
|
+
include Concerns::Aggregable
|
4
|
+
include Concerns::HitProvider
|
5
|
+
|
6
|
+
attr_accessor :query, :offset
|
7
|
+
|
8
|
+
def self.build(_query)
|
9
|
+
new.tap { |n| n.query = _query }
|
10
|
+
end
|
11
|
+
|
12
|
+
def traverse(&_block)
|
13
|
+
super
|
14
|
+
@query.traverse(&_block)
|
15
|
+
end
|
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)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def clone
|
26
|
+
prepare_clone(super, @query.clone)
|
27
|
+
end
|
28
|
+
|
29
|
+
def simplify
|
30
|
+
prepare_clone(super, @query.simplify)
|
31
|
+
end
|
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)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def prepare_clone(_clone, _query)
|
43
|
+
_clone.query = _query
|
44
|
+
_clone.offset = @offset
|
45
|
+
_clone
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Elastic::Nodes
|
2
|
+
class Term < Base
|
3
|
+
include Concerns::Boostable
|
4
|
+
|
5
|
+
BOOLEAN_MODE = [:any, :all]
|
6
|
+
|
7
|
+
attr_accessor :field, :mode
|
8
|
+
|
9
|
+
def terms=(_terms)
|
10
|
+
@terms = _terms.dup.to_a
|
11
|
+
end
|
12
|
+
|
13
|
+
def mode=(_value)
|
14
|
+
if !_value.nil? && !BOOLEAN_MODE.include?(_value)
|
15
|
+
raise ArgumentError, "invalid mode #{_value}"
|
16
|
+
end
|
17
|
+
|
18
|
+
@mode = _value
|
19
|
+
end
|
20
|
+
|
21
|
+
def terms
|
22
|
+
@terms.each
|
23
|
+
end
|
24
|
+
|
25
|
+
def clone
|
26
|
+
prepare_clone(super)
|
27
|
+
end
|
28
|
+
|
29
|
+
def simplify
|
30
|
+
prepare_clone(super)
|
31
|
+
end
|
32
|
+
|
33
|
+
def render
|
34
|
+
raise ArgumentError, 'must provide at least one term' if !@terms || @terms.empty?
|
35
|
+
|
36
|
+
if @terms.length == 1
|
37
|
+
{ 'term' => { @field.to_s => render_boost('value' => @terms.first) } }
|
38
|
+
elsif @mode == :all
|
39
|
+
{
|
40
|
+
'bool' => render_boost(
|
41
|
+
'must' => @terms.map { |t| { 'term' => { @field.to_s => t } } }
|
42
|
+
)
|
43
|
+
}
|
44
|
+
else
|
45
|
+
{ 'terms' => render_boost(@field.to_s => @terms) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def prepare_clone(_clone)
|
52
|
+
_clone.field = @field
|
53
|
+
_clone.terms = @terms
|
54
|
+
_clone.mode = @mode
|
55
|
+
_clone
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/elastic/query.rb
CHANGED
@@ -1,39 +1,101 @@
|
|
1
1
|
module Elastic
|
2
2
|
class Query
|
3
|
-
|
4
|
-
include
|
5
|
-
include
|
3
|
+
extend Forwardable
|
4
|
+
include Enumerable
|
5
|
+
include Dsl::BoolQueryBuilder
|
6
|
+
include Dsl::MetricBuilder
|
6
7
|
|
7
|
-
|
8
|
+
def_delegators :result, :[], :each, :each_with_score, :count, :first, :last
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
attr_reader :index
|
11
|
+
|
12
|
+
def initialize(_index, _query_config = nil)
|
13
|
+
@index = _index
|
14
|
+
@config = _query_config || build_base_config
|
15
|
+
end
|
16
|
+
|
17
|
+
def coord_similarity(_enable)
|
18
|
+
with_clone { |config| config.root.query.disable_coord = !_enable }
|
19
|
+
end
|
20
|
+
|
21
|
+
def limit(_size)
|
22
|
+
with_clone { |config| config.limit = _size }
|
23
|
+
end
|
24
|
+
alias :size :limit
|
25
|
+
|
26
|
+
def offset(_offset)
|
27
|
+
with_clone { |config| config.offset = _offset }
|
28
|
+
end
|
29
|
+
|
30
|
+
def segment(*_params)
|
31
|
+
with_clone do |config|
|
32
|
+
config.groups << Commands::BuildAggFromParams.for(index: index, params: _params)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def ids(_type = nil)
|
37
|
+
execute assembler.assemble_ids
|
38
|
+
end
|
39
|
+
|
40
|
+
def pluck(_field)
|
41
|
+
execute assembler.assemble_pluck(_field.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
def aggregate(_name = nil, _node = nil, &_block)
|
45
|
+
# TODO
|
46
|
+
end
|
47
|
+
|
48
|
+
def total
|
49
|
+
execute assembler.assemble_total
|
13
50
|
end
|
14
51
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
render_query_to search
|
19
|
-
render_aggregations_to search
|
20
|
-
search
|
52
|
+
def result(_reset = false)
|
53
|
+
@result = nil if _reset
|
54
|
+
@result ||= execute(assembler.assemble)
|
21
55
|
end
|
22
56
|
|
23
|
-
def
|
24
|
-
|
57
|
+
def as_es_query
|
58
|
+
assembler.assemble.render
|
25
59
|
end
|
26
60
|
|
27
61
|
private
|
28
62
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
63
|
+
def with_clone(&_block)
|
64
|
+
new_config = @config.clone
|
65
|
+
_block.call(new_config)
|
66
|
+
self.class.new(@index, new_config)
|
67
|
+
end
|
68
|
+
|
69
|
+
def with_bool_query(&_block)
|
70
|
+
with_clone { |config| _block.call(config.root.query) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def with_aggregable_for_metric(&_block)
|
74
|
+
adaptor = AggregableAdaptor.new.tap(&_block)
|
75
|
+
execute assembler.assemble_metric(adaptor.agg)
|
33
76
|
end
|
34
77
|
|
35
|
-
def
|
36
|
-
|
78
|
+
def build_base_config
|
79
|
+
Core::QueryConfig.initial_config
|
80
|
+
end
|
81
|
+
|
82
|
+
def assembler
|
83
|
+
@assembler ||= Core::QueryAssembler.new(@index, @config)
|
84
|
+
end
|
85
|
+
|
86
|
+
def execute(_query)
|
87
|
+
_query.handle_result @index.adaptor.query(
|
88
|
+
type: @index.definition.types,
|
89
|
+
query: _query.render
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
class AggregableAdaptor
|
94
|
+
attr_accessor :agg
|
95
|
+
|
96
|
+
def aggregate(_node)
|
97
|
+
self.agg = _node
|
98
|
+
end
|
37
99
|
end
|
38
100
|
end
|
39
101
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "elastic/railties/utils"
|
2
|
+
require "elastic/railties/ar_helpers"
|
3
|
+
require "elastic/railties/ar_middleware"
|
4
|
+
require "elastic/railties/type_extensions"
|
5
|
+
require "elastic/railties/query_extensions"
|
6
|
+
require "elastic/railties/indexable_record"
|
7
|
+
|
8
|
+
module Elastic
|
9
|
+
class Railtie < Rails::Railtie
|
10
|
+
initializer "elastic.configure_rails_initialization" do
|
11
|
+
Elastic.configure Rails.application.config_for(:elastic).merge(logger: Rails.logger)
|
12
|
+
|
13
|
+
# Make every activerecord model indexable
|
14
|
+
ActiveRecord::Base.send(:include, Elastic::Railties::IndexableRecord)
|
15
|
+
end
|
16
|
+
|
17
|
+
rake_tasks do
|
18
|
+
load File.expand_path('../railties/tasks/es.rake', __FILE__)
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: configure generators here too
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Expose railties utils at Elastic namespace
|
26
|
+
module Elastic
|
27
|
+
extend Elastic::Railties::Utils
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add activerecord related index helpers
|
31
|
+
class Elastic::Type
|
32
|
+
include Elastic::Railties::TypeExtensions
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add activerecord related query helpers
|
36
|
+
class Elastic::Query
|
37
|
+
include Elastic::Railties::QueryExtensions
|
38
|
+
end
|
39
|
+
|
40
|
+
# Register active record middleware
|
41
|
+
Elastic.register_middleware Elastic::Railties::ARMiddleware
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Elastic::Railties
|
2
|
+
module ARHelpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def find_each_with_options(_collection, includes: nil, scope: nil, &_block)
|
6
|
+
if _collection.respond_to? :find_each
|
7
|
+
_collection = _collection.includes(*includes) if includes
|
8
|
+
_collection = _collection.send(scope) if scope
|
9
|
+
_collection.find_each(&_block)
|
10
|
+
elsif _collection.respond_to? :each
|
11
|
+
ActiveRecord::Associations::Preloader.new.preload(_collection, *includes) if includes
|
12
|
+
_collection.each(&_block)
|
13
|
+
else
|
14
|
+
raise 'Elastic ActiveRecord importing is only supported for collection types'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def infer_ar4_field_options(_klass, _field)
|
19
|
+
# TODO: consider methods occluded by an override:
|
20
|
+
# AR defines methods, this wont work: return nil if _klass.method_defined? _field
|
21
|
+
|
22
|
+
return nil unless _klass.serialized_attributes[_field].nil? # occluded by serializer
|
23
|
+
return nil if _klass.columns_hash[_field].nil?
|
24
|
+
ar_type_to_options _klass.columns_hash[_field].type
|
25
|
+
end
|
26
|
+
|
27
|
+
def infer_ar5_field_options(_klass, _field)
|
28
|
+
# TODO: consider methods occluded by an override:
|
29
|
+
# AR defines methods, this wont work: return nil if _klass.method_defined? _field
|
30
|
+
|
31
|
+
meta = _klass.type_for_attribute _field
|
32
|
+
return nil if meta.to_s == 'ActiveRecord::Type::Serialized' # occluded by serializer
|
33
|
+
ar_type_to_options meta.type
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
39
|
+
def ar_type_to_options(_type)
|
40
|
+
case _type.try(:to_sym)
|
41
|
+
when :text then { type: :string }
|
42
|
+
when :string then { type: :string, index: 'not_analyzed' }
|
43
|
+
when :integer then { type: :long } # not sure..
|
44
|
+
when :float, :decimal then { type: :double } # not sure..
|
45
|
+
when :datetime, :date then { type: :date }
|
46
|
+
when :boolean then { type: :boolean }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Elastic::Railties
|
2
|
+
class ARMiddleware < Elastic::Core::BaseMiddleware
|
3
|
+
def self.accepts?(_target)
|
4
|
+
_target < ::ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
def mode
|
8
|
+
:index # storage mode not supported for AR
|
9
|
+
end
|
10
|
+
|
11
|
+
def field_options_for(_field, _options)
|
12
|
+
if Rails.version.to_f >= 4.2
|
13
|
+
ARHelpers.infer_ar5_field_options(target, _field)
|
14
|
+
else
|
15
|
+
ARHelpers.infer_ar4_field_options(target, _field)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def collect_all(_options, &_block)
|
20
|
+
collect_from(target, _options, &_block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def collect_from(_collection, _options, &_block)
|
24
|
+
ARHelpers.find_each_with_options(
|
25
|
+
_collection,
|
26
|
+
includes: _options[:ar_collect_includes],
|
27
|
+
scope: _options[:ar_collect_scope],
|
28
|
+
&_block
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_by_ids(_ids, _options)
|
33
|
+
results = target.where(id: _ids).order('id ASC')
|
34
|
+
results = results.includes(_options[:ar_includes]) if _options.key? :ar_includes
|
35
|
+
order_results _ids, results
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def order_results(_ordered_ids, _results)
|
41
|
+
hash = _results.each_with_object({}) { |o, h| h[o.id] = o }
|
42
|
+
_ordered_ids.map { |id| hash[id.to_i] }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,15 +1,25 @@
|
|
1
|
-
module Elastic
|
1
|
+
module Elastic::Railties
|
2
2
|
module IndexableRecord
|
3
3
|
def self.included(_base)
|
4
|
-
_base.include Indexable
|
5
4
|
_base.extend ClassMethods
|
6
5
|
end
|
7
6
|
|
8
7
|
module ClassMethods
|
8
|
+
def index_class
|
9
|
+
@index_class ||= to_s + 'Index'
|
10
|
+
end
|
11
|
+
|
12
|
+
def index_class=(_class)
|
13
|
+
@constantized_index_class = nil
|
14
|
+
@index_class = _class
|
15
|
+
end
|
16
|
+
|
17
|
+
def constantized_index_class
|
18
|
+
@constantized_index_class ||= index_class.constantize
|
19
|
+
end
|
20
|
+
|
9
21
|
def index(_options)
|
10
|
-
index_depends(_options.delete(:depends))
|
11
22
|
on = _options.delete(:on)
|
12
|
-
|
13
23
|
if on == :create
|
14
24
|
index_on_create _options
|
15
25
|
elsif on == :save
|
@@ -24,22 +34,15 @@ module Elastic
|
|
24
34
|
def index_on_save(_options = {})
|
25
35
|
after_save(_options) { index_later }
|
26
36
|
end
|
37
|
+
end
|
27
38
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def reindex
|
33
|
-
index_class.clear
|
34
|
-
|
35
|
-
scope = self
|
36
|
-
scope = self.includes(@index_depends) if @index_depends
|
37
|
-
scope.find_each { |r| index_class.store(r) } # TODO: index_many
|
38
|
-
end
|
39
|
+
def index_now
|
40
|
+
self.class.constantized_index_class.index self
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
def index_later
|
44
|
+
# TODO: ActiveJob support
|
45
|
+
index_now
|
43
46
|
end
|
44
47
|
end
|
45
48
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
namespace :es do
|
2
|
+
desc "Elastic: Updates indices mappings"
|
3
|
+
task migrate: :environment do
|
4
|
+
Elastic.configure logger: Logger.new(STDOUT)
|
5
|
+
Elastic.migrate
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Elastic: Rebuilds indices from source data"
|
9
|
+
task reindex: :environment do
|
10
|
+
Elastic.configure logger: Logger.new(STDOUT)
|
11
|
+
Elastic.reindex
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Elastic: Lists indices stats"
|
15
|
+
task stats: :environment do
|
16
|
+
Elastic.configure logger: Logger.new(STDOUT)
|
17
|
+
Elastic.stats
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Elastic::Railties
|
2
|
+
module TypeExtensions
|
3
|
+
def self.included(_klass)
|
4
|
+
_klass.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def references(*_includes)
|
9
|
+
# TODO: check target allows options
|
10
|
+
definition.middleware_options[:ar_collect_includes] = _includes
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Elastic::Railties
|
2
|
+
module Utils
|
3
|
+
def reindex(_index = nil)
|
4
|
+
logger.info "Reindexing all indices" if _index.nil?
|
5
|
+
indices(_index).each do |index|
|
6
|
+
logger.info "Reindexing index #{index.suffix}"
|
7
|
+
handle_errors { index.reindex }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def migrate(_index = nil)
|
12
|
+
logger.info "Migrating all indices" if _index.nil?
|
13
|
+
indices(_index).each do |index|
|
14
|
+
logger.info "Migrating index #{index.suffix}"
|
15
|
+
handle_errors { index.mapping.migrate }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def drop(_index = nil)
|
20
|
+
logger.info "Dropping all indices" if _index.nil?
|
21
|
+
indices(_index).each &:drop
|
22
|
+
end
|
23
|
+
|
24
|
+
def stats(_index = nil)
|
25
|
+
logger.info "Indices stats" if _index.nil?
|
26
|
+
indices(_index).each do |index|
|
27
|
+
logger.info "Stats for #{index.suffix}:"
|
28
|
+
# TODO.
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def indices(_index = nil)
|
35
|
+
Dir.glob(indices_paths.join('**/*.rb')).map do |path|
|
36
|
+
path = Pathname.new path
|
37
|
+
path = path.relative_path_from indices_paths
|
38
|
+
path = path.dirname.join(path.basename(path.extname)).to_s
|
39
|
+
next nil if _index && (path != _index && path.camelize != _index)
|
40
|
+
|
41
|
+
klass = path.camelize.constantize
|
42
|
+
next nil unless klass < Elastic::Type
|
43
|
+
klass
|
44
|
+
end.reject(&:nil?)
|
45
|
+
end
|
46
|
+
|
47
|
+
def indices_paths
|
48
|
+
Rails.root.join(Elastic::Configuration.indices_path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_errors
|
52
|
+
yield
|
53
|
+
rescue => exc
|
54
|
+
logger.error exc.message
|
55
|
+
logger.error exc.backtrace.join("\n")
|
56
|
+
end
|
57
|
+
|
58
|
+
def logger
|
59
|
+
Elastic::Configuration.logger
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Elastic::Results
|
2
|
+
class Aggregations < Base
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :key
|
6
|
+
|
7
|
+
def initialize(_aggs)
|
8
|
+
@aggs = _aggs
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](_key)
|
12
|
+
@aggs[_key.to_s].try(:as_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def each(&_block)
|
16
|
+
@aggs.each(&_block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def as_value
|
20
|
+
# TODO: return aggregation value if configured as single bucket
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def traverse(&_block)
|
25
|
+
super
|
26
|
+
@aggs.each_value { |a| a.traverse(&_block) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Elastic::Results
|
2
|
+
class Bucket < Aggregations
|
3
|
+
attr_reader :key
|
4
|
+
|
5
|
+
def initialize(_key, _aggs)
|
6
|
+
@key = _key
|
7
|
+
super _aggs
|
8
|
+
end
|
9
|
+
|
10
|
+
def as_value
|
11
|
+
# TODO: return aggregation value if configured as single bucket
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|