class CouchRest::Database

Attributes

bulk_save_cache_limit[RW]

How many documents should be cached before peforming the bulk save operation

name[R]

Name of the database of we're using.

path[R]

Name of the database we can use in requests.

server[R]

Server object we'll use to communicate with.

Public Class Methods

new(server, name) click to toggle source

Create a CouchRest::Database adapter for the supplied CouchRest::Server and database name.

Parameters

server<CouchRest::Server>

database host

name<String>

database name

# File lib/couchrest/database.rb, line 26
def initialize(server, name)
  @name     = name
  @server   = server
  @path     = "/#{CGI.escape(name)}"
  @bulk_save_cache = []
  @bulk_save_cache_limit = 500  # must be smaller than the uuid count
end

Public Instance Methods

all_docs(params = {}, payload = {}, &block) click to toggle source

Query the _all_docs view. Accepts all the same arguments as view.

# File lib/couchrest/database.rb, line 303
def all_docs(params = {}, payload = {}, &block)
  view("_all_docs", params, payload, &block)
end
Also aliased as: documents
batch_save_doc(doc) click to toggle source

Save a document to CouchDB in batch mode. See save_doc's batch argument.

# File lib/couchrest/database.rb, line 179
def batch_save_doc(doc)
  save_doc(doc, false, true)
end
bulk_delete(docs = nil, opts = {})
Alias for: bulk_save
bulk_load(ids)
Alias for: get_bulk
bulk_save(docs = nil, opts = {}) click to toggle source

POST an array of documents to CouchDB. If any of the documents are missing ids, supply one from the uuid cache.

If called with no arguments, bulk saves the cache of documents to be bulk saved.

# File lib/couchrest/database.rb, line 187
def bulk_save(docs = nil, opts = {})
  opts = { :use_uuids => true, :all_or_nothing => false }.update(opts)
  if docs.nil?
    docs = @bulk_save_cache
    @bulk_save_cache = []
  end
  if opts[:use_uuids]
    ids, noids = docs.partition{|d|d['_id']}
    uuid_count = [noids.length, @server.uuid_batch_count].max
    noids.each do |doc|
      nextid = server.next_uuid(uuid_count) rescue nil
      doc['_id'] = nextid if nextid
    end
  end
  request_body = {:docs => docs}
  if opts[:all_or_nothing]
    request_body[:all_or_nothing] = true
  end
  results = connection.post "#{path}/_bulk_docs", request_body
  docs_by_id = Hash[docs.map { |doc| [doc['_id'], doc] }] unless docs.nil?
  results.each { |r| docs_by_id[r['id']]['_rev'] = r['rev'] if r['ok'] } unless results.nil?
  results
end
Also aliased as: bulk_delete
bulk_save_doc(doc) click to toggle source

Save a document to CouchDB in bulk mode. See save_doc's bulk argument.

# File lib/couchrest/database.rb, line 174
def bulk_save_doc(doc)
  save_doc(doc, true)
end
changes(params = {}, payload = {}, &block) click to toggle source

Query CouchDB's special _changes feed for the latest. All standard CouchDB options can be provided.

Warning: sending :feed => 'continuous' will cause your code to block indefinetly while waiting for changes. You might want to look-up an alternative to this.

# File lib/couchrest/database.rb, line 314
def changes(params = {}, payload = {}, &block)
  view("_changes", params, payload, &block)
end
compact!() click to toggle source

Compact the database, removing old document revisions and optimizing space use.

# File lib/couchrest/database.rb, line 55
def compact!
  connection.post "#{path}/_compact"
end
connection() click to toggle source
# File lib/couchrest/database.rb, line 34
def connection
  server.connection
end
copy_doc(doc, dest) click to toggle source

COPY an existing document to a new id. If the destination id currently exists, a rev must be provided. dest can take one of two forms if overwriting: “id_to_overwrite?rev=revision” or the actual doc hash with a '_rev' key

# File lib/couchrest/database.rb, line 231
def copy_doc(doc, dest)
  raise ArgumentError, "_id is required for copying" unless doc['_id']
  slug = escape_docid(doc['_id'])
  destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
    "#{dest['_id']}?rev=#{dest['_rev']}"
  else
    dest
  end
  connection.copy "#{path}/#{slug}", destination
end
create!() click to toggle source

Create the database

# File lib/couchrest/database.rb, line 60
def create!
  bool = server.create_db(path) rescue false
  bool && true
end
delete!() click to toggle source

DELETE the database itself. This is not undoable and could be rather catastrophic. Use with care!

# File lib/couchrest/database.rb, line 86
def delete!
  connection.delete path
end
delete_attachment(doc, name, force=false) click to toggle source

DELETE an attachment directly from CouchDB

# File lib/couchrest/database.rb, line 347
def delete_attachment(doc, name, force=false)
  attach_path = path_for_attachment(doc, name)
  begin
    connection.delete(attach_path)
  rescue Exception => error
    if force
      # get over a 409
      doc = get(doc['_id'])
      attach_path = path_for_attachment(doc, name)
      connection.delete(attach_path)
    else
      error
    end
  end
end
delete_doc(doc, bulk = false) click to toggle source

DELETE the document from CouchDB that has the given _id and _rev.

If bulk is true (false by default) the deletion is recorded for bulk-saving (bulk-deletion :) later. Bulk saving happens automatically when bulk_save_cache limit is exceded, or on the next non bulk save.

# File lib/couchrest/database.rb, line 217
def delete_doc(doc, bulk = false)
  raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
  if bulk
    @bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], :_deleted => true }
    return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
    return {'ok' => true} # Mimic the non-deferred version
  end
  slug = escape_docid(doc['_id'])        
  connection.delete "#{path}/#{slug}?rev=#{doc['_rev']}"
end
documents(params = {}, payload = {}, &block)
Alias for: all_docs
fetch_attachment(doc, name) click to toggle source

GET an attachment directly from CouchDB

# File lib/couchrest/database.rb, line 335
def fetch_attachment(doc, name)
  connection.get path_for_attachment(doc, name), :raw => true
end
fti(name, params={}) click to toggle source

Query a CouchDB-Lucene search view

# File lib/couchrest/database.rb, line 319
def fti(name, params={})
  # -> http://localhost:5984/yourdb/_fti/YourDesign/by_name?include_docs=true&q=plop*'
  view("_fti/#{name}", params)
end
Also aliased as: search
get(*args) click to toggle source

GET the requested document by ID like `get!`, but returns nil if the document does not exist.

# File lib/couchrest/database.rb, line 111
def get(*args)
  get!(*args)
rescue CouchRest::NotFound
  nil
end
get!(id, params = {}) click to toggle source

GET a document from CouchDB, by id. Returns a Document, Design, or raises an exception if the document does not exist.

# File lib/couchrest/database.rb, line 95
def get!(id, params = {})
  slug = escape_docid(id)
  url = CouchRest.paramify_url("#{path}/#{slug}", params)
  result = connection.get(url)
  return result unless result.is_a?(Hash)
  doc = if /^_design/ =~ result["_id"]
    Design.new(result)
  else
    Document.new(result)
  end
  doc.database = self
  doc
end
get_bulk(ids) click to toggle source

load a set of documents by passing an array of ids

# File lib/couchrest/database.rb, line 326
def get_bulk(ids)
  all_docs(:keys => ids, :include_docs => true)
end
Also aliased as: bulk_load
info() click to toggle source

GET the database info from CouchDB

# File lib/couchrest/database.rb, line 50
def info
  connection.get path
end
put_attachment(doc, name, file, options = {}) click to toggle source

PUT an attachment directly to CouchDB, expects an IO object, or a string that will be converted to a StringIO in the 'file' parameter.

# File lib/couchrest/database.rb, line 341
def put_attachment(doc, name, file, options = {})
  file = StringIO.new(file) if file.is_a?(String)
  connection.put path_for_attachment(doc, name), file, options
end
recreate!() click to toggle source

Delete and re create the database

# File lib/couchrest/database.rb, line 66
def recreate!
  delete!
  create!
rescue CouchRest::NotFound
ensure
  create!
end
replicate_from(other_db, continuous = false, create_target = false, doc_ids = nil) click to toggle source

Replicates via “pulling” from another database to this database. Makes no attempt to deal with conflicts.

# File lib/couchrest/database.rb, line 75
def replicate_from(other_db, continuous = false, create_target = false, doc_ids = nil)
  replicate(other_db, continuous, :target => name, :create_target => create_target, :doc_ids => doc_ids)
end
replicate_to(other_db, continuous = false, create_target = false, doc_ids = nil) click to toggle source

Replicates via “pushing” to another database. Makes no attempt to deal with conflicts.

# File lib/couchrest/database.rb, line 80
def replicate_to(other_db, continuous = false, create_target = false, doc_ids = nil)
  replicate(other_db, continuous, :source => name, :create_target => create_target, :doc_ids => doc_ids)
end
root()
Alias for: uri
save_doc(doc, bulk = false, batch = false) click to toggle source

Save a document to CouchDB. This will use the _id field from the document as the id for PUT, or request a new UUID from CouchDB, if no _id is present on the document. IDs are attached to documents on the client side because POST has the curious property of being automatically retried by proxies in the event of network segmentation and lost responses.

If bulk is true (false by default) the document is cached for bulk-saving later. Bulk saving happens automatically when bulk_save_cache limit is exceded, or on the next non bulk save.

If batch is true (false by default) the document is saved in batch mode, “used to achieve higher throughput at the cost of lower guarantees. When […] sent using this option, it is not immediately written to disk. Instead it is stored in memory on a per-user basis for a second or so (or the number of docs in memory reaches a certain point). After the threshold has passed, the docs are committed to disk. Instead of waiting for the doc to be written to disk before responding, CouchDB sends an HTTP 202 Accepted response immediately. batch=ok is not suitable for crucial data, but it ideal for applications like logging which can accept the risk that a small proportion of updates could be lost due to a crash.”

# File lib/couchrest/database.rb, line 138
def save_doc(doc, bulk = false, batch = false)
  if doc['_attachments']
    doc['_attachments'] = encode_attachments(doc['_attachments'])
  end

  if bulk
    @bulk_save_cache << doc
    bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
    return {'ok' => true} # Compatibility with Document#save
  elsif !bulk && @bulk_save_cache.length > 0
    bulk_save
  end
  result = if doc['_id']
    slug = escape_docid(doc['_id'])
    begin
      doc_path = "#{path}/#{slug}"
      doc_path << "?batch=ok" if batch
      connection.put doc_path, doc
    rescue CouchRest::NotFound
      puts "resource not found when saving even though an id was passed"
      slug = doc['_id'] = server.next_uuid
      connection.put "#{path}/#{slug}", doc
    end
  else
    slug = doc['_id'] = @server.next_uuid
    connection.put "#{path}/#{slug}", doc
  end
  if result['ok']
    doc['_id'] = result['id']
    doc['_rev'] = result['rev']
    doc.database = self if doc.respond_to?(:database=)
  end
  result
end
slow_view(payload, params = {}, &block)
Alias for: temp_view
temp_view(payload, params = {}, &block) click to toggle source

POST a temporary view function to CouchDB for querying. This is not recommended, as you don't get any performance benefit from CouchDB's materialized views. Can be quite slow on large databases.

# File lib/couchrest/database.rb, line 296
def temp_view(payload, params = {}, &block)
  view('_temp_view', params, payload, &block)
end
Also aliased as: slow_view
to_s() click to toggle source

String of root

# File lib/couchrest/database.rb, line 45
def to_s
  uri.to_s
end
update_doc(doc_id, params = {}, update_limit = 10) { |doc| ... } click to toggle source

Updates the given doc by yielding the current state of the doc and trying to update update_limit times. Returns the doc if successfully updated without hitting the limit. If the limit is reached, the last execption will be raised.

# File lib/couchrest/database.rb, line 246
def update_doc(doc_id, params = {}, update_limit = 10)
  resp = {'ok' => false}
  last_fail = nil

  until resp['ok'] or update_limit <= 0
    doc = self.get(doc_id, params)
    yield doc
    begin
      resp = self.save_doc doc
    rescue CouchRest::RequestFailed => e
      if e.http_code == 409 # Update collision
        update_limit -= 1
        last_fail = e
      else
        raise e
      end
    end
  end

  raise last_fail unless resp['ok']
  doc
end
uri() click to toggle source

A URI object for the exact location of this database

# File lib/couchrest/database.rb, line 39
def uri
  server.uri + path 
end
Also aliased as: root
view(name, params = {}, payload = {}, &block) click to toggle source

Query a CouchDB view as defined by a _design document. Accepts paramaters as described in wiki.apache.org/couchdb/HttpViewApi

# File lib/couchrest/database.rb, line 274
def view(name, params = {}, payload = {}, &block)
  opts = {}
  params = params.dup
  payload['keys'] = params.delete(:keys) if params[:keys]

  # Continuous feeds need to be parsed differently
  opts[:continuous] = true if params['feed'] == 'continuous'

  # Try recognising the name, otherwise assume already prepared
  view_path = name_to_view_path(name)
  req_path = CouchRest.paramify_url("#{path}/#{view_path}", params)

  if payload.empty?
    connection.get req_path, opts, &block
  else
    connection.post req_path, payload, opts, &block
  end
end

Private Instance Methods

base64(data) click to toggle source
# File lib/couchrest/database.rb, line 401
def base64(data)
  Base64.encode64(data).gsub(/\s/,'')
end
encode_attachments(attachments) click to toggle source
# File lib/couchrest/database.rb, line 393
def encode_attachments(attachments)
  attachments.each do |k,v|
    next if v['stub'] || v['data'].frozen?
    v['data'] = base64(v['data']).freeze
  end
  attachments
end
escape_docid(id) click to toggle source
# File lib/couchrest/database.rb, line 389
def escape_docid id
  /^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id) 
end
name_to_view_path(name) click to toggle source

Convert a simplified view name into a complete view path. If the name already starts with a “_” no alterations will be made.

# File lib/couchrest/database.rb, line 407
def name_to_view_path(name)
  name =~ /^([^_].*?)\/(.*)$/ ? "_design/#{$1}/_view/#{$2}" : name
end
path_for_attachment(doc, name) click to toggle source
# File lib/couchrest/database.rb, line 382
def path_for_attachment(doc, name)
  docid = escape_docid(doc['_id'])
  name  = CGI.escape(name)
  rev   = doc['_rev'] ? "?rev=#{doc['_rev']}" : ''
  "#{path}/#{docid}/#{name}#{rev}"
end
replicate(other_db, continuous, options) click to toggle source
# File lib/couchrest/database.rb, line 365
def replicate(other_db, continuous, options)
  raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
  raise ArgumentError, "must provide a target or source option" unless (options.key?(:target) || options.key?(:source))
  doc_ids = options.delete(:doc_ids)
  payload = options
  if options.has_key?(:target)
    payload[:source] = other_db.root.to_s
  else
    payload[:target] = other_db.root.to_s
  end
  payload[:continuous] = continuous
  payload[:doc_ids] = doc_ids if doc_ids
  
  # Use a short lived request here
  connection.post "_replicate", payload
end