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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/Guardfile +46 -0
  3. data/elastic.gemspec +13 -4
  4. data/lib/elastic/commands/build_agg_from_params.rb +63 -0
  5. data/lib/elastic/commands/build_query_from_params.rb +132 -0
  6. data/lib/elastic/commands/import_index_documents.rb +63 -0
  7. data/lib/elastic/configuration.rb +61 -0
  8. data/lib/elastic/core/adaptor.rb +102 -0
  9. data/lib/elastic/core/base_middleware.rb +43 -0
  10. data/lib/elastic/core/default_middleware.rb +64 -0
  11. data/lib/elastic/core/definition.rb +118 -0
  12. data/lib/elastic/core/mapping_manager.rb +120 -0
  13. data/lib/elastic/core/middleware.rb +26 -0
  14. data/lib/elastic/core/query_assembler.rb +84 -0
  15. data/lib/elastic/core/query_config.rb +25 -0
  16. data/lib/elastic/core/serializer.rb +45 -0
  17. data/lib/elastic/core/source_formatter.rb +40 -0
  18. data/lib/elastic/dsl/bool_query_builder.rb +52 -0
  19. data/lib/elastic/dsl/bool_query_context.rb +42 -0
  20. data/lib/elastic/dsl/metric_builder.rb +34 -0
  21. data/lib/elastic/fields/nested.rb +42 -0
  22. data/lib/elastic/fields/value.rb +60 -0
  23. data/lib/elastic/nested_type.rb +5 -0
  24. data/lib/elastic/nodes/agg/average.rb +7 -0
  25. data/lib/elastic/nodes/agg/base_metric.rb +42 -0
  26. data/lib/elastic/nodes/agg/date_histogram.rb +48 -0
  27. data/lib/elastic/nodes/agg/maximum.rb +7 -0
  28. data/lib/elastic/nodes/agg/minimum.rb +7 -0
  29. data/lib/elastic/nodes/agg/stats.rb +7 -0
  30. data/lib/elastic/nodes/agg/sum.rb +7 -0
  31. data/lib/elastic/nodes/agg/terms.rb +38 -0
  32. data/lib/elastic/nodes/agg/top_hits.rb +17 -0
  33. data/lib/elastic/nodes/and.rb +45 -0
  34. data/lib/elastic/nodes/base.rb +34 -0
  35. data/lib/elastic/nodes/base_agg.rb +32 -0
  36. data/lib/elastic/nodes/boolean.rb +87 -0
  37. data/lib/elastic/nodes/concerns/aggregable.rb +52 -0
  38. data/lib/elastic/nodes/concerns/boostable.rb +25 -0
  39. data/lib/elastic/nodes/concerns/bucketed.rb +17 -0
  40. data/lib/elastic/nodes/concerns/hit_provider.rb +39 -0
  41. data/lib/elastic/nodes/function_score.rb +116 -0
  42. data/lib/elastic/nodes/match.rb +45 -0
  43. data/lib/elastic/nodes/nested.rb +42 -0
  44. data/lib/elastic/nodes/or.rb +9 -0
  45. data/lib/elastic/nodes/range.rb +36 -0
  46. data/lib/elastic/nodes/search.rb +48 -0
  47. data/lib/elastic/nodes/term.rb +58 -0
  48. data/lib/elastic/query.rb +84 -22
  49. data/lib/elastic/railtie.rb +41 -0
  50. data/lib/elastic/railties/ar_helpers.rb +51 -0
  51. data/lib/elastic/railties/ar_middleware.rb +45 -0
  52. data/lib/elastic/{indexable_record.rb → railties/indexable_record.rb} +21 -18
  53. data/lib/elastic/railties/query_extensions.rb +9 -0
  54. data/lib/elastic/railties/rspec.rb +6 -0
  55. data/lib/elastic/railties/tasks/es.rake +19 -0
  56. data/lib/elastic/railties/type_extensions.rb +14 -0
  57. data/lib/elastic/railties/utils.rb +62 -0
  58. data/lib/elastic/results/aggregations.rb +29 -0
  59. data/lib/elastic/results/base.rb +13 -0
  60. data/lib/elastic/results/bucket.rb +15 -0
  61. data/lib/elastic/results/bucket_collection.rb +17 -0
  62. data/lib/elastic/results/grouped_result.rb +37 -0
  63. data/lib/elastic/results/hit.rb +25 -0
  64. data/lib/elastic/results/hit_collection.rb +38 -0
  65. data/lib/elastic/results/metric.rb +13 -0
  66. data/lib/elastic/results/result_group.rb +19 -0
  67. data/lib/elastic/results/root.rb +15 -0
  68. data/lib/elastic/shims/base.rb +23 -0
  69. data/lib/elastic/shims/grouping.rb +23 -0
  70. data/lib/elastic/shims/populating.rb +69 -0
  71. data/lib/elastic/shims/reducing.rb +20 -0
  72. data/lib/elastic/support/command.rb +34 -0
  73. data/lib/elastic/support/transform.rb +37 -0
  74. data/lib/elastic/support/traversable.rb +22 -0
  75. data/lib/elastic/type.rb +56 -84
  76. data/lib/elastic/types/base_type.rb +38 -0
  77. data/lib/elastic/types/faceted_type.rb +16 -0
  78. data/lib/elastic/types/nestable_type.rb +14 -0
  79. data/lib/elastic/version.rb +1 -1
  80. data/lib/elastic.rb +72 -30
  81. data/lib/generators/elastic/index_generator.rb +10 -0
  82. data/lib/generators/elastic/templates/index.rb +17 -0
  83. metadata +222 -16
  84. data/lib/elastic/capabilities/aggregation_builder.rb +0 -64
  85. data/lib/elastic/capabilities/bool_query_builder.rb +0 -74
  86. data/lib/elastic/capabilities/context_handler.rb +0 -31
  87. data/lib/elastic/histogram.rb +0 -49
  88. data/lib/elastic/index.rb +0 -113
  89. data/lib/elastic/indexable.rb +0 -25
  90. 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,9 @@
1
+ module Elastic::Nodes
2
+ class Or < And
3
+ private
4
+
5
+ def operation
6
+ 'or'
7
+ end
8
+ end
9
+ 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
- include Capabilities::ContextHandler
4
- include Capabilities::BoolQueryBuilder
5
- include Capabilities::AggregationBuilder
3
+ extend Forwardable
4
+ include Enumerable
5
+ include Dsl::BoolQueryBuilder
6
+ include Dsl::MetricBuilder
6
7
 
7
- attr_accessor :type, :size, :input_transform
8
+ def_delegators :result, :[], :each, :each_with_score, :count, :first, :last
8
9
 
9
- def initialize(_type, minimum_should_match: 1, size: nil)
10
- @type = _type
11
- @size = size
12
- self.minimum_should_match = minimum_should_match
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 render
16
- search = {}
17
- search['size'] = @size unless @size.nil?
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 run
24
- type.query(render)
57
+ def as_es_query
58
+ assembler.assemble.render
25
59
  end
26
60
 
27
61
  private
28
62
 
29
- def render_query_to(_search)
30
- query = {}
31
- render_bool_query_to query
32
- _search['query'] = query if query.length > 0
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 transform_input(_name, _value)
36
- type.prepare_field_for_query _name, _value
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
- def index_depends(_depends)
29
- @index_depends = _depends
30
- end
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
- def search
41
- # TODO: index_class.query.decorate blabla
42
- end
43
+ def index_later
44
+ # TODO: ActiveJob support
45
+ index_now
43
46
  end
44
47
  end
45
48
  end
@@ -0,0 +1,9 @@
1
+ module Elastic::Railties
2
+ module QueryExtensions
3
+ def includes(*_includes)
4
+ with_clone do |config|
5
+ config.middleware_options[:ar_includes] = _includes
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ RSpec.configure do |config|
2
+ config.before(:example, elasticsearch: true) do
3
+ Elastic.drop
4
+ Elastic.migrate
5
+ end
6
+ 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,13 @@
1
+ module Elastic::Results
2
+ class Base
3
+ include Elastic::Support::Traversable
4
+
5
+ def as_value
6
+ self
7
+ end
8
+
9
+ def traverse(&_block)
10
+ _block.call(self)
11
+ end
12
+ end
13
+ 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