elastic-rails 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,118 @@
1
+ module Elastic::Core
2
+ class Definition
3
+ attr_reader :middleware_options
4
+
5
+ def main_target
6
+ targets.first
7
+ end
8
+
9
+ def targets
10
+ @target_cache ||= load_targets.freeze
11
+ end
12
+
13
+ def targets=(_values)
14
+ @target_cache = nil
15
+ @targets = _values
16
+ end
17
+
18
+ def types
19
+ targets.map(&:type_name)
20
+ end
21
+
22
+ def mode
23
+ main_target.mode
24
+ end
25
+
26
+ def initialize
27
+ @targets = []
28
+ @field_map = {}
29
+ @frozen = false
30
+ @middleware_options = HashWithIndifferentAccess.new
31
+ end
32
+
33
+ def register_field(_field)
34
+ raise 'definition has been frozen' if @frozen
35
+ @field_map[_field.name] = _field
36
+ end
37
+
38
+ def fields
39
+ @field_map.each_value
40
+ end
41
+
42
+ def expanded_field_names
43
+ @expanded_field_names ||= @field_map.map { |_, field| field.expanded_names }.flatten
44
+ end
45
+
46
+ def get_field(_name)
47
+ _name = _name.to_s
48
+ separator = _name.index '.'
49
+ if separator.nil?
50
+ @field_map[_name]
51
+ else
52
+ parent = @field_map[_name[0...separator]]
53
+ parent.try(:get_field, _name[separator + 1..-1])
54
+ end
55
+ end
56
+
57
+ def has_field?(_name)
58
+ !get_field(_name).nil?
59
+ end
60
+
61
+ def as_es_mapping
62
+ # TODO: Make this a command
63
+ properties = {}
64
+ @field_map.each_value do |field|
65
+ field_def = field.mapping_options
66
+
67
+ if !field_def.key?(:type) && field.mapping_inference_enabled?
68
+ inferred = infer_mapping_options(field.name)
69
+ field_def.merge! inferred.symbolize_keys unless inferred.nil?
70
+ end
71
+
72
+ if Elastic::Configuration.strict_mode && !field_def.key?(:type)
73
+ raise "explicit field type for #{field.name} required"
74
+ end
75
+
76
+ properties[field.name] = field_def if field_def.key? :type
77
+ end
78
+
79
+ { 'properties' => properties.as_json }
80
+ end
81
+
82
+ def freeze
83
+ unless @frozen
84
+ @field_map.each_value(&:freeze)
85
+ @frozen = true
86
+ @middleware_options.freeze
87
+ end
88
+ end
89
+
90
+ def frozen?
91
+ !!@frozen
92
+ end
93
+
94
+ private
95
+
96
+ def load_targets
97
+ mode = nil
98
+ @targets.map do |target|
99
+ target = target.to_s.camelize.constantize if target.is_a?(Symbol) || target.is_a?(String)
100
+
101
+ target = load_target_middleware(target) unless target.class < BaseMiddleware
102
+ raise 'index target is not indexable' if target.nil?
103
+ raise 'mistmatching indexable mode' if mode && mode != target.mode
104
+ mode = target.mode
105
+
106
+ target
107
+ end
108
+ end
109
+
110
+ def load_target_middleware(_target)
111
+ Middleware.wrap(_target)
112
+ end
113
+
114
+ def infer_mapping_options(_name)
115
+ main_target.field_options_for(_name, middleware_options)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,120 @@
1
+ module Elastic::Core
2
+ class MappingManager
3
+ attr_reader :adaptor, :definition
4
+
5
+ def initialize(_adaptor, _definition)
6
+ @adaptor = _adaptor
7
+ @definition = _definition
8
+ @status = :pending
9
+ end
10
+
11
+ def out_of_sync?
12
+ @status == :out_of_sync
13
+ end
14
+
15
+ def incomplete?
16
+ @status == :incomplete
17
+ end
18
+
19
+ def fetch
20
+ begin
21
+ mappings = @adaptor.get_mappings
22
+ mappings = @definition.types.map { |t| mappings[t] }.reject(&:nil?)
23
+ @index = merge_mappings_into_index mappings
24
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
25
+ # ignore not-found errors when fetching mappings
26
+ @index = nil
27
+ end
28
+
29
+ @status = compute_status
30
+ self
31
+ end
32
+
33
+ def unmapped_fields
34
+ @definition.expanded_field_names.reject { |f| has_field? f }
35
+ end
36
+
37
+ def has_field?(_name)
38
+ @index.key? _name
39
+ end
40
+
41
+ def get_field_options(_name)
42
+ @index[_name]
43
+ end
44
+
45
+ def migrate
46
+ # TODO: make this a command
47
+ @adaptor.create unless @adaptor.exists?
48
+ begin
49
+ @definition.types.each { |t| @adaptor.set_mapping(t, user_mapping) }
50
+ rescue Elasticsearch::Transport::Transport::Errors::BadRequest
51
+ # TODO: https://www.elastic.co/guide/en/elasticsearch/guide/current/reindex.html
52
+ end
53
+ fetch
54
+ end
55
+
56
+ private
57
+
58
+ def compute_status
59
+ if !synchronized?
60
+ :out_of_sync
61
+ elsif unmapped_fields.count > 0
62
+ :incomplete
63
+ else
64
+ :ready
65
+ end
66
+ end
67
+
68
+ def synchronized?
69
+ return false if @index.nil?
70
+ flatten(user_mapping).all? do |field, properties|
71
+ compare_field_properties(@index[field], properties)
72
+ end
73
+ end
74
+
75
+ def compare_field_properties(_current, _user)
76
+ return false if _current.nil?
77
+
78
+ case _current['type']
79
+ when 'date'
80
+ _current = { 'format' => 'dateOptionalTime' }.merge(_user)
81
+ else
82
+ _current == _user
83
+ end
84
+ end
85
+
86
+ def user_mapping
87
+ @user_mapping ||= definition.as_es_mapping
88
+ end
89
+
90
+ def flatten(_raw, _prefix = '')
91
+ _raw['properties'].flat_map do |name, raw_field|
92
+ if raw_field['type'] == 'nested'
93
+ childs = flatten(raw_field, name + '.')
94
+ childs << [
95
+ _prefix + name,
96
+ raw_field.slice(*(raw_field.keys - ['properties']))
97
+ ]
98
+ else
99
+ [[_prefix + name, raw_field.dup]]
100
+ end
101
+ end
102
+ end
103
+
104
+ def merge_mappings_into_index(_mappings)
105
+ {}.tap do |result|
106
+ _mappings.each do |mapping|
107
+ index = flatten(mapping)
108
+ index.each do |field, properties|
109
+ if result.key? field
110
+ result[field].merge! properties
111
+ else
112
+ result[field] = properties
113
+ end
114
+ end
115
+ end
116
+ result.each_value(&:freeze)
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,26 @@
1
+ module Elastic::Core
2
+ module Middleware
3
+ extend self
4
+
5
+ def register(_middleware_class)
6
+ middlewares << _middleware_class
7
+ end
8
+
9
+ def wrap(_target)
10
+ middleware_for(_target).new _target
11
+ end
12
+
13
+ def middleware_for(_target)
14
+ # TODO: improve matching logic
15
+ middleware = middlewares.reverse_each.find { |m| m.accepts?(_target) }
16
+ middleware = DefaultMiddleware if middleware.nil?
17
+ middleware
18
+ end
19
+
20
+ private
21
+
22
+ def middlewares
23
+ @middlewares ||= []
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,84 @@
1
+ module Elastic::Core
2
+ class QueryAssembler
3
+ def initialize(_index, _config)
4
+ @index = _index
5
+ @config = _config
6
+ end
7
+
8
+ def assemble
9
+ query = build_base_query
10
+
11
+ if !grouped?
12
+ query.size = (@config.limit || Elastic::Configuration.page_size)
13
+ query.offset = @config.offset
14
+ else
15
+ query.size = 0
16
+ last = attach_groups query
17
+ last.aggregate(Elastic::Nodes::TopHits.build('default'))
18
+
19
+ query = grouped_query query
20
+ query = reduced_query query
21
+ end
22
+
23
+ populated_query query
24
+ end
25
+
26
+ def assemble_ids
27
+ raise NotImplementedError, 'ids retrieval not yet implemented'
28
+ end
29
+
30
+ def assemble_total
31
+ raise NotImplementedError, 'total not yet implemented'
32
+ end
33
+
34
+ def assemble_pluck(_field)
35
+ raise NotImplementedError, 'pluck not yet implemented'
36
+ end
37
+
38
+ def assemble_metric(_node)
39
+ query = assemble_metrics([_node])
40
+ reduced_query query
41
+ end
42
+
43
+ def assemble_metrics(_aggs)
44
+ query = build_base_query
45
+ query.size = 0
46
+
47
+ last = attach_groups(query)
48
+ last.aggs = _aggs
49
+
50
+ query = grouped_query(query) if grouped?
51
+ query
52
+ end
53
+
54
+ private
55
+
56
+ def build_base_query
57
+ @config.root.simplify
58
+ end
59
+
60
+ def grouped?
61
+ !@config.groups.empty?
62
+ end
63
+
64
+ def attach_groups(_query)
65
+ @config.groups.inject(_query) do |last, node|
66
+ node = node.simplify
67
+ last.aggregate node
68
+ node
69
+ end
70
+ end
71
+
72
+ def grouped_query(_query)
73
+ Elastic::Shims::Grouping.new(_query)
74
+ end
75
+
76
+ def reduced_query(_query)
77
+ Elastic::Shims::Reducing.new(_query)
78
+ end
79
+
80
+ def populated_query(_query)
81
+ Elastic::Shims::Populating.new(@index, @config, _query)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,25 @@
1
+ module Elastic::Core
2
+ class QueryConfig
3
+ attr_accessor :root, :groups, :limit, :offset, :middleware_options
4
+
5
+ def self.initial_config
6
+ new.tap do |config|
7
+ config.root = Elastic::Nodes::Search.new
8
+ config.root.query = Elastic::Nodes::Boolean.new
9
+ config.root.query.disable_coord = true unless Elastic::Configuration.coord_similarity
10
+ config.groups = []
11
+ config.middleware_options = HashWithIndifferentAccess.new
12
+ end
13
+ end
14
+
15
+ def clone
16
+ self.class.new.tap do |clone|
17
+ clone.root = @root.clone
18
+ clone.groups = @groups.dup
19
+ clone.limit = @limit
20
+ clone.offset = @offset
21
+ clone.middleware_options = @middleware_options.dup
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,45 @@
1
+ module Elastic::Core
2
+ class Serializer
3
+ attr_reader :object
4
+
5
+ def self.original_value_occluded?(_field)
6
+ public_method_defined? _field
7
+ end
8
+
9
+ def initialize(_definition, _object)
10
+ # TODO: validate that object is of type <target>?
11
+ @definition = _definition
12
+ @object = _object
13
+ end
14
+
15
+ def fields
16
+ @definition.fields
17
+ end
18
+
19
+ def as_es_document(only_data: false)
20
+ data = {}.tap do |hash|
21
+ fields.each do |field|
22
+ value = read_attribute_for_indexing(field.name)
23
+ value = field.prepare_value_for_index(value)
24
+ hash[field.name] = value
25
+ end
26
+ end
27
+
28
+ return data if only_data
29
+
30
+ result = { '_type' => object.class.to_s, 'data' => data }
31
+ result['_id'] = read_attribute_for_indexing(:id) if has_attribute_for_indexing?(:id)
32
+ result
33
+ end
34
+
35
+ private
36
+
37
+ def has_attribute_for_indexing?(_name)
38
+ respond_to?(_name) || @object.respond_to?(_name)
39
+ end
40
+
41
+ def read_attribute_for_indexing(_name)
42
+ respond_to?(_name) ? public_send(_name) : @object.public_send(_name)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ module Elastic::Core
2
+ class SourceFormatter
3
+ def initialize(_mapping)
4
+ @mapping = _mapping
5
+ @treatment_cache = {}
6
+ end
7
+
8
+ def format(_source, _prefix = nil)
9
+ _source.each do |field, value|
10
+ field_name = _prefix ? "#{_prefix}.#{field}" : field
11
+
12
+ treatment = treatment_for field_name
13
+ if treatment == :nested
14
+ value.each { |v| format(v, field_name) }
15
+ else
16
+ _source[field] = send(treatment, value) unless treatment == :none
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def treatment_for(_field)
24
+ treatment = @treatment_cache[_field]
25
+ treatment = @treatment_cache[_field] = get_treatment(_field) if treatment.nil?
26
+ treatment
27
+ end
28
+
29
+ def get_treatment(_field)
30
+ field_options = @mapping.get_field_options(_field) || {}
31
+ return :nested if field_options['type'] == 'nested'
32
+ return :parse_date if field_options['type'] == 'date'
33
+ :none
34
+ end
35
+
36
+ def parse_date(_value)
37
+ Time.parse _value
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ module Elastic::Dsl
2
+ module BoolQueryBuilder
3
+ def must(*_queries)
4
+ with_bool_query do |query|
5
+ query.must build_query_from_params(_queries)
6
+ end
7
+ end
8
+
9
+ def should(*_queries)
10
+ with_bool_query do |query|
11
+ query.should build_query_from_params(_queries)
12
+ end
13
+ end
14
+
15
+ def with(_modifier, &_block)
16
+ raise ArgumentError, 'block missing' if _block.nil?
17
+ raise ArgumentError, 'node is not a modifier' unless _modifier.respond_to? :clone_with_query
18
+
19
+ with_bool_query do |query|
20
+ ctx = BoolQueryContext.new index, query, _modifier
21
+ ctx.instance_exec(&_block)
22
+ ctx
23
+ end
24
+ end
25
+
26
+ def boost(_amount = nil, field: nil, fixed: false, factor: 1, modifier: :none, missing: 1,
27
+ &_block)
28
+ raise ArgumentError, 'must provide at least a boost amount' if _amount.nil? && field.nil?
29
+
30
+ node = Elastic::Nodes::FunctionScore.new
31
+ node.boost_mode = :replace if fixed
32
+
33
+ if !field.nil?
34
+ node.add_field_function(field, factor: factor, modifier: modifier, missing: missing)
35
+ elsif fixed
36
+ node.add_weight_function(_amount)
37
+ else
38
+ node.boost = _amount
39
+ end
40
+
41
+ # TODO: add decay function support.
42
+
43
+ with(node, &_block)
44
+ end
45
+
46
+ private
47
+
48
+ def build_query_from_params(_params)
49
+ Elastic::Commands::BuildQueryFromParams.for(index: index, params: _params)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ module Elastic::Dsl
2
+ class BoolQueryContext
3
+ include BoolQueryBuilder
4
+
5
+ attr_reader :index
6
+
7
+ def initialize(_index, _query, _modifier)
8
+ @index = _index
9
+ @wrapper = BoolQueryWrapper.new(_query, _modifier)
10
+ end
11
+
12
+ private
13
+
14
+ def with_bool_query
15
+ yield @wrapper
16
+ self
17
+ end
18
+
19
+ class BoolQueryWrapper
20
+ def initialize(_query, _modifier)
21
+ @query = _query
22
+ @modifier = _modifier
23
+ end
24
+
25
+ def must(_node)
26
+ @query.must wrap(_node)
27
+ self
28
+ end
29
+
30
+ def should(_node)
31
+ @query.should wrap(_node)
32
+ self
33
+ end
34
+
35
+ private
36
+
37
+ def wrap(_query)
38
+ @modifier.clone_with_query _query
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ module Elastic::Dsl
2
+ module MetricBuilder
3
+ def average(_field, _options = {})
4
+ aggregate_metric(Elastic::Nodes::Agg::Average, _field, _options, 'avg_%s')
5
+ end
6
+
7
+ def sum(_field, _options = {})
8
+ aggregate_metric(Elastic::Nodes::Agg::Sum, _field, _options, 'sum_%s')
9
+ end
10
+
11
+ def minimum(_field, _options = {})
12
+ aggregate_metric(Elastic::Nodes::Agg::Minimum, _field, _options, 'min_%s')
13
+ end
14
+
15
+ def maximum(_field, _options = {})
16
+ aggregate_metric(Elastic::Nodes::Agg::Maximum, _field, _options, 'max_%s')
17
+ end
18
+
19
+ def stats(_field, _options = {})
20
+ aggregate_metric(Elastic::Nodes::Agg::Stats, _field, _options, '%s_stats')
21
+ end
22
+
23
+ private
24
+
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
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ module Elastic::Fields
2
+ class Nested
3
+ attr_reader :name, :index
4
+
5
+ def initialize(_name, _index)
6
+ @name = _name.to_s
7
+ @index = _index
8
+ end
9
+
10
+ def expanded_names
11
+ [@name] + @index.definition.expanded_field_names.map { |n| @name + '.' + n }
12
+ end
13
+
14
+ def mapping_inference_enabled?
15
+ false
16
+ end
17
+
18
+ def disable_mapping_inference
19
+ end
20
+
21
+ def freeze
22
+ @index.freeze_index_definition
23
+ super
24
+ end
25
+
26
+ def mapping_options
27
+ @index.definition.as_es_mapping.merge!(type: :nested)
28
+ end
29
+
30
+ def get_field(_name)
31
+ @index.definition.get_field _name
32
+ end
33
+
34
+ def prepare_value_for_query(_value)
35
+ _value
36
+ end
37
+
38
+ def prepare_value_for_index(_values)
39
+ _values.map { |v| @index.new(v).as_es_document(only_data: true) }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ module Elastic::Fields
2
+ class Value
3
+ MAPPING_OPTIONS = [
4
+ :type, :analyzer, :boost, :coerce, :copy_to, :doc_values, :dynamic,
5
+ :enabled, :fielddata, :geohash, :geohash_precision, :geohash_prefix, :format, :ignore_above,
6
+ :ignore_malformed, :include_in_all, :index_options, :lat_lon, :index, :fields, :norms,
7
+ :null_value, :position_increment_gap, :properties, :search_analyzer, :similarity, :store,
8
+ :term_vector
9
+ ]
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(_name, _options)
14
+ @name = _name.to_s
15
+ @options = _options
16
+ @mapping_inference = true
17
+ @transform = Elastic::Support::Transform.new @options[:transform]
18
+ end
19
+
20
+ def expanded_names
21
+ [@name]
22
+ end
23
+
24
+ def mapping_inference_enabled?
25
+ @mapping_inference && !@options.key?(:transform)
26
+ end
27
+
28
+ def disable_mapping_inference
29
+ @mapping_inference = false
30
+ end
31
+
32
+ def mapping_options
33
+ process_special_types @options.symbolize_keys.slice(*MAPPING_OPTIONS)
34
+ end
35
+
36
+ def has_field?(_name)
37
+ false
38
+ end
39
+
40
+ def prepare_value_for_query(_value)
41
+ prepare_value_for_index(_value)
42
+ end
43
+
44
+ def prepare_value_for_index(_value)
45
+ @transform.apply _value
46
+ end
47
+
48
+ private
49
+
50
+ def process_special_types(_definition)
51
+ case _definition[:type].try(:to_sym)
52
+ when :term
53
+ _definition[:type] = 'string'
54
+ _definition[:index] = 'not_analyzed'
55
+ end
56
+
57
+ _definition
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ module Elastic
2
+ class NestedType < Types::BaseType
3
+ extend Types::FacetedType
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Elastic::Nodes::Agg
2
+ class Average < BaseMetric
3
+ private def metric
4
+ 'avg'
5
+ end
6
+ end
7
+ end