elastic_search_framework 2.1.0 → 2.2.0

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