esse 0.2.0 → 0.2.2

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/esse/backend/index/aliases.rb +8 -8
  3. data/lib/esse/backend/index/close.rb +3 -3
  4. data/lib/esse/backend/index/create.rb +4 -4
  5. data/lib/esse/backend/index/delete.rb +4 -4
  6. data/lib/esse/backend/index/documents.rb +253 -6
  7. data/lib/esse/backend/index/existance.rb +3 -3
  8. data/lib/esse/backend/index/open.rb +3 -3
  9. data/lib/esse/backend/index/refresh.rb +7 -5
  10. data/lib/esse/backend/index/reset.rb +4 -4
  11. data/lib/esse/backend/index/update.rb +7 -7
  12. data/lib/esse/backend/index.rb +16 -14
  13. data/lib/esse/backend/repository_backend.rb +105 -0
  14. data/lib/esse/cli/event_listener.rb +14 -0
  15. data/lib/esse/cli/generate.rb +53 -12
  16. data/lib/esse/cli/index/base_operation.rb +5 -13
  17. data/lib/esse/cli/index/import.rb +6 -2
  18. data/lib/esse/cli/index/update_mapping.rb +3 -4
  19. data/lib/esse/cli/index.rb +2 -0
  20. data/lib/esse/cli/templates/{type_collection.rb.erb → collection.rb.erb} +6 -18
  21. data/lib/esse/cli/templates/config.rb.erb +13 -3
  22. data/lib/esse/cli/templates/index.rb.erb +53 -109
  23. data/lib/esse/cli/templates/mappings.json +27 -0
  24. data/lib/esse/cli/templates/serializer.rb.erb +34 -0
  25. data/lib/esse/cli/templates/settings.json +62 -0
  26. data/lib/esse/client_proxy/search.rb +44 -0
  27. data/lib/esse/client_proxy.rb +32 -0
  28. data/lib/esse/cluster.rb +64 -9
  29. data/lib/esse/cluster_engine.rb +42 -0
  30. data/lib/esse/collection.rb +18 -0
  31. data/lib/esse/config.rb +14 -2
  32. data/lib/esse/core.rb +23 -6
  33. data/lib/esse/deprecations/cluster.rb +27 -0
  34. data/lib/esse/deprecations/index.rb +19 -0
  35. data/lib/esse/deprecations/repository.rb +19 -0
  36. data/lib/esse/deprecations.rb +3 -0
  37. data/lib/esse/dynamic_template.rb +39 -0
  38. data/lib/esse/errors.rb +53 -2
  39. data/lib/esse/events/event.rb +4 -19
  40. data/lib/esse/events.rb +3 -0
  41. data/lib/esse/hash_document.rb +38 -0
  42. data/lib/esse/import/bulk.rb +96 -0
  43. data/lib/esse/import/request_body.rb +60 -0
  44. data/lib/esse/index/attributes.rb +98 -0
  45. data/lib/esse/index/base.rb +1 -1
  46. data/lib/esse/index/inheritance.rb +30 -0
  47. data/lib/esse/index/mappings.rb +6 -19
  48. data/lib/esse/index/object_document_mapper.rb +95 -0
  49. data/lib/esse/index/plugins.rb +42 -0
  50. data/lib/esse/index/search.rb +27 -0
  51. data/lib/esse/index/settings.rb +2 -2
  52. data/lib/esse/index/type.rb +52 -11
  53. data/lib/esse/index.rb +10 -6
  54. data/lib/esse/index_mapping.rb +10 -2
  55. data/lib/esse/index_setting.rb +3 -1
  56. data/lib/esse/null_document.rb +35 -0
  57. data/lib/esse/plugins.rb +12 -0
  58. data/lib/esse/primitives/hstring.rb +1 -1
  59. data/lib/esse/{index_type → repository}/actions.rb +1 -1
  60. data/lib/esse/{index_type → repository}/backend.rb +2 -2
  61. data/lib/esse/repository/object_document_mapper.rb +157 -0
  62. data/lib/esse/repository.rb +18 -0
  63. data/lib/esse/search/query.rb +105 -0
  64. data/lib/esse/search/response.rb +46 -0
  65. data/lib/esse/serializer.rb +76 -0
  66. data/lib/esse/version.rb +1 -1
  67. data/lib/esse.rb +20 -5
  68. metadata +35 -30
  69. data/lib/esse/backend/index_type/documents.rb +0 -214
  70. data/lib/esse/backend/index_type.rb +0 -37
  71. data/lib/esse/cli/templates/type_mappings.json +0 -6
  72. data/lib/esse/cli/templates/type_serializer.rb.erb +0 -23
  73. data/lib/esse/index/naming.rb +0 -64
  74. data/lib/esse/index_type/mappings.rb +0 -42
  75. data/lib/esse/index_type.rb +0 -15
  76. data/lib/esse/object_document_mapper.rb +0 -110
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class ClientProxy
5
+ module InstanceMethods
6
+ # Returns results matching a query.
7
+ def search(index:, **options)
8
+ definition = options.merge(
9
+ index: index,
10
+ )
11
+
12
+ Esse::Events.instrument('elasticsearch.search') do |payload|
13
+ payload[:request] = definition
14
+ payload[:response] = coerce_exception { client.search(definition) }
15
+ end
16
+ end
17
+
18
+ # Allows to retrieve a large numbers of results from a single search request.
19
+ #
20
+ # @param [Time] :scroll Specify how long a consistent view of the index should be maintained for scrolled search
21
+ # @param [Boolean] :rest_total_hits_as_int Indicates whether hits.total should be rendered as an integer or an object in the rest search response
22
+ # @param [Hash] :body The scroll ID
23
+ def scroll(scroll:, **definition)
24
+ unless definition[:body]
25
+ raise ArgumentError, 'scroll search must have a :body with the :scroll_id'
26
+ end
27
+ Esse::Events.instrument('elasticsearch.search') do |payload|
28
+ payload[:request] = definition
29
+ payload[:response] = coerce_exception { client.scroll(scroll: scroll, **definition) }
30
+ end
31
+ end
32
+
33
+ # Explicitly clears the search context for a scroll.
34
+ #
35
+ # @param [Hash] :body Body with the "scroll_id" (string or array of strings) Scroll IDs to clear.
36
+ # To clear all scroll IDs, use _all.
37
+ def clear_scroll(body:, **options)
38
+ coerce_exception { client.clear_scroll(body: body, **options) }
39
+ end
40
+ end
41
+
42
+ include InstanceMethods
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class ClientProxy
5
+ require_relative './client_proxy/search'
6
+
7
+ extend Forwardable
8
+
9
+ def_delegators :@cluster, :client
10
+
11
+ attr_reader :cluster
12
+
13
+ def initialize(cluster)
14
+ @cluster = cluster
15
+ end
16
+
17
+ # Elasticsearch::Transport was renamed to Elastic::Transport in 8.0
18
+ # This lib should support both versions that's why we are wrapping up the transport
19
+ # errors to local errors.
20
+ def coerce_exception
21
+ yield
22
+ rescue => exception
23
+ name = Hstring.new(exception.class.name)
24
+ if /^(Elasticsearch|Elastic|OpenSearch)?::Transport::Transport::Errors/.match?(name.value) && \
25
+ (exception_class = Esse::Backend::ERRORS[name.demodulize.value])
26
+ raise exception_class.new(exception.message)
27
+ else
28
+ raise exception
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/esse/cluster.rb CHANGED
@@ -1,16 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'cluster_engine'
4
+ require_relative 'client_proxy'
5
+
3
6
  module Esse
4
7
  class Cluster
5
- ATTRIBUTES = %i[index_prefix index_settings client wait_for_status].freeze
8
+ ATTRIBUTES = %i[index_prefix settings mappings client wait_for_status].freeze
6
9
  WAIT_FOR_STATUSES = %w[green yellow red].freeze
7
10
 
8
11
  # The index prefix. For example an index named UsersIndex.
9
12
  # With `index_prefix = 'app1'`. Final index/alias is: 'app1_users'
10
13
  attr_accessor :index_prefix
11
14
 
12
- # This settings will be passed through all indices during the mapping
13
- attr_accessor :index_settings
15
+ # This global settings will be passed through all indices
16
+ attr_accessor :settings
17
+
18
+ # This global mappings will be applied to all indices
19
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html
20
+ attr_accessor :mappings
14
21
 
15
22
  # if this option set, actions such as creating or deleting index,
16
23
  # importing data will wait for the status specified. Extremely useful
@@ -25,7 +32,8 @@ module Esse
25
32
 
26
33
  def initialize(id:, **options)
27
34
  @id = id.to_sym
28
- @index_settings = {}
35
+ @settings = {}
36
+ @mappings = {}
29
37
  assign(options)
30
38
  end
31
39
 
@@ -41,16 +49,28 @@ module Esse
41
49
  end
42
50
 
43
51
  def client
44
- @client ||= Elasticsearch::Client.new
52
+ @client ||= if defined? Elasticsearch::Client
53
+ Elasticsearch::Client.new
54
+ elsif defined? OpenSearch::Client
55
+ OpenSearch::Client.new
56
+ else
57
+ raise Esse::Error, <<~ERROR
58
+ Elasticsearch::Client or OpenSearch::Client is not defined.
59
+ Please install elasticsearch or opensearch-ruby gem.
60
+ ERROR
61
+ end
45
62
  end
46
63
 
47
- # Define the elasticsearch client connectio
48
- # @param es_client [Elasticsearch::Client, Hash] an instance of elasticsearch/api client or an hash
49
- # with the settings that will be used to initialize Elasticsearch::Client
64
+ # Define the elasticsearch client connection
65
+ # @param es_client [Elasticsearch::Client, OpenSearch::Client, Hash] an instance of elasticsearch/api client or an hash
66
+ # with the settings that will be used to initialize the Client
50
67
  def client=(es_client)
51
- @client = if es_client.is_a?(Hash)
68
+ @client = if es_client.is_a?(Hash) && defined?(Elasticsearch::Client)
52
69
  settings = es_client.each_with_object({}) { |(k, v), r| r[k.to_sym] = v }
53
70
  Elasticsearch::Client.new(settings)
71
+ elsif es_client.is_a?(Hash) && defined?(OpenSearch::Client)
72
+ settings = es_client.each_with_object({}) { |(k, v), r| r[k.to_sym] = v }
73
+ OpenSearch::Client.new(settings)
54
74
  else
55
75
  es_client
56
76
  end
@@ -70,5 +90,40 @@ module Esse
70
90
 
71
91
  client.cluster.health(wait_for_status: status.to_s)
72
92
  end
93
+
94
+ # @idea Change this to use the response from `GET /`
95
+ def document_type?
96
+ defined?(OpenSearch::VERSION) || \
97
+ (defined?(Elasticsearch::VERSION) && Elasticsearch::VERSION < '7')
98
+ end
99
+
100
+ def info
101
+ @info ||= begin
102
+ resp = client.info
103
+ {
104
+ distribution: (resp.dig('version', 'distribution') || 'elasticsearch'),
105
+ version: resp.dig('version', 'number'),
106
+ }
107
+ end
108
+ end
109
+
110
+ def engine
111
+ ClusterEngine.new(**info)
112
+ end
113
+
114
+ # Build a search query for the given indices
115
+ #
116
+ # @param indices [Array<Esse::Index, String>] The indices class or the index name
117
+ # @return [Esse::Search::Query] The search query instance
118
+ def search(*indices, **kwargs, &block)
119
+ Esse::Search::Query.new(api, *indices, **kwargs, &block)
120
+ end
121
+
122
+ # Return the proxy object used to perform low level actions on the elasticsearch cluster through the official api client
123
+ #
124
+ # @return [Esse::ClientProxy] The cluster api instance
125
+ def api
126
+ Esse::ClientProxy.new(self)
127
+ end
73
128
  end
74
129
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class ClusterEngine
5
+ OPENSEARCH_FORK_VERSION = '7.10.2'
6
+
7
+ attr_reader :version, :distribution
8
+
9
+ def initialize(distribution:, version:)
10
+ @distribution = distribution
11
+ @version = version
12
+ end
13
+
14
+ def engine_version
15
+ return @version unless opensearch?
16
+
17
+ OPENSEARCH_FORK_VERSION
18
+ end
19
+
20
+ def opensearch?
21
+ distribution == 'opensearch'
22
+ end
23
+
24
+ def elasticsearch?
25
+ distribution == 'elasticsearch'
26
+ end
27
+
28
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.17/removal-of-types.html
29
+ def mapping_single_type?
30
+ engine_version >= '6'
31
+ end
32
+
33
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/6.3/mapping.html
34
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/6.4/mapping.html
35
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.1/mapping.html
36
+ def mapping_default_type
37
+ return unless engine_version.to_i == 6
38
+
39
+ engine_version >= '6.4' ? :_doc : :doc
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class Collection
5
+ include Enumerable
6
+ attr_reader :options
7
+
8
+ def initialize(**options)
9
+ @options = options
10
+ end
11
+
12
+ # @yield [<Array, Hash>] A batch of documents to be serialized and indexed.
13
+ # @abstract Override this method to yield each chunk of documents with optional metadata
14
+ def each
15
+ raise NotImplementedError, 'Override this method to iterate over the collection'
16
+ end
17
+ end
18
+ end
data/lib/esse/config.rb CHANGED
@@ -9,10 +9,14 @@ module Esse
9
9
  # conf.cluster(:v1) do |cluster|
10
10
  # cluster.index_prefix = 'backend'
11
11
  # cluster.client = Elasticsearch::Client.new
12
- # cluster.index_settings = {
12
+ # cluster.settings = {
13
13
  # number_of_shards: 2,
14
14
  # number_of_replicas: 0
15
15
  # }
16
+ # cluster.mappings = {
17
+ # dynamic_templates: [...]
18
+ # properties: { ... }
19
+ # }
16
20
  # end
17
21
  # end
18
22
  #
@@ -36,13 +40,17 @@ module Esse
36
40
  # end
37
41
  class Config
38
42
  DEFAULT_CLUSTER_ID = :default
39
- ATTRIBUTES = %i[indices_directory].freeze
43
+ ATTRIBUTES = %i[indices_directory bulk_wait_interval].freeze
40
44
 
41
45
  # The location of the indices. Defaults to the `app/indices`
42
46
  attr_reader :indices_directory
43
47
 
48
+ # wait a given period between posting pages to give Elasticsearch time to catch up.
49
+ attr_reader :bulk_wait_interval
50
+
44
51
  def initialize
45
52
  self.indices_directory = 'app/indices'
53
+ self.bulk_wait_interval = 0.1
46
54
  @clusters = {}
47
55
  cluster(DEFAULT_CLUSTER_ID) # initialize the :default client
48
56
  end
@@ -66,6 +74,10 @@ module Esse
66
74
  @indices_directory = value.is_a?(Pathname) ? value : Pathname.new(value)
67
75
  end
68
76
 
77
+ def bulk_wait_interval=(value)
78
+ @bulk_wait_interval = value.to_f
79
+ end
80
+
69
81
  def load(arg)
70
82
  case arg
71
83
  when Hash
data/lib/esse/core.rb CHANGED
@@ -1,22 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
- require 'multi_json'
5
- require 'elasticsearch'
6
-
7
3
  module Esse
8
4
  require_relative 'config'
9
5
  require_relative 'cluster'
10
6
  require_relative 'primitives'
11
- require_relative 'index_type'
7
+ require_relative 'collection'
8
+ require_relative 'serializer'
9
+ require_relative 'hash_document'
10
+ require_relative 'null_document'
11
+ require_relative 'repository'
12
12
  require_relative 'index_setting'
13
+ require_relative 'dynamic_template'
13
14
  require_relative 'index_mapping'
14
15
  require_relative 'template_loader'
16
+ require_relative 'import/request_body'
17
+ require_relative 'import/bulk'
15
18
  require_relative 'backend/index'
16
- require_relative 'backend/index_type'
19
+ require_relative 'backend/repository_backend'
17
20
  require_relative 'version'
18
21
  require_relative 'logging'
19
22
  require_relative 'events'
23
+ require_relative 'search/query'
24
+ require_relative 'search/response'
25
+ require_relative 'deprecations' # Should be last
20
26
  include Logging
21
27
 
22
28
  @single_threaded = false
@@ -68,4 +74,15 @@ module Esse
68
74
  end
69
75
  [id, modified]
70
76
  end
77
+
78
+ def self.eager_load_indices!
79
+ return false unless Esse.config.indices_directory.exist?
80
+
81
+ Dir[Esse.config.indices_directory.join('**/*_index.rb')].map { |path| Pathname.new(path) }.each do |path|
82
+ next unless path.extname == '.rb'
83
+
84
+ require(path.expand_path.to_s)
85
+ end
86
+ true
87
+ end
71
88
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class Cluster
5
+ extend Gem::Deprecate
6
+
7
+ def index_settings
8
+ settings
9
+ end
10
+ deprecate :index_settings, :settings, 2022, 10
11
+
12
+ def index_settings=(value)
13
+ self.settings = value
14
+ end
15
+ deprecate :index_settings=, :settings=, 2022, 10
16
+
17
+ def index_mappings
18
+ mappings
19
+ end
20
+ deprecate :index_mappings, :mappings, 2022, 10
21
+
22
+ def index_mappings=(value)
23
+ self.mappings = value
24
+ end
25
+ deprecate :index_mappings=, :mappings=, 2022, 10
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class Index
5
+ class << self
6
+ extend Gem::Deprecate
7
+
8
+ def define_type(name, *args, **kwargs, &block)
9
+ repository(name, *args, **kwargs, &block)
10
+ end
11
+ deprecate :define_type, :repository, 2022, 8
12
+
13
+ def type_hash
14
+ repo_hash
15
+ end
16
+ deprecate :type_hash, :repo_hash, 2022, 8
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class Repository
5
+ class << self
6
+ extend Gem::Deprecate
7
+
8
+ def type_name
9
+ document_type
10
+ end
11
+ deprecate :type_name, :document_type, 2022, 10
12
+
13
+ def mappings(*args, &block)
14
+ index.mappings(*args, &block)
15
+ end
16
+ deprecate :mappings, "Esse::Index.mappings", 2022, 10
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'deprecations/index'
2
+ require_relative 'deprecations/cluster'
3
+ require_relative 'deprecations/repository'
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class DynamicTemplate
5
+ # @param [Array, Hash] value The list of dynamic_templates for mapping
6
+ def initialize(value)
7
+ @hash = normalize(value)
8
+ end
9
+
10
+ def merge!(value)
11
+ @hash = HashUtils.deep_merge(@hash, normalize(value))
12
+ end
13
+
14
+ def to_a
15
+ @hash.map do |name, value|
16
+ { name => value }
17
+ end
18
+ end
19
+
20
+ def any?
21
+ @hash.any?
22
+ end
23
+
24
+ def dup
25
+ self.class.new(@hash.dup)
26
+ end
27
+
28
+ private
29
+
30
+ def normalize(value)
31
+ case value
32
+ when Array
33
+ value.map { |v| normalize(v) }.reduce(&:merge)
34
+ when Hash
35
+ HashUtils.deep_transform_keys(value, &:to_sym)
36
+ end || {}
37
+ end
38
+ end
39
+ end
data/lib/esse/errors.rb CHANGED
@@ -4,6 +4,59 @@ module Esse
4
4
  class Error < StandardError
5
5
  end
6
6
 
7
+ # @todo Rename this
8
+ module Backend
9
+ class ServerError < ::Esse::Error; end
10
+
11
+ ES_TRANSPORT_ERRORS = {
12
+ 'MultipleChoices' => 'MultipleChoicesError', # 300
13
+ 'MovedPermanently' => 'MovedPermanentlyError', # 301
14
+ 'Found' => 'FoundError', # 302
15
+ 'SeeOther' => 'SeeOtherError', # 303
16
+ 'NotModified' => 'NotModifiedError', # 304
17
+ 'UseProxy' => 'UseProxyError', # 305
18
+ 'TemporaryRedirect' => 'TemporaryRedirectError', # 307
19
+ 'PermanentRedirect' => 'PermanentRedirectError', # 308
20
+ 'BadRequest' => 'BadRequestError', # 400
21
+ 'Unauthorized' => 'UnauthorizedError', # 401
22
+ 'PaymentRequired' => 'PaymentRequiredError', # 402
23
+ 'Forbidden' => 'ForbiddenError', # 403
24
+ 'NotFound' => 'NotFoundError', # 404
25
+ 'MethodNotAllowed' => 'MethodNotAllowedError', # 405
26
+ 'NotAcceptable' => 'NotAcceptableError', # 406
27
+ 'ProxyAuthenticationRequired' => 'ProxyAuthenticationRequiredError', # 407
28
+ 'RequestTimeout' => 'RequestTimeoutError', # 408
29
+ 'Conflict' => 'ConflictError', # 409
30
+ 'Gone' => 'GoneError', # 410
31
+ 'LengthRequired' => 'LengthRequiredError', # 411
32
+ 'PreconditionFailed' => 'PreconditionFailedError', # 412
33
+ 'RequestEntityTooLarge' => 'RequestEntityTooLargeError', # 413
34
+ 'RequestURITooLong' => 'RequestURITooLongError', # 414
35
+ 'UnsupportedMediaType' => 'UnsupportedMediaTypeError', # 415
36
+ 'RequestedRangeNotSatisfiable' => 'RequestedRangeNotSatisfiableError', # 416
37
+ 'ExpectationFailed' => 'ExpectationFailedError', # 417
38
+ 'ImATeapot' => 'ImATeapotError', # 418
39
+ 'TooManyConnectionsFromThisIP' => 'TooManyConnectionsFromThisIPError', # 421
40
+ 'UpgradeRequired' => 'UpgradeRequiredError', # 426
41
+ 'BlockedByWindowsParentalControls' => 'BlockedByWindowsParentalControlsError', # 450
42
+ 'RequestHeaderTooLarge' => 'RequestHeaderTooLargeError', # 494
43
+ 'HTTPToHTTPS' => 'HTTPToHTTPSError', # 497
44
+ 'ClientClosedRequest' => 'ClientClosedRequestError', # 499
45
+ 'InternalServerError' => 'InternalServerError', # 500
46
+ 'NotImplemented' => 'NotImplementedError', # 501
47
+ 'BadGateway' => 'BadGatewayError', # 502
48
+ 'ServiceUnavailable' => 'ServiceUnavailableError', # 503
49
+ 'GatewayTimeout' => 'GatewayTimeoutError', # 504
50
+ 'HTTPVersionNotSupported' => 'HTTPVersionNotSupportedError', # 505
51
+ 'VariantAlsoNegotiates' => 'VariantAlsoNegotiatesError', # 506
52
+ 'NotExtended' => 'NotExtendedError', # 510
53
+ }
54
+
55
+ ERRORS = ES_TRANSPORT_ERRORS.each_with_object({}) do |(transport_name, esse_name), memo|
56
+ memo[transport_name] = const_set esse_name, Class.new(ServerError)
57
+ end
58
+ end
59
+
7
60
  module Events
8
61
  class UnregisteredEventError < ::Esse::Error
9
62
  def initialize(object_or_event_id)
@@ -42,6 +95,4 @@ module Esse
42
95
  class InvalidOption < Error
43
96
  end
44
97
  end
45
-
46
- # Elasticsearch::Transport::Transport::Errors::NotFound
47
98
  end
@@ -3,6 +3,10 @@
3
3
  module Esse
4
4
  module Events
5
5
  class Event
6
+ extend Forwardable
7
+ def_delegators :@payload, :[], :fetch, :to_h, :key?
8
+ alias_method :to_hash, :to_h
9
+
6
10
  attr_reader :id
7
11
 
8
12
  # Initialize a new event
@@ -18,25 +22,6 @@ module Esse
18
22
  @payload = payload
19
23
  end
20
24
 
21
- # Get data from the payload
22
- #
23
- # @param [String,Symbol] name
24
- #
25
- # @api public
26
- def [](name)
27
- @payload.fetch(name)
28
- end
29
-
30
- # Coerce an event to a hash
31
- #
32
- # @return [Hash]
33
- #
34
- # @api public
35
- def to_h
36
- @payload
37
- end
38
- alias_method :to_hash, :to_h
39
-
40
25
  # Get or set a payload
41
26
  #
42
27
  # @overload
data/lib/esse/events.rb CHANGED
@@ -45,5 +45,8 @@ module Esse
45
45
  register_event 'elasticsearch.update_mapping'
46
46
  register_event 'elasticsearch.update_settings'
47
47
  register_event 'elasticsearch.update_aliases'
48
+ register_event 'elasticsearch.bulk'
49
+ register_event 'elasticsearch.search'
50
+ register_event 'elasticsearch.execute_search_query'
48
51
  end
49
52
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class HashDocument < Esse::Serializer
5
+ META_KEYS = %i[_id _type _routing routing].freeze
6
+
7
+ def initialize(object)
8
+ @object = object
9
+ @options = {}
10
+ end
11
+
12
+ # @return [String, Number] the document ID
13
+ def id
14
+ object['_id'] || object[:_id] || object['id'] || object[:id]
15
+ end
16
+
17
+ # @return [String, nil] the document type
18
+ def type
19
+ object['_type'] || object[:_type]
20
+ end
21
+
22
+ # @return [String, nil] the document routing
23
+ def routing
24
+ object['_routing'] || object[:_routing] || object['routing'] || object[:routing]
25
+ end
26
+
27
+ # @return [Hash] the document meta
28
+ def meta
29
+ {}
30
+ end
31
+
32
+ # @return [Hash] the document source
33
+ # @abstract Override this method to return the document source
34
+ def source
35
+ object.reject { |key, _| META_KEYS.include?(key.to_sym) }
36
+ end
37
+ end
38
+ end