es-reindex 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/mojolingo/es-reindex.svg)](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
|