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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7620b0c56bc7c9531fd32c9c573bfef963a3a2eb
4
- data.tar.gz: 86eb8a9c6f896e6697d3adb3de6af34dc99fd866
3
+ metadata.gz: eed6bf0fb22e4ef0644bc07f8044707489dcac19
4
+ data.tar.gz: 785f245811cf514f8ccd50fb5bd6b24d91e65b81
5
5
  SHA512:
6
- metadata.gz: 853868790c7f1b8658995ec38b4d9b5fc87bdf5658b6fa9dcca32d5a64cb6c14e1af9b294e1256e36c18f38bf828e6a0daeadf5d48efb607e110020e7020d772
7
- data.tar.gz: fd4f29b8dadb1cacf345161e2928f541ee4a366ad98349d24de49fac848bdb13dd602f3fbcb9698d52bb90d17a94bb2cee30d8c8779a1e6c00a1f068c0a2b91f
6
+ metadata.gz: 6504ac4e4e7b46ba4e00c15bf2d90889e688479389d9ec399e571d67a709caa3324fb65f6b57d38c7f6558dd85acd9a017ebbea3855fb63ef71e40454b43359f
7
+ data.tar.gz: c01e98b13e0edcd1c6075827b2236e4666eb5759772c79e60722cfe8bde36062776df149feeaf18c8acc1a15cc032cd0157aeea14d3f14410fd0b6ac3c0ac8a4
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ services:
2
+ - elasticsearch
3
+ language: ruby
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0
8
+ - 2.2.0
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 is required, use [rvm](https://rvm.io/) for convenience.
11
-
12
- Following gems are required:
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.new('http://my_server/index', 'http://my_server/index_copy', options).go!
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
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'bundler/setup'
3
+
4
+ task default: :spec
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new :spec
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(src, dst, options).go!
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 'rest-client', '>= 1.6.0'
26
- s.add_runtime_dependency 'multi_json'
25
+ s.add_runtime_dependency 'elasticsearch', '>= 1.0.0'
27
26
 
28
- s.add_development_dependency %q<coveralls>, ['>= 0']
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 'rest-client'
2
- require 'multi_json'
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 # specify frame size to be obtained with one fetch during scrolling
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 go!
23
- MultiJson.load_options = {mode: :compat}
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
- printf "Copying '%s/%s' to '%s/%s'%s\n Confirm or hit Ctrl-c to abort...\n",
38
- surl, sidx, durl, didx,
39
- remove? ?
40
- ' with rewriting destination mapping!' :
41
- update? ? ' with updating existing documents!' : '.'
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
- # remove old index in case of remove=true
46
- retried_request(:delete, "#{durl}/#{didx}") if remove? && retried_request(:get, "#{durl}/#{didx}/_status")
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
- # (re)create destination index
49
- unless retried_request :get, "#{durl}/#{didx}/_status"
50
- # obtain the original index settings first
51
- unless settings = retried_request(:get, "#{surl}/#{sidx}/_settings")
52
- warn "Failed to obtain original index '#{surl}/#{sidx}' settings!"
53
- exit 1
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
- puts 'OK.'
64
- end
65
- unless mappings = retried_request(:get, "#{surl}/#{sidx}/_mapping")
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
- printf "Copying '%s/%s' to '%s/%s'... \n", surl, sidx, durl, didx
130
+ def copy_docs
131
+ log "Copying '#{surl}/#{sidx}' to '#{durl}/#{didx}'..."
84
132
  @start_time = Time.now
85
- shards = retried_request :get, "#{surl}/#{sidx}/_count?q=*"
86
- shards = MultiJson.load(shards)['_shards']['total'].to_i
87
- scan = retried_request :get, "#{surl}/#{sidx}/_search?search_type=scan&scroll=10m&size=#{frame / shards}"
88
- scan = MultiJson.load scan
89
- scroll_id = scan['_scroll_id']
90
- total = scan['hits']['total']
91
- printf " %u/%u (%.1f%%) done.\r", done, total, 0
92
-
93
- bulk_op = update? ? 'index' : 'create'
94
-
95
- while true do
96
- data = retried_request :get, "#{surl}/_search/scroll?scroll=10m&scroll_id=#{scroll_id}"
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
- ['_timestamp', '_ttl'].each{|doc_arg|
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 << "\n" # empty line in the end required
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
- printf " %u/%u (%.1f%%) done in %s, E.T.A.: %s.\r",
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
- printf "#{' ' * 80}\r %u/%u done in %s.\n", done, total, tm_len
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
- # no point for large reindexation with data still being stored in index
125
- printf 'Checking document count... '
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 = retried_request :get, "#{surl}/#{sidx}/_count?q=*"
131
- dcount = retried_request :get, "#{durl}/#{didx}/_count?q=*"
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
- printf "%u == %u (%s\n", scount, dcount, scount == dcount ? 'equals).' : 'NOT EQUAL)!'
181
+ log "Document count: #{scount} = #{dcount} (#{scount == dcount ? 'equal' : 'NOT EQUAL'})"
182
+
183
+ scount == dcount
184
+ end
141
185
 
142
- exit 0
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
@@ -0,0 +1,7 @@
1
+ class ESReindex
2
+ class Railtie < Rails::Railtie
3
+ initializer 'Rails ESReindex.logger' do
4
+ ESReindex.logger = Rails.logger
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  class ESReindex
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -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.1.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: rest-client
15
+ name: elasticsearch
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - ">="
18
19
  - !ruby/object:Gem::Version
19
- version: 1.6.0
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.6.0
27
+ version: 1.0.0
27
28
  - !ruby/object:Gem::Dependency
28
- name: multi_json
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: rspec
57
+ name: rake
71
58
  requirement: !ruby/object:Gem::Requirement
72
59
  requirements:
73
- - - "~>"
60
+ - - ">="
74
61
  - !ruby/object:Gem::Version
75
- version: '2.99'
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: '2.99'
69
+ version: '0'
83
70
  - !ruby/object:Gem::Dependency
84
- name: rake
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