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