esse 0.0.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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rubocop.yml +128 -0
  4. data/CHANGELOG.md +0 -0
  5. data/Gemfile +7 -0
  6. data/Gemfile.lock +60 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +50 -0
  9. data/Rakefile +4 -0
  10. data/bin/console +22 -0
  11. data/bin/setup +8 -0
  12. data/esse.gemspec +39 -0
  13. data/exec/esse +9 -0
  14. data/lib/esse.rb +7 -0
  15. data/lib/esse/backend/index.rb +38 -0
  16. data/lib/esse/backend/index/aliases.rb +69 -0
  17. data/lib/esse/backend/index/create.rb +56 -0
  18. data/lib/esse/backend/index/delete.rb +38 -0
  19. data/lib/esse/backend/index/documents.rb +23 -0
  20. data/lib/esse/backend/index/existance.rb +23 -0
  21. data/lib/esse/backend/index/update.rb +19 -0
  22. data/lib/esse/backend/index_type.rb +32 -0
  23. data/lib/esse/backend/index_type/documents.rb +203 -0
  24. data/lib/esse/cli.rb +29 -0
  25. data/lib/esse/cli/base.rb +11 -0
  26. data/lib/esse/cli/generate.rb +51 -0
  27. data/lib/esse/cli/index.rb +14 -0
  28. data/lib/esse/cli/templates/index.rb.erb +59 -0
  29. data/lib/esse/cli/templates/mappings.json +6 -0
  30. data/lib/esse/cli/templates/serializer.rb.erb +14 -0
  31. data/lib/esse/cluster.rb +58 -0
  32. data/lib/esse/config.rb +73 -0
  33. data/lib/esse/core.rb +89 -0
  34. data/lib/esse/index.rb +21 -0
  35. data/lib/esse/index/actions.rb +10 -0
  36. data/lib/esse/index/backend.rb +13 -0
  37. data/lib/esse/index/base.rb +118 -0
  38. data/lib/esse/index/descendants.rb +17 -0
  39. data/lib/esse/index/inheritance.rb +18 -0
  40. data/lib/esse/index/mappings.rb +47 -0
  41. data/lib/esse/index/naming.rb +64 -0
  42. data/lib/esse/index/settings.rb +48 -0
  43. data/lib/esse/index/type.rb +31 -0
  44. data/lib/esse/index_mapping.rb +38 -0
  45. data/lib/esse/index_setting.rb +41 -0
  46. data/lib/esse/index_type.rb +10 -0
  47. data/lib/esse/index_type/actions.rb +11 -0
  48. data/lib/esse/index_type/backend.rb +13 -0
  49. data/lib/esse/index_type/mappings.rb +42 -0
  50. data/lib/esse/index_type/serializer.rb +87 -0
  51. data/lib/esse/primitives.rb +3 -0
  52. data/lib/esse/primitives/hstring.rb +85 -0
  53. data/lib/esse/template_loader.rb +46 -0
  54. data/lib/esse/types/mapping.rb +0 -0
  55. data/lib/esse/version.rb +5 -0
  56. metadata +215 -0
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ module Backend
5
+ class Index
6
+ module InstanceMethods
7
+ DEFAULT_OPTIONS = {
8
+ alias: true,
9
+ }.freeze
10
+
11
+ # Creates index and applies mappings and settings.
12
+ #
13
+ # UsersIndex.backend.create # creates index named `<prefix_>users_<suffix|index_version|timestamp>`
14
+ #
15
+ # @param options [Hash] Options hash
16
+ # @option options [Boolean] :alias Update `index_name` alias along with the new index
17
+ # @option options [String] :suffix The index suffix. Defaults to the `IndexClass#index_version` or
18
+ # `Esse.timestamp`. Suffixed index names might be used for zero-downtime mapping change.
19
+ # @return [Hash, false] the elasticsearch response or false in case of unsuccessful creation.
20
+ #
21
+ # @see http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
22
+ def create(suffix: nil, **options)
23
+ create!(suffix: suffix, **options)
24
+ rescue Elasticsearch::Transport::Transport::Errors::BadRequest
25
+ false
26
+ end
27
+
28
+ # Creates index and applies mappings and settings.
29
+ #
30
+ # UsersIndex.backend.create! # creates index named `<prefix_>users_<suffix|index_version|timestamp>`
31
+ #
32
+ # @param options [Hash] Options hash
33
+ # @option options [Boolean] :alias Update `index_name` alias along with the new index
34
+ # @option options [String] :suffix The index suffix. Defaults to the `IndexClass#index_version` or
35
+ # `Esse.timestamp`. Suffixed index names might be used for zero-downtime mapping change.
36
+ # @raise [Elasticsearch::Transport::Transport::Errors::NotFound] when index already exists
37
+ # @return [Hash] the elasticsearch response
38
+ #
39
+ # @see http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
40
+ def create!(suffix: nil, **options)
41
+ options = DEFAULT_OPTIONS.merge(options)
42
+ name = real_index_name(suffix)
43
+ definition = [settings_hash, mappings_hash].reduce(&:merge)
44
+
45
+ if options[:alias] && name != index_name
46
+ definition[:aliases] = { index_name => {} }
47
+ end
48
+
49
+ client.indices.create(index: name, body: definition)
50
+ end
51
+ end
52
+
53
+ include InstanceMethods
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ module Backend
5
+ class Index
6
+ module InstanceMethods
7
+ # Deletes ES index
8
+ #
9
+ # UsersIndex.backend.delete! # deletes `<prefix_>users<_suffix|_index_version|_timestamp>` index
10
+ #
11
+ # @param options [Hash] Options hash
12
+ # @option [String, nil] :suffix The index suffix Use nil if you want to delete the current index.
13
+ # @raise [Elasticsearch::Transport::Transport::Errors::NotFound] when index does not exists
14
+ # @return [Hash] elasticsearch response
15
+ def delete!(suffix:)
16
+ name = suffix ? real_index_name(suffix) : index_name
17
+
18
+ client.indices.delete(index: name)
19
+ end
20
+
21
+ # Deletes ES index
22
+ #
23
+ # UsersIndex.backend.delete # deletes `<prefix_>users<_suffix|_index_version|_timestamp>` index
24
+ #
25
+ # @param options [Hash] Options hash
26
+ # @option [String] :suffix The index suffix. Use nil if you want to delete the current index.
27
+ # @return [Hash, false] elasticsearch response, of false in case of error.
28
+ def delete(suffix: index_version)
29
+ delete!(suffix: suffix)
30
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
31
+ false
32
+ end
33
+ end
34
+
35
+ include InstanceMethods
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ module Backend
5
+ class Index
6
+ module InstanceMethods
7
+ def import!(**options)
8
+ type_hash.each_value do |type|
9
+ type.backend.import!(**options)
10
+ end
11
+ end
12
+
13
+ def import(**options)
14
+ type_hash.each_value do |type|
15
+ type.backend.import(**options)
16
+ end
17
+ end
18
+ end
19
+
20
+ include InstanceMethods
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ module Backend
5
+ class Index
6
+ module InstanceMethods
7
+ # Checks the index existance. Returns true or false
8
+ #
9
+ # UsersIndex.backend.exist? #=> true
10
+ #
11
+ # @param options [Hash] Options hash
12
+ # @option options [String, nil] :suffix The index suffix. Defaults to the index_version.
13
+ # Use nil if you want to check existence of the `index_name` index or alias.
14
+ def exist?(suffix: index_version)
15
+ name = suffix ? real_index_name(suffix) : index_name
16
+ client.indices.exists(index: name)
17
+ end
18
+ end
19
+
20
+ include InstanceMethods
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ module Backend
5
+ class Index
6
+ module InstanceMethods
7
+ def update_mapping!(**options)
8
+ # @TODO
9
+ end
10
+
11
+ def update_settings!(**options)
12
+ # @TODO
13
+ end
14
+ end
15
+
16
+ include InstanceMethods
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Esse
6
+ module Backend
7
+ class IndexType
8
+ require_relative 'index_type/documents'
9
+
10
+ extend Forwardable
11
+
12
+ # Type delegators
13
+ def_delegators :@index_type, :type_name, :each_serialized_batch, :serialize
14
+ # Index delegators
15
+ def_delegators :index_class, :index_name
16
+
17
+ def initialize(type)
18
+ @index_type = type
19
+ end
20
+
21
+ protected
22
+
23
+ def index_class
24
+ @index_type.index
25
+ end
26
+
27
+ def client
28
+ index_class.cluster.client
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ module Backend
5
+ class IndexType
6
+ module InstanceMethods
7
+ # Resolve collection and index data
8
+ #
9
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
10
+ # @option [Hash] :context The collection context. This value will be passed as argument to the collection
11
+ # May be SQL condition or any other filter you have defined on the collection.
12
+ def import(context: {}, **options)
13
+ each_serialized_batch(context || {}) do |batch|
14
+ bulk(index: batch, **options)
15
+ end
16
+ end
17
+ alias import! import
18
+
19
+ # Performs multiple indexing or delete operations in a single API call.
20
+ # This reduces overhead and can greatly increase indexing speed.
21
+ #
22
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
23
+ # @param options [Array] :index list of serialized documents to be indexed(Optional)
24
+ # @param options [Array] :delete list of serialized documents to be deleted(Optional)
25
+ # @param options [Array] :create list of serialized documents to be created(Optional)
26
+ # @return [Hash, nil] the elasticsearch response or nil if there is no data.
27
+ #
28
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-bulk.html
29
+ def bulk(index: nil, delete: nil, create: nil, **options)
30
+ body = []
31
+ Array(index).each do |entry|
32
+ id, data = Esse.doc_id!(entry)
33
+ body << { index: { _id: id, data: data } } if id
34
+ end
35
+ Array(create).each do |entry|
36
+ id, data = Esse.doc_id!(entry)
37
+ body << { create: { _id: id, data: data } } if id
38
+ end
39
+ Array(delete).each do |entry|
40
+ id, _data = Esse.doc_id!(entry, delete: [], keep: %w[_id id])
41
+ body << { delete: { _id: id } } if id
42
+ end
43
+
44
+ return if body.empty?
45
+
46
+ definition = {
47
+ index: index_name,
48
+ type: type_name,
49
+ body: body,
50
+ }.merge(options)
51
+
52
+ client.bulk(definition)
53
+ end
54
+ alias bulk! bulk
55
+
56
+ # Adds a JSON document to the specified index and makes it searchable. If the document
57
+ # already exists, updates the document and increments its version.
58
+ #
59
+ # UsersIndex::User.index(id: 1, body: { name: 'name' }) # { '_id' => 1, ...}
60
+ #
61
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
62
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
63
+ # @param options [Hash] :body The JSON document that will be indexed (Required)
64
+ # @return [Hash] the elasticsearch response Hash
65
+ #
66
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-index_.html
67
+ def index(id:, body:, **options)
68
+ client.index(
69
+ options.merge(index: index_name, type: type_name, id: id, body: body),
70
+ )
71
+ end
72
+ alias index! index
73
+
74
+ # Updates a document using the specified script.
75
+ #
76
+ # UsersIndex::User.update!(id: 1, body: { doc: { ... } }) # { '_id' => 1, ...}
77
+ #
78
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
79
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
80
+ # @param options [Hash] :body the body of the request
81
+ # @raise [Elasticsearch::Transport::Transport::Errors::NotFound] when the doc does not exist
82
+ # @return [Hash] elasticsearch response hash
83
+ #
84
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-update.html
85
+ def update!(id:, body:, **options)
86
+ client.update(
87
+ options.merge(index: index_name, type: type_name, id: id, body: body),
88
+ )
89
+ end
90
+
91
+ # Updates a document using the specified script.
92
+ #
93
+ # UsersIndex::User.update(id: 1, body: { doc: { ... } }) # { '_id' => 1, ...}
94
+ #
95
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
96
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
97
+ # @param options [Hash] :body the body of the request
98
+ # @return [Hash, false] the elasticsearch response hash, or false in case of failure
99
+ #
100
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-update.html
101
+ def update(id:, body:, **options)
102
+ update!(id: id, body: body, **options)
103
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
104
+ false
105
+ end
106
+
107
+ # Removes a JSON document from the specified index.
108
+ #
109
+ # UsersIndex::User.delete!(id: 1) # true
110
+ # UsersIndex::User.delete!(id: 'missing') # raise Elasticsearch::Transport::Transport::Errors::NotFound
111
+ #
112
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
113
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
114
+ # @raise [Elasticsearch::Transport::Transport::Errors::NotFound] when the doc does not exist
115
+ # @return [Boolean] true when the operation is successfully completed
116
+ #
117
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-delete.html
118
+ def delete!(id:, **options)
119
+ client.delete(options.merge(index: index_name, type: type_name, id: id))
120
+ end
121
+
122
+ # Removes a JSON document from the specified index.
123
+ #
124
+ # UsersIndex::User.delete(id: 1) # true
125
+ # UsersIndex::User.delete(id: 'missing') # false
126
+ #
127
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
128
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
129
+ # @raise [Elasticsearch::Transport::Transport::Errors::NotFound] when the doc does not exist
130
+ # @return [Boolean] true when the operation is successfully completed
131
+ #
132
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-delete.html
133
+ def delete(id:, **options)
134
+ delete!(id: id, **options)
135
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
136
+ false
137
+ end
138
+
139
+ # Gets the number of matches for a search query.
140
+ #
141
+ # UsersIndex::User.count # 999
142
+ # UsersIndex::User.count(body: { ... }) # 32
143
+ #
144
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
145
+ # @param options [Hash] :body A query to restrict the results specified with the Query DSL (optional)
146
+ # @return [Integer] amount of documents found
147
+ #
148
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/search-count.html
149
+ def count(**options)
150
+ response = client.count(options.merge(index: index_name, type: type_name))
151
+ response['count']
152
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
153
+ 0
154
+ end
155
+
156
+ # Check if a JSON document exists
157
+ #
158
+ # UsersIndex::User.exist?(id: 1) # true
159
+ # UsersIndex::User.exist?(id: 'missing') # false
160
+ #
161
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
162
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
163
+ # @return [Boolean] true if the document exists
164
+ def exist?(id:, **options)
165
+ client.exists(options.merge(index: index_name, type: type_name, id: id))
166
+ end
167
+
168
+ # Retrieves the specified JSON document from an index.
169
+ #
170
+ # UsersIndex::User.find!(id: 1) # { '_id' => 1, ... }
171
+ # UsersIndex::User.find!(id: 'missing') # raise Elasticsearch::Transport::Transport::Errors::NotFound
172
+ #
173
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
174
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
175
+ # @raise [Elasticsearch::Transport::Transport::Errors::NotFound] when the doc does not exist
176
+ # @return [Hash] The elasticsearch document.
177
+ #
178
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-get.html
179
+ def find!(id:, **options)
180
+ client.get(options.merge(index: index_name, type: type_name, id: id))
181
+ end
182
+
183
+ # Retrieves the specified JSON document from an index.
184
+ #
185
+ # UsersIndex::User.find(id: 1) # { '_id' => 1, ... }
186
+ # UsersIndex::User.find(id: 'missing') # nil
187
+ #
188
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
189
+ # @param options [String, Integer] :id The `_id` of the elasticsearch document
190
+ # @return [Hash, nil] The elasticsearch document
191
+ #
192
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-get.html
193
+ def find(id:, **options)
194
+ find!(id: id, **options)
195
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
196
+ nil
197
+ end
198
+ end
199
+
200
+ include InstanceMethods
201
+ end
202
+ end
203
+ end
data/lib/esse/cli.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ require_relative 'cli/index'
6
+ require_relative 'cli/generate'
7
+
8
+ module Esse
9
+ module CLI
10
+ def self.start(*args)
11
+ Root.start(*args)
12
+ end
13
+
14
+ class Root < Thor
15
+ map %w[--version -v] => :version
16
+
17
+ desc 'index SUBCOMMAND ...ARGS', 'Manage indices'
18
+ subcommand 'index', Index
19
+
20
+ desc 'generate SUBCOMMAND ...ARGS', 'Run generators'
21
+ subcommand 'generate', Generate
22
+
23
+ desc '--version, -v', 'Show package version'
24
+ def version
25
+ puts format('Esse version: %<version>s', version: Esse::VERSION)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Esse
6
+ module CLI
7
+ class Base < Thor
8
+ include Thor::Actions
9
+ end
10
+ end
11
+ end