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 +4 -4
- data/lib/elastic_search_framework.rb +1 -0
- data/lib/elastic_search_framework/index.rb +8 -3
- data/lib/elastic_search_framework/index_alias.rb +146 -0
- data/lib/elastic_search_framework/version.rb +1 -1
- data/spec/elastic_search_framework/index_alias_spec.rb +144 -0
- data/spec/elastic_search_framework/index_spec.rb +8 -8
- data/spec/example_index_2.rb +31 -0
- data/spec/example_index_alias.rb +17 -0
- data/spec/example_index_alias_2.rb +8 -0
- data/spec/spec_helper.rb +3 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcc1601dc419b4ad4233065024791ad1199077c2622d243ef4192780eefae4aa
|
4
|
+
data.tar.gz: a66d974b4015cfc9fe4c16022ff5123aceaa147ecda5288b865d8ba7cdab3d88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
@@ -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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
280
|
-
expect(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
|
data/spec/spec_helper.rb
CHANGED
@@ -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.
|
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-
|
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
|