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)
-
- (Object) design_doc
Returns the value of attribute design_doc.
-
- (Object) model
Returns the value of attribute model.
-
- (Object) name
Returns the value of attribute name.
-
- (Object) owner
Returns the value of attribute owner.
-
- (Object) query
Returns the value of attribute query.
-
- (Object) result
Returns the value of attribute result.
Class Method Summary (collapse)
- + (Object) create_model_methods(design_doc, name, opts = {})
-
+ (Object) define(design_doc, name, opts = {})
Simplified view definition.
- + (Object) define_and_create(design_doc, name, opts = {})
Instance Method Summary (collapse)
-
- (Object) [](value)
Accept requests as if the view was an array.
-
- (Object) all
Fetch all the documents the view can access.
-
- (Object) count
Perform a count operation based on the current view.
- - (Object) current_page
-
- (Object) database(value)
Specify the database the view should use.
-
- (Object) descending
The results should be provided in descending order.
-
- (Object) docs
Provide all the documents from the view.
-
- (Object) each(&block)
Run through each document provided by the #all method.
-
- (Boolean) empty?
Check to see if the array of documents is empty.
-
- (Object) endkey(value)
The opposite of #startkey, finds all index entries whose key is before the value specified.
-
- (Object) endkey_doc(value)
The result set should end at the position of the provided document.
-
- (Object) first
If another request has been made on the view, this will return the first document in the set.
-
- (Object) group
Control whether the reduce function reduces to a set of distinct keys or to a single result row.
-
- (Object) group_level(value)
Will set the level the grouping should be performed to.
- - (Object) include_docs
-
- (Object) info
No yet implemented.
-
- (View) initialize(design_doc, parent, new_query = {}, name = nil)
constructor
Initialize a new View object.
-
- (Object) key(value)
Find all entries in the index whose key matches the value provided.
-
- (Object) keys(*keys)
Keys is a special CouchDB option that will cause the view request to be POSTed including an array of keys.
-
- (Object) last
Same as first but will order the view in descending order.
-
- (Object) length
Return the number of documents in the currently defined result set.
-
- (Object) limit(value)
Limit the result set to the value supplied.
- - (Object) limit_value
- - (Object) num_pages
-
- (Object) offset
Wrapper for the results offset.
- - (Object) offset_value
-
- (Object) page(page)
Kaminari compatible pagination support.
- - (Object) per(num)
-
- (Object) proxy(value)
Set the view's proxy that will be used instead of the model for any future searches.
-
- (Object) reduce
Use the reduce function on the view.
-
- (Object) reset!
Return any cached values to their nil state so that any queries requested later will have a fresh set of data.
-
- (Object) rows
Return each row wrapped in a ViewRow object.
-
- (Object) skip(value = 0)
Skip the number of entries in the index specified by value.
-
- (Object) stale(value)
Allow the results of a query to be provided "stale".
-
- (Object) startkey(value)
Find all index keys that start with the value provided.
-
- (Object) startkey_doc(value)
The result set should start from the position of the provided document.
- - (Object) total_count
-
- (Object) total_rows
Wrapper for the total_rows value provided by the query.
-
- (Object) values
Convenience wrapper to provide all the values from the route set without having to go through rows.
Constructor Details
- (View) initialize(design_doc, parent, new_query = {}, name = nil)
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 40 41 |
# File 'lib/couchrest/model/designs/view.rb', line 21 def initialize(design_doc, parent, new_query = {}, name = nil) self.design_doc = design_doc proxy = new_query.delete(:proxy) if parent.is_a?(Class) && parent < CouchRest::Model::Base raise "Name must be provided for view to be initialized" if name.nil? self.model = (proxy || parent) self.owner = parent self.name = name.to_s # Default options: self.query = { } elsif parent.is_a?(self.class) self.model = (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
- (Object) design_doc
Returns the value of attribute design_doc
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def design_doc @design_doc end |
- (Object) model
Returns the value of attribute model
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def model @model end |
- (Object) name
Returns the value of attribute name
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def name @name end |
- (Object) owner
Returns the value of attribute owner
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def owner @owner end |
- (Object) query
Returns the value of attribute query
17 18 19 |
# File 'lib/couchrest/model/designs/view.rb', line 17 def query @query end |
- (Object) result
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
+ (Object) create_model_methods(design_doc, name, opts = {})
505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/couchrest/model/designs/view.rb', line 505 def create_model_methods(design_doc, name, opts = {}) method = design_doc.method_name design_doc.model.instance_eval <<-EOS, __FILE__, __LINE__ + 1 def #{name}(opts = {}) #{method}.view('#{name}', opts) end def find_#{name}(*key) #{name}.key(*key).first() end EOS end |
+ (Object) define(design_doc, name, opts = {})
Simplified view definition. A new view will be added to the provided 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.define(Meeting, design, "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.define(Meeting, design, "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.
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
# File 'lib/couchrest/model/designs/view.rb', line 455 def define(design_doc, name, opts = {}) model = design_doc.model # Is this an all view? if name.to_s == 'all' opts[:map] = <<-EOF function(doc) { if (doc['#{model.model_type_key}'] == '#{model.to_s}') { emit(doc._id, null); } } EOF elsif !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 if opts[:reduce].nil? opts[:reduce] = <<-EOF function(key, values, rereduce) { return sum(values); } EOF end end design_doc['views'] ||= {} view = design_doc['views'][name.to_s] = { } view['map'] = opts[:map] view['reduce'] = opts[:reduce] if opts[:reduce] view end |
+ (Object) define_and_create(design_doc, name, opts = {})
422 423 424 425 |
# File 'lib/couchrest/model/designs/view.rb', line 422 def define_and_create(design_doc, name, opts = {}) define(design_doc, name, opts) create_model_methods(design_doc, name, opts) end |
Instance Method Details
- (Object) [](value)
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.
165 166 167 |
# File 'lib/couchrest/model/designs/view.rb', line 165 def [](value) execute[value] end |
- (Object) all
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.
64 65 66 67 |
# File 'lib/couchrest/model/designs/view.rb', line 64 def all include_docs! docs end |
- (Object) count
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
115 116 117 118 119 120 121 122 123 |
# File 'lib/couchrest/model/designs/view.rb', line 115 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 |
- (Object) current_page
378 379 380 |
# File 'lib/couchrest/model/designs/view.rb', line 378 def current_page (offset_value / limit_value) + 1 end |
- (Object) database(value)
Specify the database the view should use. If not defined, an attempt will be made to load its value from the model.
319 320 321 |
# File 'lib/couchrest/model/designs/view.rb', line 319 def database(value) update_query(:database => value) end |
- (Object) descending
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.
251 252 253 254 255 256 257 258 |
# File 'lib/couchrest/model/designs/view.rb', line 251 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 |
- (Object) docs
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.
72 73 74 |
# File 'lib/couchrest/model/designs/view.rb', line 72 def docs @docs ||= rows.map{|r| r.doc} end |
- (Object) each(&block)
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.
135 136 137 |
# File 'lib/couchrest/model/designs/view.rb', line 135 def each(&block) all.each(&block) end |
- (Boolean) empty?
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.
128 129 130 |
# File 'lib/couchrest/model/designs/view.rb', line 128 def empty? all.empty? end |
- (Object) endkey(value)
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.
215 216 217 218 |
# File 'lib/couchrest/model/designs/view.rb', line 215 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 |
- (Object) endkey_doc(value)
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.
223 224 225 |
# File 'lib/couchrest/model/designs/view.rb', line 223 def endkey_doc(value) update_query(:endkey_docid => value.is_a?(String) ? value : value.id) end |
- (Object) first
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.
80 81 82 |
# File 'lib/couchrest/model/designs/view.rb', line 80 def first result ? all.first : limit(1).all.first end |
- (Object) group
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.
287 288 289 290 |
# File 'lib/couchrest/model/designs/view.rb', line 287 def group raise "View#reduce must have been set before grouping is permitted" unless query[:reduce] update_query(:group => true) end |
- (Object) group_level(value)
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.
296 297 298 |
# File 'lib/couchrest/model/designs/view.rb', line 296 def group_level(value) group.update_query(:group_level => value.to_i) end |
- (Object) include_docs
300 301 302 |
# File 'lib/couchrest/model/designs/view.rb', line 300 def include_docs update_query.include_docs! end |
- (Object) info
No yet implemented. Eventually this will provide a raw hash of the information CouchDB holds about the view.
171 172 173 |
# File 'lib/couchrest/model/designs/view.rb', line 171 def info raise "Not yet implemented" end |
- (Object) key(value)
Find all entries in the index whose key matches the value provided.
Cannot be used when the #startkey or #endkey have been set.
186 187 188 189 |
# File 'lib/couchrest/model/designs/view.rb', line 186 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 |
- (Object) keys(*keys)
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.
234 235 236 237 238 239 240 241 |
# File 'lib/couchrest/model/designs/view.rb', line 234 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 |
- (Object) last
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!
91 92 93 |
# File 'lib/couchrest/model/designs/view.rb', line 91 def last result ? all.last : limit(1).descending.all.last end |
- (Object) length
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.
98 99 100 |
# File 'lib/couchrest/model/designs/view.rb', line 98 def length docs.length end |
- (Object) limit(value)
Limit the result set to the value supplied.
261 262 263 |
# File 'lib/couchrest/model/designs/view.rb', line 261 def limit(value) update_query(:limit => value) end |
- (Object) limit_value
370 371 372 |
# File 'lib/couchrest/model/designs/view.rb', line 370 def limit_value query[:limit] end |
- (Object) num_pages
374 375 376 |
# File 'lib/couchrest/model/designs/view.rb', line 374 def num_pages (total_count.to_f / limit_value).ceil end |
- (Object) offset
Wrapper for the results offset. As per the CouchDB API, this may be nil if groups are used.
141 142 143 |
# File 'lib/couchrest/model/designs/view.rb', line 141 def offset execute['offset'] end |
- (Object) offset_value
366 367 368 |
# File 'lib/couchrest/model/designs/view.rb', line 366 def offset_value query[:skip] end |
- (Object) page(page)
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.
349 350 351 |
# File 'lib/couchrest/model/designs/view.rb', line 349 def page(page) limit(owner.default_per_page).skip(owner.default_per_page * ([page.to_i, 1].max - 1)) end |
- (Object) per(num)
353 354 355 356 357 358 359 360 |
# File 'lib/couchrest/model/designs/view.rb', line 353 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 |
- (Object) proxy(value)
Set the view's proxy that will be used instead of the model for any future searches. As soon as this enters the new view's initializer it will be removed and set as the model object.
See the Proxyable mixin for more details.
330 331 332 |
# File 'lib/couchrest/model/designs/view.rb', line 330 def proxy(value) update_query(:proxy => value) end |
- (Object) reduce
Use the reduce function on the view. If none is available this method will fail.
277 278 279 280 |
# File 'lib/couchrest/model/designs/view.rb', line 277 def reduce raise "Cannot reduce a view without a reduce method" unless can_reduce? update_query(:reduce => true, :include_docs => nil) end |
- (Object) reset!
Return any cached values to their nil state so that any queries requested later will have a fresh set of data.
336 337 338 339 340 |
# File 'lib/couchrest/model/designs/view.rb', line 336 def reset! self.result = nil @rows = nil @docs = nil end |
- (Object) rows
Return each row wrapped in a ViewRow object. Unlike the raw CouchDB request, this will provide an empty array if there are no results.
51 52 53 54 55 56 57 58 |
# File 'lib/couchrest/model/designs/view.rb', line 51 def rows return @rows if @rows if execute && result['rows'] @rows ||= result['rows'].map{|v| ViewRow.new(v, model)} else [ ] end end |
- (Object) skip(value = 0)
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.
271 272 273 |
# File 'lib/couchrest/model/designs/view.rb', line 271 def skip(value = 0) update_query(:skip => value) end |
- (Object) stale(value)
Allow the results of a query to be provided "stale". Setting to 'ok' will disable all view updates for the query. When 'update_after' is provided the index will be update after the result has been returned.
310 311 312 313 314 315 |
# File 'lib/couchrest/model/designs/view.rb', line 310 def stale(value) unless (['ok', 'update_after'].include?(value.to_s)) raise "View#stale can only be set with 'ok' or 'update_after'." end update_query(:stale => value.to_s) end |
- (Object) startkey(value)
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.
198 199 200 201 |
# File 'lib/couchrest/model/designs/view.rb', line 198 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 |
- (Object) startkey_doc(value)
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.
206 207 208 |
# File 'lib/couchrest/model/designs/view.rb', line 206 def startkey_doc(value) update_query(:startkey_docid => value.is_a?(String) ? value : value.id) end |
- (Object) total_count
362 363 364 |
# File 'lib/couchrest/model/designs/view.rb', line 362 def total_count @total_count ||= limit(nil).skip(nil).count end |
- (Object) total_rows
Wrapper for the total_rows value provided by the query. As per the CouchDB API, this may be nil if groups are used.
147 148 149 |
# File 'lib/couchrest/model/designs/view.rb', line 147 def total_rows execute['total_rows'] end |
- (Object) values
Convenience wrapper to provide all the values from the route set without having to go through rows.
153 154 155 |
# File 'lib/couchrest/model/designs/view.rb', line 153 def values rows.map{|r| r.value} end |