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 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