Class: Couchbase::View

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/couchbase/view.rb

Overview

This class implements Couchbase View execution

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bucket, endpoint, params = {}) ⇒ View

Set up view endpoint and optional params

Parameters:

  • bucket (Couchbase::Bucket)

    Connection object which stores all info about how to make requests to Couchbase views.

  • endpoint (String)

    Full Couchbase View URI.

  • params (Hash) (defaults to: {})

    Optional parameter which will be passed to #fetch



74
75
76
77
78
79
80
81
82
# File 'lib/couchbase/view.rb', line 74

def initialize(bucket, endpoint, params = {})
  @bucket = bucket
  @endpoint = endpoint
  @params = {:connection_timeout => 75_000}.merge(params)
  @wrapper_class = params.delete(:wrapper_class) || ViewRow
  unless @wrapper_class.respond_to?(:wrap)
    raise ArgumentError, "wrapper class should reposond to :wrap, check the options"
  end
end

Instance Attribute Details

#paramsObject (readonly)

Returns the value of attribute params.



62
63
64
# File 'lib/couchbase/view.rb', line 62

def params
  @params
end

Instance Method Details

#each(params = {}) ⇒ Object

Yields each document that was fetched by view. It doesn’t instantiate all the results because of streaming JSON parser. Returns Enumerator unless block given.

Examples:

Use each method with block


view.each do |doc|
  # do something with doc
end

Use Enumerator version


enum = view.each  # request hasn't issued yet
enum.map{|doc| doc.title.upcase}

Pass options during view initialization


endpoint = "http://localhost:5984/default/_design/blog/_view/recent"
view = View.new(conn, endpoint, :descending => true)
view.each do |document|
  # do something with document
end

Parameters:

  • params (Hash) (defaults to: {})

    Params for Couchdb query. Some useful are: :start_key, :start_key_doc_id, :descending. See #fetch.



110
111
112
113
# File 'lib/couchbase/view.rb', line 110

def each(params = {})
  return enum_for(:each, params) unless block_given?
  fetch(params) {|doc| yield(doc)}
end

#fetch(params = {}) {|document| ... } ⇒ Array

Note:

Avoid using $ symbol as prefix for properties in your documents, because server marks with it meta fields like flags and expiration, therefore dollar prefix is some kind of reserved. It won’t hurt your application. Currently the Couchbase::ViewRow class extracts $flags, $cas and $expiration properties from the document and store them in Couchbase::ViewRow#meta hash.

Performs query to Couchbase view. This method will stream results if block given or return complete result set otherwise. In latter case it defines method total_rows returning corresponding entry from Couchbase result object.

Examples:

Query recent_posts view with key filter

doc.recent_posts(:body => {:keys => ["key1", "key2"]})

Fetch second page of result set (splitted in 10 items per page)

page = 2
per_page = 10
doc.recent_posts(:skip => (page - 1) * per_page, :limit => per_page)

Simple join using Map/Reduce

# Given the bucket with Posts(:id, :type, :title, :body) and
# Comments(:id, :type, :post_id, :author, :body). The map function
# below (in javascript) will build the View index called
# "recent_posts_with_comments" which will behave like left inner join.
#
#   function(doc) {
#     switch (doc.type) {
#       case "Post":
#         emit([doc.id, 0], null);
#         break;
#       case "Comment":
#         emit([doc.post_id, 1], null);
#         break;
#     }
#   }
#
post_id = 42
doc.recent_posts_with_comments(:start_key => [post_id, 0],
                               :end_key => [post_id, 1],
                               :include_docs => true)

Parameters:

  • params (Hash) (defaults to: {})

    parameters for Couchbase query.

Options Hash (params):

  • :include_docs (true, false) — default: false

    Include the full content of the documents in the return. Note that the document is fetched from the in memory cache where it may have been changed or even deleted. See also :quiet parameter below to control error reporting during fetch.

  • :quiet (true, false) — default: true

    Do not raise error if associated document not found in the memory. If the parameter true will use nil value instead.

  • :descending (true, false) — default: false

    Return the documents in descending by key order

  • :key (String, Fixnum, Hash, Array)

    Return only documents that match the specified key. Will be JSON encoded.

  • :keys (Array)

    The same as :key, but will work for set of keys. Will be JSON encoded.

  • :startkey (String, Fixnum, Hash, Array)

    Return records starting with the specified key. :start_key option should also work here. Will be JSON encoded.

  • :startkey_docid (String)

    Document id to start with (to allow pagination for duplicate startkeys). :start_key_doc_id also should work.

  • :endkey (String, Fixnum, Hash, Array)

    Stop returning records when the specified key is reached. :end_key option should also work here. Will be JSON encoded.

  • :endkey_docid (String)

    Last document id to include in the output (to allow pagination for duplicate startkeys). :end_key_doc_id also should work.

  • :inclusive_end (true, false) — default: true

    Specifies whether the specified end key should be included in the result

  • :limit (Fixnum)

    Limit the number of documents in the output.

  • :skip (Fixnum)

    Skip this number of records before starting to return the results.

  • :on_error (String, Symbol) — default: :continue

    Sets the response in the event of an error. Supported values:

    :continue

    Continue to generate view information in the event of an error, including the error information in the view response stream.

    :stop

    Stop immediately when an error condition occurs. No further view information will be returned.

  • :connection_timeout (Fixnum) — default: 75000

    Timeout before the view request is dropped (milliseconds)

  • :reduce (true, false) — default: true

    Use the reduction function

  • :group (true, false) — default: false

    Group the results using the reduce function to a group or single row.

  • :group_level (Fixnum)

    Specify the group level to be used.

  • :stale (String, Symbol, false) — default: :update_after

    Allow the results from a stale view to be used. Supported values:

    false

    Force a view update before returning data

    :ok

    Allow stale views

    :update_after

    Allow stale view, update view after it has been accessed

  • :body (Hash)

    Accepts the same parameters, except :body of course, but sends them in POST body instead of query string. It could be useful for really large and complex parameters.

Yield Parameters:

Returns:

  • (Array)

    with documents. There will be total_entries method defined on this array if it’s possible.

Raises:



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/couchbase/view.rb', line 262

def fetch(params = {})
  params = @params.merge(params)
  options = {:chunked => true, :extended => true, :type => :view}
  if body = params.delete(:body)
    body = MultiJson.dump(body) unless body.is_a?(String)
    options.update(:body => body, :method => params.delete(:method) || :post)
  end
  include_docs = params.delete(:include_docs)
  quiet = true
  if params.has_key?(:quiet)
    quiet = params.delete(:quiet)
  end
  path = Utils.build_query(@endpoint, params)
  request = @bucket.make_http_request(path, options)
  res = []
  request.on_body do |chunk|
    res << chunk
    request.pause if chunk.value.nil? || chunk.error
  end
  filter = ["/rows/", "/errors/"]
  filter << "/total_rows" unless block_given?
  parser = YAJI::Parser.new(:filter => filter, :with_path => true)
  docs = []
  parser.on_object do |path, obj|
    case path
    when "/total_rows"
      # if total_rows key present, save it and take next object
      docs.instance_eval("def total_rows; #{obj}; end")
    when "/errors/"
      from, reason = obj["from"], obj["reason"]
      if @on_error
        @on_error.call(from, reason)
      else
        raise Error::View.new(from, reason)
      end
    else
      if include_docs
        val, flags, cas = @bucket.get(obj['id'], :extended => true, :quiet => quiet)
        obj['doc'] = {
          'value' => val,
          'meta' => {
            'id' => obj['id'],
            'flags' => flags,
            'cas' => cas
          }
        }
      end
      if block_given?
        yield @wrapper_class.wrap(@bucket, obj)
      else
        docs << @wrapper_class.wrap(@bucket, obj)
      end
    end
  end
  # run event loop until the terminating chunk will be found
  # last_res variable keeps latest known chunk of the result
  last_res = nil
  while true
    # feed response received chunks to the parser
    while r = res.shift
      if r.error
        if @on_error
          @on_error.call("http_error", r.error)
          break
        else
          raise Error::View.new("http_error", r.error, nil)
        end
      end
      last_res = r
      parser << r.value
    end
    if last_res.nil? || !last_res.completed?  # shall we run the event loop?
      request.continue
    else
      break
    end
  end
  # return nil for call with block
  block_given? ? nil : docs
end

#inspectString

Returns a string containing a human-readable representation of the Couchbase::View

Returns:

  • (String)


347
348
349
# File 'lib/couchbase/view.rb', line 347

def inspect
  %(#<#{self.class.name}:#{self.object_id} @endpoint=#{@endpoint.inspect} @params=#{@params.inspect}>)
end

#on_error {|from, reason| ... } ⇒ Object

Registers callback function for handling error objects in view results stream.

Examples:

Using #on_error to log all errors in view result


# JSON-encoded view result
#
# {
#   "total_rows": 0,
#   "rows": [ ],
#   "errors": [
#     {
#       "from": "127.0.0.1:5984",
#       "reason": "Design document `_design/testfoobar` missing in database `test_db_b`."
#     },
#     {
#       "from": "http:// localhost:5984/_view_merge/",
#       "reason": "Design document `_design/testfoobar` missing in database `test_db_c`."
#     }
#   ]
# }

view.on_error do |from, reason|
  logger.warn("#{view.inspect} received the error '#{reason}' from #{from}")
end
docs = view.fetch

More concise example to just count errors


errcount = 0
view.on_error{|f,r| errcount += 1}.fetch

Yield Parameters:

  • from (String)

    Location of the node where error occured

  • reason (String)

    The reason message describing what happened.



151
152
153
154
# File 'lib/couchbase/view.rb', line 151

def on_error(&callback)
  @on_error = callback
  self  # enable call chains
end