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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b7fcb53e08a79f313f4d3a4209f600ca621efd4
4
- data.tar.gz: 082420e99a70e5d5ddd8b037e0f27eeec77b1363
3
+ metadata.gz: a5c4d94d6d4b35572694f0968dfcb60251cd531a
4
+ data.tar.gz: 8fc9e4861325fb3e7ddf90d5751b7dd69892a0ac
5
5
  SHA512:
6
- metadata.gz: e2f2356d768d8ebf6b97aca4d6cc42929bc2c57716df72a03ddc416444aec1ec46a4a7bc60522d08a7ae581f906f4a402c1a5129d66f61472f2d8732df8db67f
7
- data.tar.gz: 25a21fe7f5d976aae28e26b0e98b173ed38add01102b38793ff1c31b7b2786b5ecd7fde2ab57e7d6d471e42a47bf0ec9c0ea1caf0ced1c48d7a5c67753e6a7cc
6
+ metadata.gz: 7084256b0dca89eecfdb1a0b2b2c8784e52b155da96fc60b9419913314fe7fb06c01525669a10414cb807e0ec75d6538d8d0c7f344d14eb6fdde16abe3f8442c
7
+ data.tar.gz: 7f38c177624c2df6216e821701507a91000bd95980226bdad50bf1fab18d6146f1041bd4ffc1845c6824bfd819a8d6b31cba12b789abfd05d875ef13efe7418b
data/Guardfile ADDED
@@ -0,0 +1,46 @@
1
+ watch(%r{^spec/.+_spec\.rb$})
2
+ # A sample Guardfile
3
+ # More info at https://github.com/guard/guard#readme
4
+
5
+ ## Uncomment and set this to only include directories you want to watch
6
+ # directories %w(app lib config test spec feature)
7
+
8
+ ## Uncomment to clear the screen before every task
9
+ # clearing :on
10
+
11
+ ## Make Guard exit when config is changed so it can be restarted
12
+ #
13
+ ## Note: if you want Guard to automatically start up again, run guard in a
14
+ ## shell loop, e.g.:
15
+ #
16
+ # $ while bundle exec guard; do echo "Restarting Guard..."; done
17
+ #
18
+ ## Note: if you are using the `directories` clause above and you are not
19
+ ## watching the project directory ('.'), the you will want to move the Guardfile
20
+ ## to a watched dir and symlink it back, e.g.
21
+ #
22
+ # $ mkdir config
23
+ # $ mv Guardfile config/
24
+ # $ ln -s config/Guardfile .
25
+ #
26
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
27
+ #
28
+ watch ("Guardfile") do
29
+ UI.info "Exiting because Guard must be restarted for changes to take effect"
30
+ exit 0
31
+ end
32
+
33
+ # Note: The cmd option is now required due to the increasing number of ways
34
+ # rspec may be run, below are examples of the most common uses.
35
+ # * bundler: 'bundle exec rspec'
36
+ # * bundler binstubs: 'bin/rspec'
37
+ # * spring: 'bin/rspec' (This will use spring if running and you have
38
+ # installed the spring binstubs per the docs)
39
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
40
+ # * 'just' rspec: 'rspec'
41
+
42
+ guard :rspec, cmd: 'bundle exec rspec' do
43
+ watch(%r{^spec/.+_spec\.rb$})
44
+ watch(%r{^lib/elastic/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
45
+ watch('spec/spec_helper.rb') { "spec" }
46
+ end
data/elastic.gemspec CHANGED
@@ -19,9 +19,18 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "elasticsearch", "~> 1.0"
22
+ spec.add_dependency "elasticsearch", "~> 1.0", ">= 1.0.18"
23
+ spec.add_dependency "activesupport"
23
24
 
24
- spec.add_development_dependency "bundler", "~> 1.12"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
- spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "rake", "~> 10.4"
27
+ spec.add_development_dependency "rspec", "~> 3.1"
28
+ spec.add_development_dependency "rspec-nc", "~> 0.2"
29
+ spec.add_development_dependency "guard", "~> 2.11"
30
+ spec.add_development_dependency "guard-rspec", "~> 4.5"
31
+ spec.add_development_dependency "terminal-notifier-guard", "~> 1.6", ">= 1.6.1"
32
+ spec.add_development_dependency "pry", "~> 0.10"
33
+ spec.add_development_dependency "pry-remote", "~> 0.1"
34
+ spec.add_development_dependency "pry-byebug", "~> 3.2"
35
+ spec.add_development_dependency "pry-nav", "~> 0.2"
27
36
  end
@@ -0,0 +1,63 @@
1
+ module Elastic::Commands
2
+ class BuildAggFromParams < Elastic::Support::Command.new(:index, :params)
3
+ def perform
4
+ parse_params
5
+ raise ArgumentError, "field not mapped: #{@field}" unless index.definition.has_field? @field
6
+
7
+ path = parse_nesting_path
8
+ raise NotImplementedError, "nested paths not yet supported in aggregations" if path
9
+
10
+ build_node
11
+ end
12
+
13
+ private
14
+
15
+ def agg_name
16
+ @options.fetch(:as, @field)
17
+ end
18
+
19
+ def parse_params
20
+ @field = params[0].to_s
21
+ @options = params[1] || {}
22
+ end
23
+
24
+ def parse_nesting_path
25
+ dot_index = @field.rindex('.')
26
+ return nil if dot_index.nil?
27
+ @field.slice(0, dot_index)
28
+ end
29
+
30
+ def build_node
31
+ type = @options[:type]
32
+ type = infer_type_from_options_and_mapping if type.nil?
33
+
34
+ send("build_#{type}")
35
+ end
36
+
37
+ def infer_type_from_options_and_mapping
38
+ return :range if @options.key? :ranges
39
+
40
+ properties = index.mapping.get_field_options @field
41
+ return :date_histogram if properties['type'] == 'date'
42
+ return :histogram if @options.key? :interval
43
+
44
+ :terms
45
+ end
46
+
47
+ def build_range
48
+ raise NotImplementedError, 'range aggregation not yet implemented'
49
+ end
50
+
51
+ def build_histogram
52
+ raise NotImplementedError, 'histogram aggregation not yet implemented'
53
+ end
54
+
55
+ def build_date_histogram
56
+ Elastic::Nodes::Agg::DateHistogram.build(agg_name, @field, interval: @options[:interval])
57
+ end
58
+
59
+ def build_terms
60
+ Elastic::Nodes::Agg::Terms.build(agg_name, @field, size: @options[:size])
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,132 @@
1
+ module Elastic::Commands
2
+ class BuildQueryFromParams < Elastic::Support::Command.new(
3
+ :index, :params, block: nil, prefix: nil
4
+ )
5
+
6
+ def perform
7
+ if block
8
+ # TODO: builder mode, support nesting through first parameter
9
+ else
10
+ node = Elastic::Nodes::Boolean.build_or(params.map do |part|
11
+ Elastic::Nodes::Boolean.build_and(part.map do |field, options|
12
+ field = field.to_s
13
+ path = get_nesting_path field
14
+ query_node = build_query_node(field, options)
15
+ path.nil? ? query_node : Elastic::Nodes::Nested.build(with_prefix(path), query_node)
16
+ end)
17
+ end)
18
+ end
19
+
20
+ node.simplify
21
+ end
22
+
23
+ private
24
+
25
+ def get_nesting_path(_field)
26
+ dot_index = _field.rindex('.')
27
+ return nil if dot_index.nil?
28
+ _field.slice 0, dot_index
29
+ end
30
+
31
+ def build_query_node(_field, _options)
32
+ _field = with_prefix _field
33
+ _options = prepare_options _field, _options
34
+
35
+ type = infer_type_from_params(_options)
36
+ send("build_#{type}", _field, _options)
37
+ end
38
+
39
+ def infer_type_from_params(_query)
40
+ return :term if _query.key? :term
41
+ return :term if _query.key? :terms
42
+ return :match if _query.key? :matches
43
+ return :range if _query.key? :gte
44
+ return :range if _query.key? :gt
45
+ return :range if _query.key? :lte
46
+ return :range if _query.key? :lt
47
+ return :nested if _query.key? :nested
48
+ nil
49
+ end
50
+
51
+ def with_prefix(_path)
52
+ return _path if prefix.nil?
53
+ "#{prefix}.#{_path}"
54
+ end
55
+
56
+ def prepare_options(_field, _options)
57
+ properties = index.mapping.get_field_options _field.to_s
58
+ raise ArgumentError, "field not mapped: #{_field}" if properties.nil?
59
+
60
+ case properties['type']
61
+ when 'nested'
62
+ prepare_nested_options _options, properties
63
+ when 'string'
64
+ prepare_string_options _options, properties
65
+ else
66
+ prepare_default_options _options, properties
67
+ end
68
+ end
69
+
70
+ def prepare_nested_options(_options, _properties)
71
+ return _options if _options.is_a?(Hash) && _options[:nested]
72
+ { nested: _options }
73
+ end
74
+
75
+ def prepare_string_options(_options, _properties)
76
+ return _options if _options.is_a? Hash
77
+ _properties['index'] == 'not_analyzed' ? { term: _options } : { matches: _options }
78
+ end
79
+
80
+ def prepare_default_options(_options, _properties)
81
+ return _options if _options.is_a? Hash
82
+ return range_to_options(_options) if _options.is_a? Range
83
+ { term: _options }
84
+ end
85
+
86
+ def build_nested(_field, _options)
87
+ query = _options[:nested]
88
+ query = [query] unless query.is_a? Array
89
+
90
+ nested_query = BuildQueryFromParams.for(index: index, params: query, prefix: _field)
91
+ Elastic::Nodes::Nested.build _field, nested_query
92
+ end
93
+
94
+ def build_term(_field, _options)
95
+ terms = Array(_options[:term] || _options[:terms])
96
+
97
+ Elastic::Nodes::Term.new.tap do |node|
98
+ node.field = _field
99
+ node.mode = _options[:mode]
100
+ node.terms = terms.map { |t| prep(_field, t) }
101
+ end
102
+ end
103
+
104
+ def build_range(_field, _options)
105
+ Elastic::Nodes::Range.new.tap do |node|
106
+ node.field = _field
107
+ node.gte = prep(_field, _options[:gte]) if _options.key? :gte
108
+ node.gt = prep(_field, _options[:gt]) if _options.key? :gt
109
+ node.lte = prep(_field, _options[:lte]) if _options.key? :lte
110
+ node.lt = prep(_field, _options[:lt]) if _options.key? :lt
111
+ end
112
+ end
113
+
114
+ def build_match(_field, _options)
115
+ Elastic::Nodes::Match.new.tap do |node|
116
+ node.field = _field
117
+ node.query = prep(_field, _options[:matches])
118
+ end
119
+ end
120
+
121
+ def range_to_options(_range)
122
+ {
123
+ gte: _range.begin,
124
+ (_range.exclude_end? ? :lt : :lte) => _range.end
125
+ }
126
+ end
127
+
128
+ def prep(_field, _value)
129
+ index.definition.get_field(_field).prepare_value_for_query(_value)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,63 @@
1
+ module Elastic::Commands
2
+ class ImportIndexDocuments < Elastic::Support::Command.new(
3
+ :index, collection: nil, cache_size: 10000, verbose: false
4
+ )
5
+ def perform
6
+ if collection.present?
7
+ import_collection
8
+ else
9
+ targets.each { |target| import_target(target) }
10
+ end
11
+ flush
12
+ end
13
+
14
+ private
15
+
16
+ def import_collection
17
+ main_target.collect_for(collection, middleware_options) { |obj| queue obj }
18
+ end
19
+
20
+ def import_target(_target)
21
+ _target.collect_all(middleware_options) { |obj| queue obj }
22
+ end
23
+
24
+ def cache
25
+ @cache ||= []
26
+ end
27
+
28
+ def queue(_object)
29
+ cache << render_for_es(_object)
30
+ flush if cache.length >= cache_size
31
+ end
32
+
33
+ def flush
34
+ unless cache.empty?
35
+ index.adaptor.bulk_index(cache)
36
+ log_flush(cache.size) if verbose
37
+ cache.clear
38
+ end
39
+ end
40
+
41
+ def log_flush(_size)
42
+ @total ||= 0
43
+ @total += _size
44
+ Elastic::Configuration.logger.info "Imported #{@total} documents"
45
+ end
46
+
47
+ def render_for_es(_object)
48
+ index.new(_object).as_es_document
49
+ end
50
+
51
+ def main_target
52
+ index.definition.main_target
53
+ end
54
+
55
+ def targets
56
+ index.definition.targets
57
+ end
58
+
59
+ def middleware_options
60
+ index.definition.middleware_options
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,61 @@
1
+ module Elastic
2
+ module Configuration
3
+ extend self
4
+
5
+ def configure(_options = nil, &_block)
6
+ if _options.nil?
7
+ _block.call self
8
+ else
9
+ @config = config.merge _options.symbolize_keys
10
+ end
11
+ end
12
+
13
+ def api_client
14
+ config[:client] ||= load_api_client
15
+ end
16
+
17
+ def index_name
18
+ config[:index]
19
+ end
20
+
21
+ def indices_path
22
+ 'app/indices'
23
+ end
24
+
25
+ def page_size
26
+ @config[:page_size]
27
+ end
28
+
29
+ def coord_similarity
30
+ @config[:coord_similarity]
31
+ end
32
+
33
+ def strict_mode
34
+ @config[:strict_types]
35
+ end
36
+
37
+ def logger
38
+ @config[:logger] || default_logger
39
+ end
40
+
41
+ private
42
+
43
+ def config
44
+ @config ||= {
45
+ host: '127.0.0.1',
46
+ port: 9200,
47
+ page_size: 20,
48
+ coord_similarity: true,
49
+ strict_types: true
50
+ }
51
+ end
52
+
53
+ def default_logger
54
+ @default_logger ||= Logger.new(STDOUT)
55
+ end
56
+
57
+ def load_api_client
58
+ Elasticsearch::Client.new host: config[:host], port: config[:port]
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,102 @@
1
+ module Elastic::Core
2
+ class Adaptor
3
+ def initialize(_suffix)
4
+ @suffix = _suffix
5
+ end
6
+
7
+ def index_name
8
+ @index_name ||= "#{Elastic::Configuration.index_name}_#{@suffix}"
9
+ end
10
+
11
+ def remap(_type, _mapping)
12
+ # TODO
13
+ end
14
+
15
+ def exists?
16
+ api_client.indices.exists? build_options
17
+ end
18
+
19
+ def ensure_index
20
+ create unless exists?
21
+ self
22
+ end
23
+
24
+ def create
25
+ api_client.indices.create build_options
26
+ api_client.cluster.health wait_for_status: 'yellow'
27
+ self
28
+ end
29
+
30
+ def drop
31
+ api_client.indices.delete build_options
32
+ self
33
+ end
34
+
35
+ def exists_type?(_type)
36
+ api_client.indices.exists_type build_options(type: _type)
37
+ end
38
+
39
+ def exists_mapping?(_type)
40
+ !api_client.indices.get_mapping(build_options(type: _type)).empty?
41
+ end
42
+
43
+ def get_mappings(type: nil)
44
+ mappings = api_client.indices.get_mapping build_options(type: type)
45
+ mappings[index_name]['mappings']
46
+ end
47
+
48
+ def set_mapping(_type, _mapping)
49
+ api_client.indices.put_mapping build_options(
50
+ type: _type,
51
+ update_all_types: true,
52
+ body: _mapping
53
+ )
54
+ self
55
+ end
56
+
57
+ def index(_document)
58
+ api_client.index build_options(
59
+ id: _document['_id'],
60
+ type: _document['_type'],
61
+ body: _document['data']
62
+ )
63
+ self
64
+ end
65
+
66
+ def bulk_index(_documents)
67
+ body = _documents.map do |doc|
68
+ { 'index' => doc.merge('_index' => index_name) }
69
+ end
70
+
71
+ api_client.bulk body: body
72
+ self
73
+ end
74
+
75
+ def refresh
76
+ api_client.indices.refresh build_options
77
+ self
78
+ end
79
+
80
+ def find(_id, type: '_all')
81
+ api_client.get build_options(type: type, id: _id)
82
+ end
83
+
84
+ def count(type: nil, query: nil)
85
+ api_client.count(build_options(type: type, body: query))['count']
86
+ end
87
+
88
+ def query(type: nil, query: nil)
89
+ api_client.search build_options(type: type, body: query)
90
+ end
91
+
92
+ private
93
+
94
+ def api_client
95
+ Elastic::Configuration.api_client
96
+ end
97
+
98
+ def build_options(_options = {})
99
+ { index: index_name }.merge! _options
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,43 @@
1
+ module Elastic::Core
2
+ class BaseMiddleware
3
+ attr_reader :target
4
+
5
+ def initialize(_target)
6
+ @target = _target
7
+ end
8
+
9
+ def type_name
10
+ @target.to_s
11
+ end
12
+
13
+ def mode
14
+ not_supported :mode
15
+ end
16
+
17
+ def field_options_for(_field, _options)
18
+ not_supported :field_options_for
19
+ end
20
+
21
+ def collect_all(_options, &_block)
22
+ not_supported :collect_all
23
+ end
24
+
25
+ def collect_for(_collection, _options, &_block)
26
+ not_supported :collect_for
27
+ end
28
+
29
+ def find_by_ids(_ids, _options)
30
+ not_supported :find_by_ids
31
+ end
32
+
33
+ def build_from_data(_data, _options)
34
+ not_supported :build_from_data
35
+ end
36
+
37
+ private
38
+
39
+ def not_supported(_feature)
40
+ raise NotImplementedError, "#{self.class} does not support '#{_feature}'"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,64 @@
1
+ module Elastic::Core
2
+ class DefaultMiddleware < BaseMiddleware
3
+ def mode
4
+ case target_mode
5
+ when :find_multiple_ids, :find_single_id
6
+ :index
7
+ else
8
+ :storage
9
+ end
10
+ end
11
+
12
+ def field_options_for(_field, _options)
13
+ nil
14
+ end
15
+
16
+ def collect_all(_options, &_block)
17
+ method = collect_method_for(target)
18
+ target.public_send(method, &_block) if method
19
+ end
20
+
21
+ def collect_for(_collection, _options, &_block)
22
+ method = collect_method_for(_collection)
23
+ raise ArgumentError, "Could not find a method to iterate over collection" if method.nil?
24
+ _collection.public_send(method, &_block)
25
+ end
26
+
27
+ def find_by_ids(_ids, _options)
28
+ case target_mode
29
+ when :find_multiple_ids
30
+ target.find_by_elastic_ids(_ids)
31
+ when :find_single_id
32
+ _ids.map { |id| target.find_by_elastic_id(id) }
33
+ end
34
+ end
35
+
36
+ def build_from_data(_data, _options)
37
+ case target_mode
38
+ when :custom_build
39
+ target.build_from_elastic_data(_data)
40
+ when :open_struct
41
+ OpenStruct.new _data
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def target_mode
48
+ @target_mode ||= detect_output_mode
49
+ end
50
+
51
+ def collect_method_for(_target)
52
+ return :find_each_for_elastic if _target.respond_to?(:find_each_for_elastic)
53
+ return :each if _target.respond_to?(:each)
54
+ nil
55
+ end
56
+
57
+ def detect_output_mode
58
+ return :find_multiple_ids if target.respond_to? :find_by_elastic_ids
59
+ return :find_single_id if target.respond_to? :find_by_elastic_id
60
+ return :custom_build if target.respond_to? :build_from_elastic_data
61
+ :open_struct
62
+ end
63
+ end
64
+ end