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,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