-
-
Save hazdik/accd164839a5d2277f5f6506ef941724 to your computer and use it in GitHub Desktop.
Undeletes a document from a CouchDB database.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# | |
# Undelete a document from a CouchDB database. | |
# | |
# Recovers a previously deleted document by looking at the _changes | |
# feed, putting a new empty document preserving the revisions chain, | |
# retrieves the revs_info for the document, asks the user which one | |
# to recover, and puts back the old revision into place. | |
# | |
# For this to work, GETting the document must return error: "not_found", | |
# deleted: true. | |
# | |
# Requires Typhoeus and Oj. | |
# | |
# Latest version: https://gist.github.com/vjt/6158675 | |
# | |
# h/t to http://stackoverflow.com/questions/10854883/retrieve-just-deleted-document | |
# | |
# - vjt Mon Aug 5 21:22:13 CEST 2013 | |
# | |
# Made in Ventotene. | |
# | |
require 'typhoeus' | |
require 'oj' | |
def get(url, params = {}) | |
res = Typhoeus.get(url, :params => params, :headers => {'Accept' => 'application/json'}) | |
Oj.load(res.body) | |
end | |
def put(url, body, params = {}) | |
res = Typhoeus.put(url, :body => Oj.dump(body), :params => params, :headers => {'Accept' => 'application/json'}) | |
Oj.load(res.body) | |
end | |
def delete(url, params = {}) | |
res = Typhoeus.delete(url, :params => params, :headers => {'Accept' => 'application/json'}) | |
Oj.load(res.body) | |
end | |
class UndeleteError < StandardError; end | |
if $0 == __FILE__ | |
database = ARGV[0] | |
docid = ARGV[1] | |
unless database && docid | |
raise ArgumentError, "Usage: #$0 <Database> <Document ID>" | |
end | |
docurl = [database, docid].join('/') | |
changes = [database, '_changes'].join('/') | |
puts "Processing document #{docurl}..." | |
# Verify the document is deleted | |
res = get(docurl) | |
unless res == {"error"=>"not_found", "reason"=>"deleted"} | |
raise UndeleteError, "The specified document is not recoverable. Sorry, you'll have to recover from backup." | |
end | |
# Now get the last revision from _changes | |
res = get(changes)['results'].select {|d| d['id'] == docid && d['deleted'] == true}.last | |
unless res | |
raise UndeleteError, "Could not find the last delete change." | |
end | |
lastrev = res['changes'].first['rev'] | |
# Put a new empty_version of the document | |
res = put(docurl, {}, {:rev => lastrev}) | |
unless res['ok'] == true | |
raise UndeleteError, "Sorry, an unexpected error has occurred: #{res.inspect}" | |
end | |
currrev = res['rev'] | |
# Show previous revisions | |
res = get(docurl, :revs_info => true) | |
puts "The following revisions have been found:" | |
puts res['_revs_info'].select {|r| r['status'] == 'available'}.map(&:inspect).tap(&:shift).join("\n") | |
print "Type the revision number you want to recover: " | |
rev = $stdin.gets.chomp | |
puts "OK, fetching revision #{rev}..." | |
recovered = get(docurl, :rev => rev, :attachments => true) | |
recovered['_rev'] = currrev | |
puts "Putting back the document into couchdb..." | |
res = put docurl, recovered, :rev => currrev | |
if res['ok'] == true | |
puts "All done. Check that #{docurl} now contains what you do expect." | |
else | |
puts "An error occurred: #{res}" | |
end | |
end # if $0 == __FILE__ | |
# EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment