elastics 0.1.1 → 0.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/.rspec +1 -0
- data/.travis.yml +7 -0
- data/README.md +80 -9
- data/Rakefile +3 -8
- data/elastics.gemspec +4 -1
- data/lib/elastics.rb +11 -0
- data/lib/elastics/active_record.rb +19 -4
- data/lib/elastics/active_record/helper_methods.rb +27 -26
- data/lib/elastics/active_record/instrumentation.rb +63 -7
- data/lib/elastics/active_record/model_schema.rb +22 -17
- data/lib/elastics/active_record/search_result.rb +49 -0
- data/lib/elastics/active_record/tasks_config.rb +25 -0
- data/lib/elastics/capistrano.rb +14 -0
- data/lib/elastics/client.rb +49 -22
- data/lib/elastics/client/cluster.rb +70 -0
- data/lib/elastics/railtie.rb +5 -4
- data/lib/elastics/tasks.rb +20 -19
- data/lib/elastics/tasks/config.rb +38 -0
- data/lib/elastics/tasks/indices.rb +72 -10
- data/lib/elastics/tasks/mappings.rb +11 -5
- data/lib/elastics/tasks/migrations.rb +42 -0
- data/lib/elastics/version.rb +1 -1
- data/lib/elastics/version_manager.rb +86 -0
- data/lib/tasks/elastics.rake +31 -8
- data/spec/lib/elastics/client/cluster_spec.rb +108 -0
- data/spec/spec_helper.rb +9 -0
- metadata +62 -7
- data/lib/elastics/active_record/log_subscriber.rb +0 -27
@@ -7,11 +7,17 @@ module Elastics
|
|
7
7
|
@mappings_paths ||= base_paths.map { |x| File.join x, 'mappings' }
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
# Mappings to put can be filtered with `:indices` & `:types` arrays.
|
11
|
+
def put_mappings(options = {})
|
12
|
+
version = options.fetch :version, :current
|
13
|
+
filter = options[:indices].try!(:map, &:to_s)
|
14
|
+
each_filtered(types, options[:types]) do |type|
|
12
15
|
index = index_for_type(type)
|
13
|
-
|
14
|
-
|
16
|
+
next if filter && !filter.include?(index)
|
17
|
+
versioned_index = versioned_index_name(index, version)
|
18
|
+
log "Putting mapping #{index}/#{type} (#{versioned_index}/#{type})"
|
19
|
+
client.put_mapping index: versioned_index, type: type,
|
20
|
+
data: mappings[type]
|
15
21
|
end
|
16
22
|
end
|
17
23
|
|
@@ -33,7 +39,7 @@ module Elastics
|
|
33
39
|
end
|
34
40
|
|
35
41
|
def index_for_type(type)
|
36
|
-
config[:index] ||
|
42
|
+
config[:index] || type
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Tasks
|
3
|
+
module Migrations
|
4
|
+
def migrate(options = {})
|
5
|
+
create_indices(options)
|
6
|
+
put_mappings(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def migrate!(options = {})
|
10
|
+
options_next = options.merge version: :next
|
11
|
+
drop_indices(options_next)
|
12
|
+
create_indices(options_next)
|
13
|
+
put_mappings(options_next)
|
14
|
+
reindex(options_next) if options.fetch(:reindex, true)
|
15
|
+
forward_aliases(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reindex(options = {})
|
19
|
+
version = options.fetch(:version, :current)
|
20
|
+
Rails.application.eager_load! if defined?(Rails)
|
21
|
+
VersionManager.use_version version do
|
22
|
+
models_to_reindex(options).each do |model|
|
23
|
+
log "Reindexing #{model.elastics_index_base} into " \
|
24
|
+
"#{model.elastics_index_name}/#{model.elastics_type_name}"
|
25
|
+
model.reindex_elastics
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def models_to_reindex(options = {})
|
31
|
+
indices = options[:indices].try!(:map, &:to_s)
|
32
|
+
types = options[:types].try!(:map, &:to_s)
|
33
|
+
models = Elastics.models.select do |model|
|
34
|
+
next if indices && !indices.include?(model.elastics_index_base)
|
35
|
+
next if types && !types.include?(model.elastics_type_name)
|
36
|
+
true
|
37
|
+
end
|
38
|
+
models.reject { |model| models.find { |other| model < other } }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/elastics/version.rb
CHANGED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Elastics
|
2
|
+
class VersionManager
|
3
|
+
class << self
|
4
|
+
def default_version
|
5
|
+
@default_version = ENV['ELASTICS_MAPPING_VERSION'] unless defined?(@default_version)
|
6
|
+
@default_version
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_version=(version)
|
10
|
+
@default_version = version
|
11
|
+
Elastics.models.each &:reset_elastics_index_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def use_version(version)
|
15
|
+
old_version = default_version
|
16
|
+
self.default_version = version
|
17
|
+
yield
|
18
|
+
ensure
|
19
|
+
self.default_version = old_version
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :service_index
|
24
|
+
|
25
|
+
def initialize(client, options = {})
|
26
|
+
@service_index = options[:service_index] || '.elastics'
|
27
|
+
@index_prefix = options[:index_prefix]
|
28
|
+
@client = client
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset
|
32
|
+
@versions = nil
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def update(index, data)
|
37
|
+
set index, versions[index].merge(data)
|
38
|
+
end
|
39
|
+
|
40
|
+
def set(index, data)
|
41
|
+
@client.post index: @service_index, type: :mapping_versions,
|
42
|
+
id: prefixed_index(index), data: data
|
43
|
+
@versions[index] = data.with_indifferent_access
|
44
|
+
end
|
45
|
+
|
46
|
+
def versions
|
47
|
+
@versions ||= Hash.new do |hash, index|
|
48
|
+
result = @client.get index: @service_index, type: :mapping_versions,
|
49
|
+
id: prefixed_index(index)
|
50
|
+
if result
|
51
|
+
hash[index] = result['_source'].with_indifferent_access
|
52
|
+
else
|
53
|
+
set index, current: 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def current_version(index)
|
59
|
+
versions[index][:current]
|
60
|
+
end
|
61
|
+
|
62
|
+
def next_version(index)
|
63
|
+
current_version(index) + 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def version_number(index, version)
|
67
|
+
case version.to_s
|
68
|
+
when 'current' then current_version(index)
|
69
|
+
when 'next' then next_version(index)
|
70
|
+
else raise NameError, "Invalid version alias: #{version}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def prefixed_index(index)
|
75
|
+
"#{@index_prefix}#{index}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def index_name(index, version = self.class.default_version)
|
79
|
+
if version && version != :alias
|
80
|
+
"#{prefixed_index(index)}-v#{version_number index, version}"
|
81
|
+
else
|
82
|
+
prefixed_index(index)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/tasks/elastics.rake
CHANGED
@@ -1,20 +1,43 @@
|
|
1
1
|
namespace 'elastics' do
|
2
|
-
task load_config
|
2
|
+
task :load_config do |task, args|
|
3
|
+
[:environment, 'db:load_config'].each do |dep|
|
4
|
+
Rake::Task[dep].invoke if Rake::Task.task_defined?(dep)
|
5
|
+
end
|
6
|
+
@elastics_options = {
|
7
|
+
version: ENV['version'] || :current,
|
8
|
+
reindex: !ENV.key?('no_reindex'),
|
9
|
+
drop: !ENV.key?('no_drop'),
|
10
|
+
types: ENV['types'] && ENV['types'].split(',').map(&:strip),
|
11
|
+
indices: args.extras.empty? ? nil : args.extras
|
12
|
+
}
|
3
13
|
end
|
4
14
|
|
5
|
-
desc '
|
6
|
-
task
|
7
|
-
|
8
|
-
Elastics::Tasks.migrate(flush: flush)
|
15
|
+
desc 'Drop administrative index'
|
16
|
+
task :purge, [:keep_data] => :load_config do |task, args|
|
17
|
+
Elastics::Tasks.purge args[:keep_data]
|
9
18
|
end
|
10
19
|
|
11
20
|
desc 'Creates indices'
|
12
21
|
task create: :load_config do
|
13
|
-
Elastics::Tasks.create_indices
|
22
|
+
Elastics::Tasks.create_indices @elastics_options
|
14
23
|
end
|
15
24
|
|
16
25
|
desc 'Drops indices'
|
17
|
-
task
|
18
|
-
Elastics::Tasks.
|
26
|
+
task drop: :load_config do
|
27
|
+
Elastics::Tasks.drop_indices @elastics_options
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Creates indices and applies mappings. Full migration when param is present'
|
31
|
+
task migrate: :load_config do
|
32
|
+
if ENV['full']
|
33
|
+
Elastics::Tasks.migrate! @elastics_options
|
34
|
+
else
|
35
|
+
Elastics::Tasks.migrate @elastics_options
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Reindex'
|
40
|
+
task reindex: :load_config do
|
41
|
+
Elastics::Tasks.reindex @elastics_options
|
19
42
|
end
|
20
43
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Elastics::Client do
|
4
|
+
let(:client) { described_class.new(config) }
|
5
|
+
let(:config) { {
|
6
|
+
host: hosts,
|
7
|
+
resurrect_timeout: resurrect_timeout,
|
8
|
+
} }
|
9
|
+
let(:hosts) { [:one, :two, :three] }
|
10
|
+
let(:resurrect_timeout) { 5 }
|
11
|
+
|
12
|
+
describe '#next_cluster_host' do
|
13
|
+
subject { -> { client.send(:next_cluster_host) } }
|
14
|
+
context 'when @hosts contains one element' do
|
15
|
+
let(:hosts) { [:one] }
|
16
|
+
|
17
|
+
it 'returns this host on each call' do
|
18
|
+
expect(6.times.map { subject.call }).to eq([:one] * 6)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when @hosts contains multiple elements' do
|
23
|
+
it 'returns host using round-robbin' do
|
24
|
+
expect(6.times.map { subject.call }).to eq(hosts.cycle.take(6))
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'and one host is dead' do
|
28
|
+
before { client.send(:add_dead_host, :one, resurrect_at) }
|
29
|
+
let(:resurrect_at) {}
|
30
|
+
|
31
|
+
it 'returns other hosts using round-robbin' do
|
32
|
+
expect(6.times.map { subject.call }).to eq((hosts - [:one]).cycle.take(6))
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'but should be resurrected' do
|
36
|
+
let(:resurrect_at) { 0 }
|
37
|
+
|
38
|
+
it 'resurrects this host' do
|
39
|
+
expect(6.times.map { subject.call }).to eq(hosts.cycle.take(7).drop(1))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'and all hosts are dead' do
|
45
|
+
before { hosts.each { |host| client.send(:add_dead_host, host) } }
|
46
|
+
it { should raise_error(Elastics::Client::Cluster::NoAliveHosts) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#resurrect_cluster' do
|
52
|
+
subject { -> { client.send(:resurrect_cluster) } }
|
53
|
+
context 'when multiple hosts are blocked' do
|
54
|
+
before { hosts.each { |host| client.send(:add_dead_host, host) } }
|
55
|
+
|
56
|
+
it { should_not change { client.instance_variable_get(:@hosts) }.from([]) }
|
57
|
+
|
58
|
+
context 'and can be resurrected' do
|
59
|
+
before { expect(Time).to receive(:now).and_return(resurrect_timeout.seconds.from_now) }
|
60
|
+
it { should change { client.instance_variable_get(:@hosts) }.from([]).to(hosts) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#http_request' do
|
66
|
+
subject { -> { client.send :http_request, :method, '/path', :query, :body } }
|
67
|
+
|
68
|
+
context 'when all hosts are alive' do
|
69
|
+
it 'uses hosts using round-robbin' do
|
70
|
+
hosts.cycle.take(6).each do |host|
|
71
|
+
expect(client.client).to receive(:request).
|
72
|
+
with(:method, "http://#{host}/path", :query, :body, Elastics::Client::HEADERS)
|
73
|
+
subject.call
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when one host is unreachable' do
|
79
|
+
before do
|
80
|
+
expect(client.client).to receive(:request).
|
81
|
+
with(:method, "http://one/path", :query, :body, Elastics::Client::HEADERS) do
|
82
|
+
raise Timeout::Error
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'uses hosts using round-robbin' do
|
87
|
+
(hosts - [:one]).cycle.take(6).each do |host|
|
88
|
+
expect(client.client).to receive(:request).
|
89
|
+
with(:method, "http://#{host}/path", :query, :body, Elastics::Client::HEADERS)
|
90
|
+
end
|
91
|
+
6.times { subject.call }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when all hosts are unreachable' do
|
96
|
+
before do
|
97
|
+
hosts.each do |host|
|
98
|
+
expect(client.client).to receive(:request).
|
99
|
+
with(:method, "http://#{host}/path", :query, :body, Elastics::Client::HEADERS) do
|
100
|
+
raise Timeout::Error
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it { should raise_error(Elastics::Client::Cluster::NoAliveHosts) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max Melentiev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|
@@ -44,14 +44,56 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '10'
|
47
|
+
version: '10.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '10'
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.1.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.1.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thread_safe
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.3.4
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.3.4
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.1.6
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.1.6
|
55
97
|
description: Lightweight and extensible elasticsearch client
|
56
98
|
email:
|
57
99
|
- melentievm@gmail.com
|
@@ -60,6 +102,8 @@ extensions: []
|
|
60
102
|
extra_rdoc_files: []
|
61
103
|
files:
|
62
104
|
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
63
107
|
- Gemfile
|
64
108
|
- README.md
|
65
109
|
- Rakefile
|
@@ -68,16 +112,24 @@ files:
|
|
68
112
|
- lib/elastics/active_record.rb
|
69
113
|
- lib/elastics/active_record/helper_methods.rb
|
70
114
|
- lib/elastics/active_record/instrumentation.rb
|
71
|
-
- lib/elastics/active_record/log_subscriber.rb
|
72
115
|
- lib/elastics/active_record/model_schema.rb
|
116
|
+
- lib/elastics/active_record/search_result.rb
|
117
|
+
- lib/elastics/active_record/tasks_config.rb
|
118
|
+
- lib/elastics/capistrano.rb
|
73
119
|
- lib/elastics/client.rb
|
120
|
+
- lib/elastics/client/cluster.rb
|
74
121
|
- lib/elastics/query_helper.rb
|
75
122
|
- lib/elastics/railtie.rb
|
76
123
|
- lib/elastics/tasks.rb
|
124
|
+
- lib/elastics/tasks/config.rb
|
77
125
|
- lib/elastics/tasks/indices.rb
|
78
126
|
- lib/elastics/tasks/mappings.rb
|
127
|
+
- lib/elastics/tasks/migrations.rb
|
79
128
|
- lib/elastics/version.rb
|
129
|
+
- lib/elastics/version_manager.rb
|
80
130
|
- lib/tasks/elastics.rake
|
131
|
+
- spec/lib/elastics/client/cluster_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
81
133
|
homepage: http://github.com/printercu/elastics-rb
|
82
134
|
licenses:
|
83
135
|
- MIT
|
@@ -98,8 +150,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
150
|
version: '0'
|
99
151
|
requirements: []
|
100
152
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.
|
153
|
+
rubygems_version: 2.4.2
|
102
154
|
signing_key:
|
103
155
|
specification_version: 4
|
104
156
|
summary: ElasticSearch client with ActiveRecord integration
|
105
|
-
test_files:
|
157
|
+
test_files:
|
158
|
+
- spec/lib/elastics/client/cluster_spec.rb
|
159
|
+
- spec/spec_helper.rb
|
160
|
+
has_rdoc:
|