es-reindex 0.1.0 → 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/.travis.yml +8 -0
- data/README.markdown +44 -7
- data/Rakefile +7 -0
- data/bin/es-reindex +3 -1
- data/es-reindex.gemspec +9 -6
- data/lib/es-reindex.rb +141 -102
- data/lib/es-reindex/railtie.rb +7 -0
- data/lib/es-reindex/version.rb +1 -1
- data/spec/es-reindex_spec.rb +31 -1
- data/spec/integration/copy_spec.rb +50 -0
- data/spec/integration/reindex_spec.rb +31 -0
- data/spec/spec_helper.rb +16 -1
- data/spec/support/test_model.rb +20 -0
- metadata +40 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eed6bf0fb22e4ef0644bc07f8044707489dcac19
|
4
|
+
data.tar.gz: 785f245811cf514f8ccd50fb5bd6b24d91e65b81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6504ac4e4e7b46ba4e00c15bf2d90889e688479389d9ec399e571d67a709caa3324fb65f6b57d38c7f6558dd85acd9a017ebbea3855fb63ef71e40454b43359f
|
7
|
+
data.tar.gz: c01e98b13e0edcd1c6075827b2236e4666eb5759772c79e60722cfe8bde36062776df149feeaf18c8acc1a15cc032cd0157aeea14d3f14410fd0b6ac3c0ac8a4
|
data/.travis.yml
ADDED
data/README.markdown
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](https://travis-ci.org/mojolingo/es-reindex)
|
2
|
+
|
1
3
|
# es-reindex - simple ruby gem for copying ElasticSearch index
|
2
4
|
|
3
5
|
Simple ruby gem to copy and reindex ElasticSearch index,
|
@@ -7,12 +9,9 @@ Progress and time estimation is displayed during the scrolling process.
|
|
7
9
|
|
8
10
|
## Requirements
|
9
11
|
|
10
|
-
Ruby 1.9.3 or newer
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
+ [rest-client](https://github.com/archiloque/rest-client)
|
15
|
-
+ [multi_json](https://github.com/intridea/multi_json)
|
12
|
+
- Ruby 1.9.3 or newer
|
13
|
+
- Gems:
|
14
|
+
- [elasticsearch](https://github.com/elasticsearch/elasticsearch-ruby)
|
16
15
|
|
17
16
|
## Usage (command line)
|
18
17
|
|
@@ -38,6 +37,8 @@ Usage:
|
|
38
37
|
|
39
38
|
You can also use it as a PORO:
|
40
39
|
|
40
|
+
#### To Copy
|
41
|
+
|
41
42
|
```ruby
|
42
43
|
# Options:
|
43
44
|
# remove: same as -r
|
@@ -49,11 +50,36 @@ options = {
|
|
49
50
|
update: true
|
50
51
|
}
|
51
52
|
|
52
|
-
ESReindex.
|
53
|
+
ESReindex.copy! 'http://my_server/index', 'http://my_server/index_copy', options
|
54
|
+
```
|
55
|
+
|
56
|
+
#### To Reindex
|
57
|
+
|
58
|
+
If you want to reindex the destination from the source without copying the mappings/settings from the source, you can do it as such:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
ESReindex.reindex! 'http://my_server/index', 'http://my_server/index_copy',
|
62
|
+
mappings: -> { set_of_mappings },
|
63
|
+
settings: -> { set_of_settings}
|
64
|
+
```
|
65
|
+
|
66
|
+
If using the `.reindex!` method, you MUST pass valid mappings/settings in via the options.
|
67
|
+
|
68
|
+
#### Callbacks
|
69
|
+
There also a set of callbacks you can use:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
ESReindex.copy! 'http://my_server/index', 'http://my_server/index_copy',
|
73
|
+
before_create: -> { do_something }, # Runs before the (re)creation of the destination index
|
74
|
+
after_create: -> { do_something_else }, # Runs after the (re)creation of the destinatino index
|
75
|
+
before_each: ->doc { use_the doc }, # Runs before each document is copied
|
76
|
+
after_each: ->doc { foo_bar doc }, # Runs after each document is copied
|
77
|
+
after_copy: -> { finish_thing } # Runs after everything is copied over
|
53
78
|
```
|
54
79
|
|
55
80
|
## Changelog
|
56
81
|
|
82
|
+
+ __0.2.0__: Lots of bugfixes, use elasticsearch client gem, add .reindex! method and callbacks
|
57
83
|
+ __0.1.0__: First gem release
|
58
84
|
+ __0.0.9__: Gemification, Oj -> MultiJSON
|
59
85
|
+ __0.0.8__: Optimization in string concat (@nara)
|
@@ -65,6 +91,17 @@ ESReindex.new('http://my_server/index', 'http://my_server/index_copy', options).
|
|
65
91
|
+ __0.0.2__: repated document count comparison
|
66
92
|
+ __0.0.1__: first revision
|
67
93
|
|
94
|
+
## Credits
|
95
|
+
|
96
|
+
- [Justin Aiken](https://github.com/JustinAiken)
|
97
|
+
- [Victor Luft](https://github.com/victorluft)
|
98
|
+
|
99
|
+
Original script:
|
100
|
+
- @geronime
|
101
|
+
- @pgaertig
|
102
|
+
|
103
|
+
Developed by [Mojo Lingo](http://mojolingo.com).
|
104
|
+
|
68
105
|
## License
|
69
106
|
es-reindex the gem is copyright (c)2014 Mojo Lingo, and released under the terms
|
70
107
|
of the MIT license. See the LICENSE file for the gory details.
|
data/Rakefile
ADDED
data/bin/es-reindex
CHANGED
@@ -24,4 +24,6 @@ if ARGV.size == 0 or ARGV[0] =~ /^-(?:h|-?help)$/
|
|
24
24
|
end
|
25
25
|
|
26
26
|
src, dst, options = ESReindex::ArgsParser.parse(ARGV)
|
27
|
-
ESReindex.new(
|
27
|
+
ESReindex.logger = Logger.new(STDOUT)
|
28
|
+
ESReindex.logger.level = Logger::INFO
|
29
|
+
ESReindex.copy! src, dst, options.merge(from_cli: true)
|
data/es-reindex.gemspec
CHANGED
@@ -6,8 +6,8 @@ require "es-reindex/version"
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "es-reindex"
|
8
8
|
s.version = ESReindex::VERSION
|
9
|
-
s.authors = ["Justin Aiken"]
|
10
|
-
s.email = ["jaiken@mojolingo.com"]
|
9
|
+
s.authors = ["Justin Aiken", "Victor Luft"]
|
10
|
+
s.email = ["jaiken@mojolingo.com", "vluft@mojolingo.com"]
|
11
11
|
s.homepage = "https://github.com/mojolingo/es-reindex"
|
12
12
|
s.summary = %q{Ruby gem to copy ElasticSearch index (reindex).}
|
13
13
|
s.description = %q{Ruby gem to copy ElasticSearch index (reindex).}
|
@@ -22,12 +22,15 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
23
|
s.require_paths = ["lib"]
|
24
24
|
|
25
|
-
s.add_runtime_dependency '
|
26
|
-
s.add_runtime_dependency 'multi_json'
|
25
|
+
s.add_runtime_dependency 'elasticsearch', '>= 1.0.0'
|
27
26
|
|
28
|
-
|
27
|
+
# Development Dependencies:
|
28
|
+
s.add_development_dependency 'elasticsearch-persistence', '~> 0.1'
|
29
29
|
s.add_development_dependency %q<bundler>, ["~> 1.0"]
|
30
|
-
s.add_development_dependency %q<rspec>, ["~> 2.99"]
|
31
30
|
s.add_development_dependency %q<rake>, [">= 0"]
|
31
|
+
|
32
|
+
# Testing Dependencies:
|
33
|
+
s.add_development_dependency %q<coveralls>, ['>= 0']
|
34
|
+
s.add_development_dependency %q<rspec>, ["~> 2.99"]
|
32
35
|
s.add_development_dependency %q<guard-rspec>, ['~> 4.5']
|
33
36
|
end
|
data/lib/es-reindex.rb
CHANGED
@@ -1,29 +1,46 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'elasticsearch'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'es-reindex/railtie' if defined?(Rails)
|
3
5
|
|
4
6
|
class ESReindex
|
5
7
|
|
6
8
|
DEFAULT_URL = 'http://10.203.175.32:9200'
|
7
9
|
|
8
|
-
attr_accessor :src, :dst, :options, :start_time, :done
|
10
|
+
attr_accessor :src, :dst, :options, :surl, :durl, :sidx, :didx, :sclient, :dclient, :start_time, :done, :settings, :mappings
|
11
|
+
|
12
|
+
def self.copy!(src, dst, options = {})
|
13
|
+
self.new(src, dst, options).tap do |reindexer|
|
14
|
+
reindexer.setup_index_urls
|
15
|
+
reindexer.copy!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.reindex!(src, dst, options={})
|
20
|
+
self.new(src, dst, options.merge(copy_mappings: false)).tap do |reindexer|
|
21
|
+
reindexer.setup_index_urls
|
22
|
+
reindexer.copy!
|
23
|
+
end
|
24
|
+
end
|
9
25
|
|
10
26
|
def initialize(src, dst, options = {})
|
27
|
+
ESReindex.logger ||= Logger.new(STDERR)
|
28
|
+
|
11
29
|
@src = src || ''
|
12
30
|
@dst = dst || ''
|
13
31
|
@options = {
|
32
|
+
from_cli: false, # Coming from CLI?
|
14
33
|
remove: false, # remove the index in the new location first
|
15
34
|
update: false, # update existing documents (default: only create non-existing)
|
16
|
-
frame: 1000
|
35
|
+
frame: 1000, # specify frame size to be obtained with one fetch during scrolling
|
36
|
+
copy_mappings: true # Copy old mappings/settings
|
17
37
|
}.merge! options
|
18
38
|
|
19
39
|
@done = 0
|
20
40
|
end
|
21
41
|
|
22
|
-
def
|
23
|
-
|
24
|
-
MultiJson.dump_options = {mode: :compat}
|
25
|
-
|
26
|
-
surl, durl, sidx, didx = '', '', '', ''
|
42
|
+
def setup_index_urls
|
43
|
+
@surl, @durl, @sidx, @didx = '', '', '', ''
|
27
44
|
[[src, surl, sidx], [dst, durl, didx]].each do |param, url, idx|
|
28
45
|
if param =~ %r{^(.*)/(.*?)$}
|
29
46
|
url.replace $1
|
@@ -34,116 +51,148 @@ class ESReindex
|
|
34
51
|
end
|
35
52
|
end
|
36
53
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
54
|
+
@sclient = Elasticsearch::Client.new host: surl
|
55
|
+
@dclient = Elasticsearch::Client.new host: durl
|
56
|
+
end
|
57
|
+
|
58
|
+
def copy!
|
59
|
+
log "Copying '#{surl}/#{sidx}' to '#{durl}/#{didx}'#{remove? ? ' with rewriting destination mapping!' : update? ? ' with updating existing documents!' : '.'}"
|
60
|
+
confirm if from_cli?
|
61
|
+
|
62
|
+
success = (
|
63
|
+
clear_destination &&
|
64
|
+
create_destination &&
|
65
|
+
copy_docs &&
|
66
|
+
check_docs
|
67
|
+
)
|
68
|
+
|
69
|
+
if from_cli?
|
70
|
+
exit (success ? 0 : 1)
|
71
|
+
else
|
72
|
+
success
|
73
|
+
end
|
74
|
+
end
|
42
75
|
|
76
|
+
def confirm
|
77
|
+
printf "Confirm or hit Ctrl-c to abort...\n"
|
43
78
|
$stdin.readline
|
79
|
+
end
|
44
80
|
|
45
|
-
|
46
|
-
|
81
|
+
def clear_destination
|
82
|
+
dclient.indices.delete(index: didx) if remove? && dclient.indices.exists(index: didx)
|
83
|
+
true
|
84
|
+
rescue => e
|
85
|
+
false
|
86
|
+
end
|
47
87
|
|
48
|
-
|
49
|
-
unless
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
settings = MultiJson.load settings
|
56
|
-
sidx = settings.keys[0]
|
57
|
-
settings[sidx].delete 'index.version.created'
|
58
|
-
printf 'Creating \'%s/%s\' index with settings from \'%s/%s\'... ', durl, didx, surl, sidx
|
59
|
-
unless retried_request :post, "#{durl}/#{didx}", MultiJson.dump(settings[sidx])
|
60
|
-
puts 'FAILED!'
|
61
|
-
exit 1
|
88
|
+
def create_destination
|
89
|
+
unless dclient.indices.exists index: didx
|
90
|
+
if copy_mappings?
|
91
|
+
return false unless get_settings
|
92
|
+
return false unless get_mappings
|
93
|
+
create_msg = " with settings & mappings from '#{surl}/#{sidx}'"
|
62
94
|
else
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
warn "Failed to obtain original index '#{surl}/#{sidx}' mappings!"
|
67
|
-
exit 1
|
68
|
-
end
|
69
|
-
mappings = MultiJson.load mappings
|
70
|
-
mappings = mappings[sidx]
|
71
|
-
mappings = mappings['mappings'] if mappings.is_a?(Hash) && mappings.has_key?('mappings')
|
72
|
-
mappings.each_pair do |type, mapping|
|
73
|
-
printf 'Copying mapping \'%s/%s/%s\'... ', durl, didx, type
|
74
|
-
unless retried_request(:put, "#{durl}/#{didx}/#{type}/_mapping", MultiJson.dump(type => mapping))
|
75
|
-
puts 'FAILED!'
|
76
|
-
exit 1
|
77
|
-
else
|
78
|
-
puts 'OK.'
|
79
|
-
end
|
95
|
+
@mappings = options[:mappings].call
|
96
|
+
@settings = options[:settings].call
|
97
|
+
create_msg = ""
|
80
98
|
end
|
99
|
+
|
100
|
+
options[:before_create].call if options[:before_create].present?
|
101
|
+
|
102
|
+
log "Creating '#{durl}/#{didx}' index#{create_msg}..."
|
103
|
+
dclient.indices.create index: didx, body: { settings: settings, mappings: mappings }
|
104
|
+
log "Succesfully created '#{durl}/#{didx}''#{create_msg}."
|
105
|
+
|
106
|
+
options[:after_create].call if options[:after_create].present?
|
107
|
+
end
|
108
|
+
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_settings
|
113
|
+
unless settings = sclient.indices.get_settings(index: sidx)
|
114
|
+
log "Failed to obtain original index '#{surl}/#{sidx}' settings!"
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
|
118
|
+
@settings = settings[sidx]["settings"]
|
119
|
+
@settings["index"]["version"].delete "created"
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_mappings
|
123
|
+
unless mappings = sclient.indices.get_mapping(index: sidx)
|
124
|
+
log "Failed to obtain original index '#{surl}/#{sidx}' mappings!", :error
|
125
|
+
return false
|
81
126
|
end
|
127
|
+
@mappings = mappings[sidx]["mappings"]
|
128
|
+
end
|
82
129
|
|
83
|
-
|
130
|
+
def copy_docs
|
131
|
+
log "Copying '#{surl}/#{sidx}' to '#{durl}/#{didx}'..."
|
84
132
|
@start_time = Time.now
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
data = MultiJson.load data
|
98
|
-
break if data['hits']['hits'].empty?
|
99
|
-
scroll_id = data['_scroll_id']
|
100
|
-
bulk = ''
|
101
|
-
data['hits']['hits'].each do |doc|
|
133
|
+
|
134
|
+
scroll = sclient.search index: sidx, search_type: "scan", scroll: '10m', size: frame
|
135
|
+
scroll_id = scroll['_scroll_id']
|
136
|
+
total = scroll['hits']['total']
|
137
|
+
log "Copy progress: %u/%u (%.1f%%) done.\r" % [done, total, 0]
|
138
|
+
|
139
|
+
action = update? ? 'index' : 'create'
|
140
|
+
|
141
|
+
while scroll = sclient.scroll(scroll_id: scroll['_scroll_id'], scroll: '10m') and not scroll['hits']['hits'].empty? do
|
142
|
+
bulk = []
|
143
|
+
scroll['hits']['hits'].each do |doc|
|
144
|
+
options[:before_each].call doc if options[:before_each].present?
|
102
145
|
### === implement possible modifications to the document
|
103
146
|
### === end modifications to the document
|
104
|
-
base = {'_index' => didx, '_id' => doc['_id'], '_type' => doc['_type']}
|
105
|
-
|
106
|
-
base[doc_arg] = doc[doc_arg] if doc.key? doc_arg
|
107
|
-
}
|
108
|
-
bulk << MultiJson.dump(bulk_op => base) + "\n"
|
109
|
-
bulk << MultiJson.dump(doc['_source']) + "\n"
|
147
|
+
base = {'_index' => didx, '_id' => doc['_id'], '_type' => doc['_type'], data: doc['_source']}
|
148
|
+
bulk << {action => base}
|
110
149
|
@done = done + 1
|
150
|
+
options[:after_each].call doc if options[:after_each].present?
|
111
151
|
end
|
112
152
|
unless bulk.empty?
|
113
|
-
bulk
|
114
|
-
retried_request :post, "#{durl}/_bulk", bulk
|
153
|
+
dclient.bulk body: bulk
|
115
154
|
end
|
116
155
|
|
117
156
|
eta = total * (Time.now - start_time) / done
|
118
|
-
|
119
|
-
done, total, 100.0 * done / total, tm_len, start_time + eta
|
157
|
+
log "Copy progress: #{done}/#{total} (%.1f%%) done in #{tm_len}. E.T.A.: #{start_time + eta}." % (100.0 * done / total)
|
120
158
|
end
|
121
159
|
|
122
|
-
|
160
|
+
log "Copy progress: %u/%u done in %s.\n" % [done, total, tm_len]
|
161
|
+
|
162
|
+
options[:after_copy].call if options[:after_copy].present?
|
163
|
+
|
164
|
+
true
|
165
|
+
end
|
123
166
|
|
124
|
-
|
125
|
-
|
167
|
+
def check_docs
|
168
|
+
log 'Checking document count... '
|
126
169
|
scount, dcount = 1, 0
|
127
170
|
begin
|
128
171
|
Timeout::timeout(60) do
|
129
172
|
while true
|
130
|
-
scount =
|
131
|
-
dcount =
|
132
|
-
scount = MultiJson.load(scount)['count'].to_i
|
133
|
-
dcount = MultiJson.load(dcount)['count'].to_i
|
173
|
+
scount = sclient.count(index: sidx)["count"]
|
174
|
+
dcount = dclient.count(index: didx)["count"]
|
134
175
|
break if scount == dcount
|
135
176
|
sleep 1
|
136
177
|
end
|
137
178
|
end
|
138
179
|
rescue Timeout::Error
|
139
180
|
end
|
140
|
-
|
181
|
+
log "Document count: #{scount} = #{dcount} (#{scount == dcount ? 'equal' : 'NOT EQUAL'})"
|
182
|
+
|
183
|
+
scount == dcount
|
184
|
+
end
|
141
185
|
|
142
|
-
|
186
|
+
class << self
|
187
|
+
attr_accessor :logger
|
143
188
|
end
|
144
189
|
|
145
190
|
private
|
146
191
|
|
192
|
+
def log(msg, level = :info)
|
193
|
+
ESReindex.logger.send level, msg
|
194
|
+
end
|
195
|
+
|
147
196
|
def remove?
|
148
197
|
@options[:remove]
|
149
198
|
end
|
@@ -156,6 +205,14 @@ private
|
|
156
205
|
@options[:frame]
|
157
206
|
end
|
158
207
|
|
208
|
+
def from_cli?
|
209
|
+
@options[:from_cli]
|
210
|
+
end
|
211
|
+
|
212
|
+
def copy_mappings?
|
213
|
+
@options[:copy_mappings]
|
214
|
+
end
|
215
|
+
|
159
216
|
def tm_len
|
160
217
|
l = Time.now - @start_time
|
161
218
|
t = []
|
@@ -169,22 +226,4 @@ private
|
|
169
226
|
out
|
170
227
|
end
|
171
228
|
|
172
|
-
def retried_request(method, url, data=nil)
|
173
|
-
while true
|
174
|
-
begin
|
175
|
-
return data ?
|
176
|
-
RestClient.send(method, url, data) :
|
177
|
-
RestClient.send(method, url)
|
178
|
-
rescue RestClient::ResourceNotFound # no point to retry
|
179
|
-
return nil
|
180
|
-
rescue RestClient::BadRequest => e # Something's wrong!
|
181
|
-
warn "\n#{method.to_s.upcase} #{url} :-> ERROR: #{e.class} - #{e.message}"
|
182
|
-
warn e.response
|
183
|
-
return nil
|
184
|
-
rescue => e
|
185
|
-
warn "\nRetrying #{method.to_s.upcase} ERROR: #{e.class} - #{e.message}"
|
186
|
-
warn e.response
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
229
|
end
|
data/lib/es-reindex/version.rb
CHANGED
data/spec/es-reindex_spec.rb
CHANGED
@@ -8,12 +8,42 @@ describe ESReindex do
|
|
8
8
|
let(:reindexer) { ESReindex.new src, dst, options }
|
9
9
|
|
10
10
|
it "can be freshly initialized with options" do
|
11
|
-
expect(reindexer.options).to eq remove: false, update: true, frame: 1000
|
11
|
+
expect(reindexer.options).to eq remove: false, update: true, frame: 1000, from_cli: false, copy_mappings: true
|
12
12
|
end
|
13
13
|
|
14
14
|
it "starts with 0 indexes done" do
|
15
15
|
expect(reindexer.done).to eq 0
|
16
16
|
end
|
17
17
|
|
18
|
+
describe "#copy!" do
|
19
|
+
after { reindexer.copy! }
|
20
|
+
|
21
|
+
before do
|
22
|
+
%w{
|
23
|
+
confirm clear_destination create_destination copy_docs check_docs
|
24
|
+
}.each { |meth| reindexer.stub(meth).and_return true }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when run as a PORO" do
|
28
|
+
it "doesn't use #exit" do
|
29
|
+
expect(reindexer).to receive(:clear_destination).and_return false
|
30
|
+
expect(reindexer).not_to receive :exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when run from the CLI bin" do
|
35
|
+
let(:options) { {from_cli: true} }
|
36
|
+
|
37
|
+
it "exits 1 on failure" do
|
38
|
+
expect(reindexer).to receive(:clear_destination).and_return false
|
39
|
+
expect(reindexer).to receive(:exit).with 1
|
40
|
+
end
|
41
|
+
|
42
|
+
it "exits 0 on success" do
|
43
|
+
expect(reindexer).to receive(:exit).with 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
18
48
|
skip "it can actually do stuff"
|
19
49
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "copy!", type: :integration do
|
4
|
+
let(:orig_index_name) { "test_index" }
|
5
|
+
let(:new_index_name) { "test_index_clone" }
|
6
|
+
|
7
|
+
let!(:original_klass) { test_klass index_name: orig_index_name }
|
8
|
+
let!(:new_klass) { test_klass index_name: new_index_name }
|
9
|
+
|
10
|
+
let(:test_post) { original_klass.create id: 1, text: 'test_post' }
|
11
|
+
let(:test_post_2) { new_klass.create id: 2, text: 'other_post' }
|
12
|
+
|
13
|
+
# Create the index (test_index) on the test_klass:
|
14
|
+
before do
|
15
|
+
original_klass.create_index!
|
16
|
+
test_post
|
17
|
+
original_klass.refresh_index!
|
18
|
+
end
|
19
|
+
|
20
|
+
it "copies the index" do
|
21
|
+
ESReindex.copy! "#{ES_HOST}/#{orig_index_name}", "#{ES_HOST}/#{new_index_name}", {}
|
22
|
+
expect(new_klass.find test_post.id).to be_present
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the destination index already exists" do
|
26
|
+
|
27
|
+
# Create the index (test_index_clone) on the destination klass:
|
28
|
+
before do
|
29
|
+
new_klass.create_index!
|
30
|
+
test_post_2
|
31
|
+
new_klass.refresh_index!
|
32
|
+
end
|
33
|
+
|
34
|
+
it "merges it right on over" do
|
35
|
+
ESReindex.copy! "#{ES_HOST}/#{orig_index_name}", "#{ES_HOST}/#{new_index_name}", {}
|
36
|
+
expect(new_klass.find test_post.id).to be_present
|
37
|
+
expect(new_klass.find test_post_2.id).to be_present
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with the remove option" do
|
41
|
+
it "overwrites the destination index" do
|
42
|
+
ESReindex.copy! "#{ES_HOST}/#{orig_index_name}", "#{ES_HOST}/#{new_index_name}", {remove: true}
|
43
|
+
expect(new_klass.find test_post.id).to be_present
|
44
|
+
expect {
|
45
|
+
new_klass.find test_post_2.id
|
46
|
+
}.to raise_error Elasticsearch::Persistence::Repository::DocumentNotFound
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "reindex!", type: :integration do
|
4
|
+
let(:orig_index_name) { "test_index" }
|
5
|
+
let(:new_index_name) { "test_index_clone" }
|
6
|
+
|
7
|
+
let!(:original_klass) { test_klass index_name: orig_index_name }
|
8
|
+
let!(:new_klass) { test_klass index_name: new_index_name, attributes: [:foo] }
|
9
|
+
|
10
|
+
let(:test_post) { original_klass.create id: 1, text: 'test_post' }
|
11
|
+
let(:test_post_2) { new_klass.create id: 2, text: 'other_post' }
|
12
|
+
|
13
|
+
# Create the index (test_index) on the test_klass:
|
14
|
+
before do
|
15
|
+
original_klass.create_index!
|
16
|
+
test_post
|
17
|
+
original_klass.refresh_index!
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:reindexed_post) { new_klass.find test_post.id }
|
21
|
+
|
22
|
+
it "reindexes with the selected mappings" do
|
23
|
+
ESReindex.reindex! "#{ES_HOST}/#{orig_index_name}", "#{ES_HOST}/#{new_index_name}",
|
24
|
+
mappings: -> { new_klass.mappings },
|
25
|
+
settings: -> { new_klass.settings }
|
26
|
+
|
27
|
+
expect(reindexed_post.id).to eq test_post.id
|
28
|
+
expect(reindexed_post.text).to eq test_post.text
|
29
|
+
expect(reindexed_post).to respond_to :foo
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,10 +4,25 @@ Coveralls.wear!
|
|
4
4
|
require 'es-reindex'
|
5
5
|
require 'es-reindex/args_parser'
|
6
6
|
|
7
|
+
ES_HOST = ENV['ES_HOST'] || ESReindex::DEFAULT_URL
|
8
|
+
|
9
|
+
# Requires supporting files with custom matchers and macros, etc,
|
10
|
+
# in ./support/ and its subdirectories.
|
11
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
12
|
+
|
7
13
|
RSpec.configure do |config|
|
8
14
|
config.tty = true
|
9
15
|
|
10
16
|
config.filter_run focus: true
|
11
17
|
config.run_all_when_everything_filtered = true
|
12
|
-
end
|
13
18
|
|
19
|
+
ESReindex.logger = Logger.new(STDERR)
|
20
|
+
ESReindex.logger.level = Logger::WARN
|
21
|
+
|
22
|
+
# Make sure our indexes are clear for a fresh start
|
23
|
+
config.before type: :integration do
|
24
|
+
Elasticsearch::Client.new(host: ES_HOST).indices.tap do |es|
|
25
|
+
es.delete index: 'test*'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'elasticsearch/persistence/model'
|
2
|
+
|
3
|
+
def test_klass(options)
|
4
|
+
index_name = options[:index_name]
|
5
|
+
extra_attrs = options[:attributes] || []
|
6
|
+
|
7
|
+
Class.new do
|
8
|
+
def self.name; 'TestKlass'; end
|
9
|
+
|
10
|
+
include Elasticsearch::Persistence::Model
|
11
|
+
|
12
|
+
gateway.client Elasticsearch::Client.new host: ES_HOST
|
13
|
+
gateway.index = index_name
|
14
|
+
|
15
|
+
attribute :id
|
16
|
+
attribute :text
|
17
|
+
|
18
|
+
extra_attrs.each { |att| attribute att }
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,57 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: es-reindex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Aiken
|
8
|
+
- Victor Luft
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
date: 2015-01-09 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
15
|
+
name: elasticsearch
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
16
17
|
requirements:
|
17
18
|
- - ">="
|
18
19
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
20
|
+
version: 1.0.0
|
20
21
|
type: :runtime
|
21
22
|
prerelease: false
|
22
23
|
version_requirements: !ruby/object:Gem::Requirement
|
23
24
|
requirements:
|
24
25
|
- - ">="
|
25
26
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
27
|
+
version: 1.0.0
|
27
28
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
29
|
+
name: elasticsearch-persistence
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
30
31
|
requirements:
|
31
|
-
- - "
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: coveralls
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
32
|
+
- - "~>"
|
46
33
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
34
|
+
version: '0.1'
|
48
35
|
type: :development
|
49
36
|
prerelease: false
|
50
37
|
version_requirements: !ruby/object:Gem::Requirement
|
51
38
|
requirements:
|
52
|
-
- - "
|
39
|
+
- - "~>"
|
53
40
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
41
|
+
version: '0.1'
|
55
42
|
- !ruby/object:Gem::Dependency
|
56
43
|
name: bundler
|
57
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,21 +54,21 @@ dependencies:
|
|
67
54
|
- !ruby/object:Gem::Version
|
68
55
|
version: '1.0'
|
69
56
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
57
|
+
name: rake
|
71
58
|
requirement: !ruby/object:Gem::Requirement
|
72
59
|
requirements:
|
73
|
-
- - "
|
60
|
+
- - ">="
|
74
61
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
62
|
+
version: '0'
|
76
63
|
type: :development
|
77
64
|
prerelease: false
|
78
65
|
version_requirements: !ruby/object:Gem::Requirement
|
79
66
|
requirements:
|
80
|
-
- - "
|
67
|
+
- - ">="
|
81
68
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
69
|
+
version: '0'
|
83
70
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
71
|
+
name: coveralls
|
85
72
|
requirement: !ruby/object:Gem::Requirement
|
86
73
|
requirements:
|
87
74
|
- - ">="
|
@@ -94,6 +81,20 @@ dependencies:
|
|
94
81
|
- - ">="
|
95
82
|
- !ruby/object:Gem::Version
|
96
83
|
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '2.99'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2.99'
|
97
98
|
- !ruby/object:Gem::Dependency
|
98
99
|
name: guard-rspec
|
99
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,6 +112,7 @@ dependencies:
|
|
111
112
|
description: Ruby gem to copy ElasticSearch index (reindex).
|
112
113
|
email:
|
113
114
|
- jaiken@mojolingo.com
|
115
|
+
- vluft@mojolingo.com
|
114
116
|
executables:
|
115
117
|
- es-reindex
|
116
118
|
extensions: []
|
@@ -118,18 +120,24 @@ extra_rdoc_files: []
|
|
118
120
|
files:
|
119
121
|
- ".gitignore"
|
120
122
|
- ".rspec"
|
123
|
+
- ".travis.yml"
|
121
124
|
- Gemfile
|
122
125
|
- Guardfile
|
123
126
|
- LICENSE
|
124
127
|
- README.markdown
|
128
|
+
- Rakefile
|
125
129
|
- bin/es-reindex
|
126
130
|
- es-reindex.gemspec
|
127
131
|
- lib/es-reindex.rb
|
128
132
|
- lib/es-reindex/args_parser.rb
|
133
|
+
- lib/es-reindex/railtie.rb
|
129
134
|
- lib/es-reindex/version.rb
|
130
135
|
- spec/es-reindex/args_parser_spec.rb
|
131
136
|
- spec/es-reindex_spec.rb
|
137
|
+
- spec/integration/copy_spec.rb
|
138
|
+
- spec/integration/reindex_spec.rb
|
132
139
|
- spec/spec_helper.rb
|
140
|
+
- spec/support/test_model.rb
|
133
141
|
homepage: https://github.com/mojolingo/es-reindex
|
134
142
|
licenses:
|
135
143
|
- MIT
|
@@ -157,4 +165,7 @@ summary: Ruby gem to copy ElasticSearch index (reindex).
|
|
157
165
|
test_files:
|
158
166
|
- spec/es-reindex/args_parser_spec.rb
|
159
167
|
- spec/es-reindex_spec.rb
|
168
|
+
- spec/integration/copy_spec.rb
|
169
|
+
- spec/integration/reindex_spec.rb
|
160
170
|
- spec/spec_helper.rb
|
171
|
+
- spec/support/test_model.rb
|