elastic_search_framework 2.1.0 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bb31cdcff7f6e3f40501c7dcadba923be7582ff8d91daec8e52d7dd8689e8da
4
- data.tar.gz: b5c57937085a1719aae84d24fb10a93fbb582ec12ebe6f6b8c15f3aa987c053e
3
+ metadata.gz: fcc1601dc419b4ad4233065024791ad1199077c2622d243ef4192780eefae4aa
4
+ data.tar.gz: a66d974b4015cfc9fe4c16022ff5123aceaa147ecda5288b865d8ba7cdab3d88
5
5
  SHA512:
6
- metadata.gz: 214b8d927d48733fc52a98c7667621529efb9d68e331d8f4b3e4e70c76a2b72fd2e5d56f749cd5d0b2a6002316fd56bc77d9b68bf8091fe0402ea7c7f994db61
7
- data.tar.gz: 8151848be2786d442eea281aa10e89338d06109b20bde72de2df87fd2f0b31a8a2279a9d560a035f66af92333657890cd5a34ed22dbb828a25e9d3003886d459
6
+ metadata.gz: 235055cf6c16281b2f1f6ded4b71f37e1c72e6e6a1de0f820c186b86ae72c2d6885aee4bd4ddbb1e36e5648f834698f0e4c2e6869f693230794838066b49274a
7
+ data.tar.gz: 7df89d3968eb81751e8ee316c9a321da04b2b891130fcbef78fe24b4a3e7d5c8b29350abb56dc7f9f95fa1636510268b774231cd5d8c8c878d4c0ea3c622d6ef
@@ -8,6 +8,7 @@ require_relative 'elastic_search_framework/logger'
8
8
  require_relative 'elastic_search_framework/exceptions'
9
9
  require_relative 'elastic_search_framework/repository'
10
10
  require_relative 'elastic_search_framework/index'
11
+ require_relative 'elastic_search_framework/index_alias'
11
12
  require_relative 'elastic_search_framework/query'
12
13
 
13
14
  module ElasticSearchFramework
@@ -2,14 +2,19 @@ module ElasticSearchFramework
2
2
  module Index
3
3
  attr_accessor :index_settings
4
4
 
5
- def index(name:)
5
+ def index(name:, version: nil)
6
6
  unless instance_variable_defined?(:@elastic_search_index_def)
7
- instance_variable_set(:@elastic_search_index_def, name: "#{name}")
7
+ instance_variable_set(:@elastic_search_index_def, name: "#{name}#{version}")
8
+ instance_variable_set(:@elastic_search_index_version, version: version) unless version.nil?
8
9
  else
9
10
  raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Duplicate index description. Name: #{name}.")
10
11
  end
11
12
  end
12
13
 
14
+ def version
15
+ instance_variable_defined?(:@elastic_search_index_version) ? instance_variable_get(:@elastic_search_index_version) : 0
16
+ end
17
+
13
18
  def id(field)
14
19
  unless instance_variable_defined?(:@elastic_search_index_id)
15
20
  instance_variable_set(:@elastic_search_index_id, field)
@@ -160,7 +165,7 @@ module ElasticSearchFramework
160
165
  end
161
166
 
162
167
  def repository
163
- ElasticSearchFramework::Repository.new
168
+ @repository ||= ElasticSearchFramework::Repository.new
164
169
  end
165
170
 
166
171
  def get_item(id:, type: 'default')
@@ -0,0 +1,146 @@
1
+ module ElasticSearchFramework
2
+ module IndexAlias
3
+ def index(klass, active:)
4
+ unless instance_variable_defined?(:@elastic_search_indexes)
5
+ instance_variable_set(:@elastic_search_indexes, [])
6
+ end
7
+ indexes = self.instance_variable_get(:@elastic_search_indexes)
8
+ indexes << {klass: klass, active: active}
9
+ instance_variable_set(:@elastic_search_indexes, indexes)
10
+ end
11
+
12
+ def indexes
13
+ self.instance_variable_get(:@elastic_search_indexes)
14
+ end
15
+
16
+ def name(name)
17
+ unless instance_variable_defined?(:@elastic_search_index_alias_name)
18
+ instance_variable_set(:@elastic_search_index_alias_name, "#{name}")
19
+ else
20
+ raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Duplicate index alias name: #{name}.")
21
+ end
22
+ end
23
+
24
+ def valid?
25
+ indexes.select { |i| i[:active] == true }.length == 1
26
+ end
27
+
28
+ def create
29
+ if !valid?
30
+ raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Invalid Index alias.")
31
+ end
32
+
33
+ uri = URI("#{host}/_aliases")
34
+
35
+ payload = {
36
+ actions: [],
37
+ }
38
+
39
+ indexes.each do |index|
40
+ action = nil
41
+ if exists?(index: index[:klass])
42
+ action = "remove" if !index[:active]
43
+ else
44
+ action = "add" if index[:active]
45
+ end
46
+ next if action.nil?
47
+
48
+ payload[:actions] << {action => {index: index[:klass].full_name, alias: self.full_name}}
49
+ end
50
+
51
+ request = Net::HTTP::Post.new(uri.request_uri)
52
+ request.body = JSON.dump(payload)
53
+ request.content_type = "application/json"
54
+
55
+ response = repository.with_client do |client|
56
+ client.request(request)
57
+ end
58
+
59
+ is_valid_response?(response.code) || Integer(response.code) == 404
60
+ end
61
+
62
+ def delete
63
+ uri = URI("#{host}/_all/_aliases/#{full_name}")
64
+
65
+ request = Net::HTTP::Delete.new(uri.request_uri)
66
+
67
+ response = repository.with_client do |client|
68
+ client.request(request)
69
+ end
70
+
71
+ is_valid_response?(response.code) || Integer(response.code) == 404
72
+ end
73
+
74
+ def exists?(index:)
75
+ uri = URI("#{host}/#{index.full_name}/_alias/#{full_name}")
76
+
77
+ request = Net::HTTP::Get.new(uri.request_uri)
78
+
79
+ response = repository.with_client do |client|
80
+ client.request(request)
81
+ end
82
+
83
+ return false if response.code == "404"
84
+
85
+ result = nil
86
+ if is_valid_response?(response.code)
87
+ result = JSON.parse(response.body)
88
+ end
89
+
90
+ return true if !result.nil? && result[index.full_name]["aliases"] != nil
91
+ return false
92
+ end
93
+
94
+ def is_valid_response?(code)
95
+ [200, 201, 202].include?(Integer(code))
96
+ end
97
+
98
+ def full_name
99
+ name = instance_variable_get(:@elastic_search_index_alias_name)
100
+ if ElasticSearchFramework.namespace != nil
101
+ "#{ElasticSearchFramework.namespace}#{ElasticSearchFramework.namespace_delimiter}#{name.downcase}"
102
+ else
103
+ name.downcase
104
+ end
105
+ end
106
+
107
+ def description
108
+ index = indexes.last[:klass]
109
+ hash = index.instance_variable_get(:@elastic_search_index_def)
110
+ if index.instance_variable_defined?(:@elastic_search_index_id)
111
+ hash[:id] = index.instance_variable_get(:@elastic_search_index_id)
112
+ else
113
+ hash[:id] = :id
114
+ end
115
+ hash
116
+ end
117
+
118
+ def host
119
+ "#{ElasticSearchFramework.host}:#{ElasticSearchFramework.port}"
120
+ end
121
+
122
+ def repository
123
+ @repository ||= ElasticSearchFramework::Repository.new
124
+ end
125
+
126
+ def get_item(id:, type: "default")
127
+ repository.get(index: self, id: id, type: type)
128
+ end
129
+
130
+ def put_item(type: "default", item:)
131
+ indexes.each do |index|
132
+ repository.set(entity: item, index: index[:klass], type: type)
133
+ end
134
+ end
135
+
136
+ def delete_item(id:, type: "default")
137
+ indexes.each do |index|
138
+ repository.drop(index: index[:klass], id: id, type: type)
139
+ end
140
+ end
141
+
142
+ def query
143
+ ElasticSearchFramework::Query.new(index: self)
144
+ end
145
+ end
146
+ end
@@ -1,3 +1,3 @@
1
1
  module ElasticSearchFramework
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'
3
3
  end
@@ -0,0 +1,144 @@
1
+ RSpec.describe ElasticSearchFramework::Index do
2
+ describe '#full_name' do
3
+ let(:namespace) { 'uat' }
4
+ let(:namespace_delimiter) { '.' }
5
+ before do
6
+ ElasticSearchFramework.namespace = namespace
7
+ ElasticSearchFramework.namespace_delimiter = namespace_delimiter
8
+ end
9
+ it 'should return the full index name including namespace and delimiter' do
10
+ expect(ExampleIndexAlias.full_name).to eq "#{ElasticSearchFramework.namespace}#{ElasticSearchFramework.namespace_delimiter}example"
11
+ end
12
+
13
+ context 'when the namespace is nil' do
14
+ before { ElasticSearchFramework.namespace = nil }
15
+
16
+ it 'returns the description name downcased' do
17
+ expect(ExampleIndexAlias.full_name).to eq 'example'
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#valid?' do
23
+ context 'for a valid index definition' do
24
+ it 'should return true' do
25
+ expect(ExampleIndexAlias.valid?).to be true
26
+ end
27
+ end
28
+ context 'for an invalid index definition' do
29
+ it 'should return true' do
30
+ expect(InvalidIndexAlias.valid?).to be false
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#create' do
36
+ context 'when alias is valid and does not exist' do
37
+ before do
38
+ ExampleIndexAlias.delete
39
+ ExampleIndex.delete if ExampleIndex.exists?
40
+ ExampleIndex.create
41
+ end
42
+
43
+ it 'creates an alias for the active index' do
44
+ expect(ExampleIndexAlias.exists?(index: ExampleIndex)).to be false
45
+ ExampleIndexAlias.create
46
+ expect(ExampleIndexAlias.exists?(index: ExampleIndex)).to be true
47
+ end
48
+
49
+ after do
50
+ ExampleIndexAlias.delete
51
+ ExampleIndex.delete
52
+ end
53
+ end
54
+
55
+ context 'when alias is not valid' do
56
+ before { allow(ExampleIndexAlias).to receive(:valid?).and_return(false) }
57
+
58
+ it 'raises an error' do
59
+ expect(ExampleIndexAlias.exists?(index: ExampleIndex)).to be false
60
+ expect { ExampleIndexAlias.create }.to raise_error(
61
+ ElasticSearchFramework::Exceptions::IndexError
62
+ )
63
+ end
64
+ end
65
+
66
+ context 'when alias is valid but already exists' do
67
+ before do
68
+ ExampleIndex.delete if ExampleIndex.exists?
69
+ ExampleIndex.create
70
+ ExampleIndexAlias.create
71
+ end
72
+
73
+ it 'does not try to create a new alias' do
74
+ ExampleIndexAlias.create
75
+ end
76
+
77
+ after do
78
+ ExampleIndexAlias.delete
79
+ ExampleIndex.delete
80
+ end
81
+ end
82
+
83
+ context 'when alias is valid and does not exist and requires updating' do
84
+ before do
85
+ ExampleIndexAlias.delete
86
+ ExampleIndex.delete if ExampleIndex.exists?
87
+ ExampleIndex2.delete if ExampleIndex2.exists?
88
+ ExampleIndex.create
89
+ ExampleIndex2.create
90
+ ExampleIndexAlias.create
91
+ end
92
+
93
+ it 'modifies the alias to the active index' do
94
+ expect(ExampleIndexAlias.exists?(index: ExampleIndex)).to be true
95
+ ExampleIndexAlias2.create
96
+ expect(ExampleIndexAlias.exists?(index: ExampleIndex)).to be false
97
+ expect(ExampleIndexAlias2.exists?(index: ExampleIndex2)).to be true
98
+ end
99
+
100
+ after do
101
+ ExampleIndexAlias.delete
102
+ ExampleIndex.delete
103
+ ExampleIndex2.delete
104
+ end
105
+ end
106
+ end
107
+
108
+ describe '#get_item' do
109
+ let(:id) { 10 }
110
+ let(:type) { 'default' }
111
+ it 'should call get on the repository' do
112
+ expect(ExampleIndexAlias.repository).to receive(:get).with(index: ExampleIndexAlias, id: id, type: type).once
113
+ ExampleIndexAlias.get_item(id: id, type: type)
114
+ end
115
+ end
116
+
117
+ describe '#put_item' do
118
+ let(:id) { 10 }
119
+ let(:type) { 'default' }
120
+ let(:item) do
121
+ TestItem.new.tap do |i|
122
+ i.id = id
123
+ i.name = 'abc'
124
+ i.timestamp = Time.now.to_i
125
+ i.number = 5
126
+ end
127
+ end
128
+ it 'should call set on the repository for each index of the alias' do
129
+ expect(ExampleIndexAlias.repository).to receive(:set).with(entity: item, index: ExampleIndex, type: type).once
130
+ expect(ExampleIndexAlias.repository).to receive(:set).with(entity: item, index: ExampleIndex2, type: type).once
131
+ ExampleIndexAlias.put_item(type: type, item: item)
132
+ end
133
+ end
134
+
135
+ describe '#delete_item' do
136
+ let(:id) { 10 }
137
+ let(:type) { 'default' }
138
+ it 'should call drop on the repository for each index of the alias' do
139
+ expect(ExampleIndexAlias.repository).to receive(:drop).with(index: ExampleIndex, id: id, type: type).once
140
+ expect(ExampleIndexAlias.repository).to receive(:drop).with(index: ExampleIndex2, id: id, type: type).once
141
+ ExampleIndexAlias.delete_item(id: id, type: type)
142
+ end
143
+ end
144
+ end
@@ -83,13 +83,13 @@ RSpec.describe ElasticSearchFramework::Index do
83
83
  'type' => 'custom'
84
84
  }
85
85
  },
86
- 'analyzer'=>{
87
- 'custom_analyzer'=>{
88
- 'filter'=>['lowercase'],
89
- 'type'=>'custom',
90
- 'tokenizer'=>'standard'
91
- }
86
+ 'analyzer' => {
87
+ 'custom_analyzer' => {
88
+ 'filter' => ['lowercase'],
89
+ 'type' => 'custom',
90
+ 'tokenizer' => 'standard'
92
91
  }
92
+ }
93
93
  }
94
94
  end
95
95
 
@@ -276,8 +276,8 @@ RSpec.describe ElasticSearchFramework::Index do
276
276
  it 'should return a ElasticSearchFramework::Repository instance' do
277
277
  expect(ExampleIndex.repository).to be_a(ElasticSearchFramework::Repository)
278
278
  end
279
- it 'should return a unique ElasticSearchFramework::Repository instance' do
280
- expect(ExampleIndex.repository).not_to eq ExampleIndex.repository
279
+ it 'should return the same ElasticSearchFramework::Repository instance for multiple calls' do
280
+ expect(ExampleIndex.repository).to eq ExampleIndex.repository
281
281
  end
282
282
  end
283
283
 
@@ -0,0 +1,31 @@
1
+ class ExampleIndex2
2
+ extend ElasticSearchFramework::Index
3
+
4
+ index name: 'example_index', version: 2
5
+
6
+ mapping name: 'default', field: :name, type: :keyword, index: true
7
+ end
8
+
9
+ class ExampleIndexWithId2
10
+ extend ElasticSearchFramework::Index
11
+
12
+ index name: 'example_index', version: 2
13
+
14
+ id :number
15
+
16
+ mapping name: 'default', field: :name, type: :keyword, index: true
17
+ end
18
+
19
+ class ExampleIndexWithSettings2
20
+ extend ElasticSearchFramework::Index
21
+
22
+ index name: 'example_index', version: 2
23
+
24
+ normalizer_value = { custom_normalizer: { type: 'custom', char_filter: [], filter: ['lowercase'] } }
25
+ analyzer_value = { custom_analyzer: { type: 'custom', tokenizer: 'standard', filter: %w(lowercase) } }
26
+
27
+ settings name: :number_of_shards, value: 1
28
+ settings name: :analysis, type: :normalizer, value: normalizer_value
29
+ settings name: :analysis, type: :analyzer, value: analyzer_value
30
+ mapping name: 'default', field: :name, type: :keyword, index: true
31
+ end
@@ -0,0 +1,17 @@
1
+ class ExampleIndexAlias
2
+ extend ElasticSearchFramework::IndexAlias
3
+
4
+ index ExampleIndex, active: true
5
+ index ExampleIndex2, active: false
6
+
7
+ name :example
8
+ end
9
+
10
+ class InvalidIndexAlias
11
+ extend ElasticSearchFramework::IndexAlias
12
+
13
+ index ExampleIndex, active: false
14
+ index ExampleIndex2, active: false
15
+
16
+ name :example
17
+ end
@@ -0,0 +1,8 @@
1
+ class ExampleIndexAlias2
2
+ extend ElasticSearchFramework::IndexAlias
3
+
4
+ index ExampleIndex, active: false
5
+ index ExampleIndex2, active: true
6
+
7
+ name :example
8
+ end
@@ -9,6 +9,9 @@ end
9
9
  require 'elastic_search_framework'
10
10
  require_relative '../spec/test_item.rb'
11
11
  require_relative '../spec/example_index'
12
+ require_relative '../spec/example_index_2'
13
+ require_relative '../spec/example_index_alias'
14
+ require_relative '../spec/example_index_alias_2'
12
15
  require 'pry'
13
16
 
14
17
  RSpec.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic_search_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - vaughanbrittonsage
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-17 00:00:00.000000000 Z
11
+ date: 2019-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -119,14 +119,19 @@ files:
119
119
  - lib/elastic_search_framework/exceptions.rb
120
120
  - lib/elastic_search_framework/exceptions/index_error.rb
121
121
  - lib/elastic_search_framework/index.rb
122
+ - lib/elastic_search_framework/index_alias.rb
122
123
  - lib/elastic_search_framework/logger.rb
123
124
  - lib/elastic_search_framework/query.rb
124
125
  - lib/elastic_search_framework/repository.rb
125
126
  - lib/elastic_search_framework/version.rb
127
+ - spec/elastic_search_framework/index_alias_spec.rb
126
128
  - spec/elastic_search_framework/index_spec.rb
127
129
  - spec/elastic_search_framework/query_spec.rb
128
130
  - spec/elastic_search_framework/repository_spec.rb
129
131
  - spec/example_index.rb
132
+ - spec/example_index_2.rb
133
+ - spec/example_index_alias.rb
134
+ - spec/example_index_alias_2.rb
130
135
  - spec/spec_helper.rb
131
136
  - spec/test_item.rb
132
137
  homepage: https://github.com/sage/elastic_search_framework