esse 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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