Class: CouchRest::Model::Designs::View
- Inherits:
-
Object
- Object
- CouchRest::Model::Designs::View
- Includes:
- Enumerable
- Defined in:
- lib/couchrest/model/designs/view.rb
Overview
A proxy class that allows view queries to be created using chained method calls. After each call a new instance of the method is created based on the original in a similar fashion to ruby’s Sequel library, or Rails 3’s Arel.
CouchDB views have inherent limitations, so joins and filters as used in a normal relational database are not possible.
Instance Attribute Summary collapse
-
#model ⇒ Object
Returns the value of attribute model.
-
#name ⇒ Object
Returns the value of attribute name.
-
#owner ⇒ Object
Returns the value of attribute owner.
-
#query ⇒ Object
Returns the value of attribute query.
-
#result ⇒ Object
Returns the value of attribute result.
Class Method Summary collapse
-
.create(model, name, opts = {}) ⇒ Object
Simplified view creation.
Instance Method Summary collapse
-
#[](value) ⇒ Object
Accept requests as if the view was an array.
-
#all ⇒ Object
Fetch all the documents the view can access.
-
#count ⇒ Object
Perform a count operation based on the current view.
- #current_page ⇒ Object
-
#database(value) ⇒ Object
Specify the database the view should use.
-
#descending ⇒ Object
The results should be provided in descending order.
-
#docs ⇒ Object
Provide all the documents from the view.
-
#each(&block) ⇒ Object
Run through each document provided by the
#all
method. -
#empty? ⇒ Boolean
Check to see if the array of documents is empty.
-
#endkey(value) ⇒ Object
The opposite of
#startkey
, finds all index entries whose key is before the value specified. -
#endkey_doc(value) ⇒ Object
The result set should end at the position of the provided document.
-
#first ⇒ Object
If another request has been made on the view, this will return the first document in the set.
-
#group ⇒ Object
Control whether the reduce function reduces to a set of distinct keys or to a single result row.
-
#group_level(value) ⇒ Object
Will set the level the grouping should be performed to.
- #include_docs ⇒ Object
-
#info ⇒ Object
No yet implemented.
-
#initialize(parent, new_query = {}, name = nil) ⇒ View
constructor
Initialize a new View object.
-
#key(value) ⇒ Object
Find all entries in the index whose key matches the value provided.
-
#keys(*keys) ⇒ Object
Keys is a special CouchDB option that will cause the view request to be POSTed including an array of keys.
-
#last ⇒ Object
Same as first but will order the view in descending order.
-
#length ⇒ Object
Return the number of documents in the currently defined result set.
-
#limit(value) ⇒ Object
Limit the result set to the value supplied.
- #limit_value ⇒ Object
- #num_pages ⇒ Object
-
#offset ⇒ Object
Wrapper for the results offset.
- #offset_value ⇒ Object
-
#page(page) ⇒ Object
Kaminari compatible pagination support.
- #per(num) ⇒ Object
-
#proxy(value) ⇒ Object
Set the view’s proxy that will be used instead of the model for any future searches.
-
#reduce ⇒ Object
Use the reduce function on the view.
-
#reset! ⇒ Object
Return any cached values to their nil state so that any queries requested later will have a fresh set of data.
-
#rows ⇒ Object
Return each row wrapped in a ViewRow object.
-
#skip(value = 0) ⇒ Object
Skip the number of entries in the index specified by value.
-
#startkey(value) ⇒ Object
Find all index keys that start with the value provided.
-
#startkey_doc(value) ⇒ Object
The result set should start from the position of the provided document.
- #total_count ⇒ Object
-
#total_rows ⇒ Object
Wrapper for the total_rows value provided by the query.
-
#values ⇒ Object
Convenience wrapper to provide all the values from the route set without having to go through
rows
.
Constructor Details
#initialize(parent, new_query = {}, name = nil) ⇒ View
Initialize a new View object. This method should not be called from outside CouchRest Model.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/couchrest/model/designs/view.rb', line 21 def initialize(parent, new_query = {}, name = nil) if parent.is_a?(Class) && parent < CouchRest::Model::Base raise "Name must be provided for view to be initialized" if name.nil? self.model = parent self.owner = parent self.name = name.to_s # Default options: self.query = { } elsif parent.is_a?(self.class) self.model = (new_query.delete(:proxy) || parent.model) self.owner = parent.owner self.name = parent.name self.query = parent.query.dup else raise "View cannot be initialized without a parent Model or View" end query.update(new_query) super() end |
Instance Attribute Details
#model ⇒ Object
Returns the value of attribute model.
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def model @model end |
#name ⇒ Object
Returns the value of attribute name.
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def name @name end |
#owner ⇒ Object
Returns the value of attribute owner.
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def owner @owner end |
#query ⇒ Object
Returns the value of attribute query.
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def query @query end |
#result ⇒ Object
Returns the value of attribute result.
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def result @result end |
Class Method Details
.create(model, name, opts = {}) ⇒ Object
Simplified view creation. A new view will be added to the provided model’s design document using the name and options.
If the view name starts with “by_” and :by
is not provided in the options, the new view’s map method will be interpreted and generated automatically. For example:
View.create(Meeting, "by_date_and_name")
Will create a view that searches by the date and name properties. Explicity setting the attributes to use is possible using the :by
option. For example:
View.create(Meeting, "by_date_and_name", :by => [:date, :firstname, :lastname])
The view name is the same, but three keys would be used in the subsecuent index.
By default, a check is made on each of the view’s keys to ensure they do not contain a nil value (‘null’ in javascript). This is probably what you want in most cases but sometimes in can be useful to create an index where nil is permited. Set the :allow_nil
option to true to remove this check.
Conversely, keys are not checked to see if they are empty or blank. If you’d like to enable this, set the :allow_blank
option to false. The default is true, empty strings are permited in the indexes.
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/couchrest/model/designs/view.rb', line 440 def create(model, name, opts = {}) unless opts[:map] if opts[:by].nil? && name.to_s =~ /^by_(.+)/ opts[:by] = $1.split(/_and_/) end raise "View cannot be created without recognised name, :map or :by options" if opts[:by].nil? opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank] opts[:guards] ||= [] opts[:guards].push "(doc['#{model.model_type_key}'] == '#{model.to_s}')" keys = opts[:by].map{|o| "doc['#{o}']"} emit = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]" opts[:guards] += keys.map{|k| "(#{k} != null)"} unless opts[:allow_nil] opts[:guards] += keys.map{|k| "(#{k} != '')"} unless opts[:allow_blank] opts[:map] = <<-EOF function(doc) { if (#{opts[:guards].join(' && ')}) { emit(#{emit}, 1); } } EOF opts[:reduce] = <<-EOF function(key, values, rereduce) { return sum(values); } EOF end model.design_doc['views'] ||= {} view = model.design_doc['views'][name.to_s] = { } view['map'] = opts[:map] view['reduce'] = opts[:reduce] if opts[:reduce] view end |
Instance Method Details
#[](value) ⇒ Object
Accept requests as if the view was an array. Used for backwards compatibity with older queries:
Model.all(:raw => true, :limit => 0)['total_rows']
In this example, the raw option will be ignored, and the total rows will still be accessible.
163 164 165 |
# File 'lib/couchrest/model/designs/view.rb', line 163 def [](value) execute[value] end |
#all ⇒ Object
Fetch all the documents the view can access. If the view has not already been prepared for including documents in the query, it will be added automatically and reset any previously cached results.
62 63 64 65 |
# File 'lib/couchrest/model/designs/view.rb', line 62 def all include_docs! docs end |
#count ⇒ Object
Perform a count operation based on the current view. If the view can be reduced, the reduce will be performed and return the first value. This is okay for most simple queries, but may provide unexpected results if your reduce method does not calculate the total number of documents in a result set.
Trying to use this method with the group option will raise an error.
If no reduce function is defined, a query will be performed to return the total number of rows, this is the equivalant of:
view.limit(0).total_rows
113 114 115 116 117 118 119 120 121 |
# File 'lib/couchrest/model/designs/view.rb', line 113 def count raise "View#count cannot be used with group options" if query[:group] if can_reduce? row = reduce.skip(0).limit(1).rows.first row.nil? ? 0 : row.value else limit(0).total_rows end end |
#current_page ⇒ Object
365 366 367 |
# File 'lib/couchrest/model/designs/view.rb', line 365 def current_page (offset_value / limit_value) + 1 end |
#database(value) ⇒ Object
Specify the database the view should use. If not defined, an attempt will be made to load its value from the model.
306 307 308 |
# File 'lib/couchrest/model/designs/view.rb', line 306 def database(value) update_query(:database => value) end |
#descending ⇒ Object
The results should be provided in descending order. If the startkey or endkey query options have already been seen set, calling this method will automatically swap the options around. If you don’t want this, simply set descending before any other option.
Descending is false by default, and this method cannot be undone once used, it has no inverse option.
249 250 251 252 253 254 255 256 |
# File 'lib/couchrest/model/designs/view.rb', line 249 def descending if query[:startkey] || query[:endkey] query[:startkey], query[:endkey] = query[:endkey], query[:startkey] elsif query[:startkey_docid] || query[:endkey_docid] query[:startkey_docid], query[:endkey_docid] = query[:endkey_docid], query[:startkey_docid] end update_query(:descending => true) end |
#docs ⇒ Object
Provide all the documents from the view. If the view has not been prepared with the include_docs
option, each document will be loaded individually.
70 71 72 |
# File 'lib/couchrest/model/designs/view.rb', line 70 def docs @docs ||= rows.map{|r| r.doc} end |
#each(&block) ⇒ Object
Run through each document provided by the #all
method. This is also used by the Enumerator mixin to provide all the standard ruby collection directly on the view.
133 134 135 |
# File 'lib/couchrest/model/designs/view.rb', line 133 def each(&block) all.each(&block) end |
#empty? ⇒ Boolean
Check to see if the array of documents is empty. This will perform the query and return all documents ready to use, if you don’t want to load anything, use #total_rows
or #count
instead.
126 127 128 |
# File 'lib/couchrest/model/designs/view.rb', line 126 def empty? all.empty? end |
#endkey(value) ⇒ Object
The opposite of #startkey
, finds all index entries whose key is before the value specified.
See the #startkey
method for more details and the #inclusive_end
option.
213 214 215 216 |
# File 'lib/couchrest/model/designs/view.rb', line 213 def endkey(value) raise "View#endkey cannot be used when key has been set" unless query[:key].nil? && query[:keys].nil? update_query(:endkey => value) end |
#endkey_doc(value) ⇒ Object
The result set should end at the position of the provided document. The value may be provided as an object that responds to the #id
call or a string.
221 222 223 |
# File 'lib/couchrest/model/designs/view.rb', line 221 def endkey_doc(value) update_query(:endkey_docid => value.is_a?(String) ? value : value.id) end |
#first ⇒ Object
If another request has been made on the view, this will return the first document in the set. If not, a new query object will be generated with a limit of 1 so that only the first document is loaded.
78 79 80 |
# File 'lib/couchrest/model/designs/view.rb', line 78 def first result ? all.first : limit(1).all.first end |
#group ⇒ Object
Control whether the reduce function reduces to a set of distinct keys or to a single result row.
By default the value is false, and can only be set when the view’s #reduce
option has been set.
285 286 287 288 |
# File 'lib/couchrest/model/designs/view.rb', line 285 def group raise "View#reduce must have been set before grouping is permitted" unless query[:reduce] update_query(:group => true) end |
#group_level(value) ⇒ Object
Will set the level the grouping should be performed to. As per the CouchDB API, it only makes sense when the index key is an array.
This will automatically set the group option.
294 295 296 |
# File 'lib/couchrest/model/designs/view.rb', line 294 def group_level(value) group.update_query(:group_level => value.to_i) end |
#include_docs ⇒ Object
298 299 300 |
# File 'lib/couchrest/model/designs/view.rb', line 298 def include_docs update_query.include_docs! end |
#info ⇒ Object
No yet implemented. Eventually this will provide a raw hash of the information CouchDB holds about the view.
169 170 171 |
# File 'lib/couchrest/model/designs/view.rb', line 169 def info raise "Not yet implemented" end |
#key(value) ⇒ Object
Find all entries in the index whose key matches the value provided.
Cannot be used when the #startkey
or #endkey
have been set.
184 185 186 187 |
# File 'lib/couchrest/model/designs/view.rb', line 184 def key(value) raise "View#key cannot be used when startkey or endkey have been set" unless query[:keys].nil? && query[:startkey].nil? && query[:endkey].nil? update_query(:key => value) end |
#keys(*keys) ⇒ Object
Keys is a special CouchDB option that will cause the view request to be POSTed including an array of keys. Only documents with the matching keys will be returned. This is much faster than sending multiple requests for a set non-consecutive documents.
If no values are provided, this method will act as a wrapper around the rows result set, providing an array of keys.
232 233 234 235 236 237 238 239 |
# File 'lib/couchrest/model/designs/view.rb', line 232 def keys(*keys) if keys.empty? rows.map{|r| r.key} else raise "View#keys cannot by used when key or startkey/endkey have been set" unless query[:key].nil? && query[:startkey].nil? && query[:endkey].nil? update_query(:keys => keys.first) end end |
#last ⇒ Object
Same as first but will order the view in descending order. This does not however reverse the search keys or the offset, so if you are using a startkey
and endkey
you might end up with unexpected results.
If in doubt, don’t use this method!
89 90 91 |
# File 'lib/couchrest/model/designs/view.rb', line 89 def last result ? all.last : limit(1).descending.all.last end |
#length ⇒ Object
Return the number of documents in the currently defined result set. Use #count
for the total number of documents regardless of the current limit defined.
96 97 98 |
# File 'lib/couchrest/model/designs/view.rb', line 96 def length docs.length end |
#limit(value) ⇒ Object
Limit the result set to the value supplied.
259 260 261 |
# File 'lib/couchrest/model/designs/view.rb', line 259 def limit(value) update_query(:limit => value) end |
#limit_value ⇒ Object
357 358 359 |
# File 'lib/couchrest/model/designs/view.rb', line 357 def limit_value query[:limit] end |
#num_pages ⇒ Object
361 362 363 |
# File 'lib/couchrest/model/designs/view.rb', line 361 def num_pages (total_count.to_f / limit_value).ceil end |
#offset ⇒ Object
Wrapper for the results offset. As per the CouchDB API, this may be nil if groups are used.
139 140 141 |
# File 'lib/couchrest/model/designs/view.rb', line 139 def offset execute['offset'] end |
#offset_value ⇒ Object
353 354 355 |
# File 'lib/couchrest/model/designs/view.rb', line 353 def offset_value query[:skip] end |
#page(page) ⇒ Object
Kaminari compatible pagination support
Based on the really simple support for scoped pagination in the the Kaminari gem, we provide compatible methods here to perform the same actions you’d expect.
336 337 338 |
# File 'lib/couchrest/model/designs/view.rb', line 336 def page(page) limit(owner.default_per_page).skip(owner.default_per_page * ([page.to_i, 1].max - 1)) end |
#per(num) ⇒ Object
340 341 342 343 344 345 346 347 |
# File 'lib/couchrest/model/designs/view.rb', line 340 def per(num) raise "View#page must be called before #per!" if limit_value.nil? || offset_value.nil? if (n = num.to_i) <= 0 self else limit(num).skip(offset_value / limit_value * n) end end |
#proxy(value) ⇒ Object
Set the view’s proxy that will be used instead of the model for any future searches. As soon as this enters the new object’s initializer it will be removed and replace the model object.
See the Proxyable mixin for more details.
317 318 319 |
# File 'lib/couchrest/model/designs/view.rb', line 317 def proxy(value) update_query(:proxy => value) end |
#reduce ⇒ Object
Use the reduce function on the view. If none is available this method will fail.
275 276 277 278 |
# File 'lib/couchrest/model/designs/view.rb', line 275 def reduce raise "Cannot reduce a view without a reduce method" unless can_reduce? update_query(:reduce => true, :include_docs => nil) end |
#reset! ⇒ Object
Return any cached values to their nil state so that any queries requested later will have a fresh set of data.
323 324 325 326 327 |
# File 'lib/couchrest/model/designs/view.rb', line 323 def reset! self.result = nil @rows = nil @docs = nil end |
#rows ⇒ Object
Return each row wrapped in a ViewRow object. Unlike the raw CouchDB request, this will provide an empty array if there are no results.
49 50 51 52 53 54 55 56 |
# File 'lib/couchrest/model/designs/view.rb', line 49 def rows return @rows if @rows if execute && result['rows'] @rows ||= result['rows'].map{|v| ViewRow.new(v, model)} else [ ] end end |
#skip(value = 0) ⇒ Object
Skip the number of entries in the index specified by value. This would be the equivilent of an offset in SQL.
The CouchDB documentation states that the skip option should not be used with large data sets as it is inefficient. Use the startkey_doc
method instead to skip ranges efficiently.
269 270 271 |
# File 'lib/couchrest/model/designs/view.rb', line 269 def skip(value = 0) update_query(:skip => value) end |
#startkey(value) ⇒ Object
Find all index keys that start with the value provided. May or may not be used in conjunction with the endkey
option.
When the #descending
option is used (not the default), the start and end keys should be reversed, as per the CouchDB API.
Cannot be used if the key has been set.
196 197 198 199 |
# File 'lib/couchrest/model/designs/view.rb', line 196 def startkey(value) raise "View#startkey cannot be used when key has been set" unless query[:key].nil? && query[:keys].nil? update_query(:startkey => value) end |
#startkey_doc(value) ⇒ Object
The result set should start from the position of the provided document. The value may be provided as an object that responds to the #id
call or a string.
204 205 206 |
# File 'lib/couchrest/model/designs/view.rb', line 204 def startkey_doc(value) update_query(:startkey_docid => value.is_a?(String) ? value : value.id) end |
#total_count ⇒ Object
349 350 351 |
# File 'lib/couchrest/model/designs/view.rb', line 349 def total_count @total_count ||= limit(nil).skip(nil).count end |
#total_rows ⇒ Object
Wrapper for the total_rows value provided by the query. As per the CouchDB API, this may be nil if groups are used.
145 146 147 |
# File 'lib/couchrest/model/designs/view.rb', line 145 def total_rows execute['total_rows'] end |
#values ⇒ Object
Convenience wrapper to provide all the values from the route set without having to go through rows
.
151 152 153 |
# File 'lib/couchrest/model/designs/view.rb', line 151 def values rows.map{|r| r.value} end |