|
1 | 1 | # Views (aka Map/Reduce indexes) |
2 | 2 |
|
3 | | -Views are defined in the model and typically just emit an attribute that |
4 | | -can then be used for filtering results or ordering. |
| 3 | +Views are a powerful feature in Couchbase that allow you to define map functions to extract and emit specific data from your documents. The Ruby Couchbase ORM provides a convenient way to define and query views within your Ruby application. |
| 4 | + |
| 5 | +## 8.1 Defining Views |
| 6 | + |
| 7 | +To define a view in your Ruby Couchbase ORM model, you can use the `view` method followed by the view name and options. Here's an example: |
| 8 | + |
| 9 | +```ruby |
| 10 | +class Factory < CouchbaseOrm::Base |
| 11 | + attribute :name, type: String |
| 12 | + attribute :location, type: String |
| 13 | + attribute :established_year, type: Integer |
| 14 | + attribute :active, type: Boolean |
| 15 | + |
| 16 | + view :by_name, emit_key: :name |
| 17 | + view :by_location, emit_key: :location |
| 18 | + view :by_established_year, emit_key: :established_year |
| 19 | + view :by_active, emit_key: :active |
| 20 | +end |
| 21 | +``` |
| 22 | + |
| 23 | +In this example, we define four views for the `Factory` model: |
| 24 | +- `by_name`: Emits the `name` attribute as the key. |
| 25 | +- `by_location`: Emits the `location` attribute as the key. |
| 26 | +- `by_established_year`: Emits the `established_year` attribute as the key. |
| 27 | +- `by_active`: Emits the `active` attribute as the key. |
| 28 | + |
| 29 | +The `emit_key` option specifies the attribute to use as the key for the view. |
| 30 | + |
| 31 | +## 8.2 Custom Map Functions |
| 32 | + |
| 33 | +You can also define custom map functions for your views using the `map` option. This allows you to emit custom keys and values based on your specific requirements. Here's an example: |
5 | 34 |
|
6 | 35 | ```ruby |
7 | | - class Comment < CouchbaseOrm::Base |
8 | | - attribute :author :string |
9 | | - attribute :body, :string |
10 | | - view :all # => emits :id and will return all comments |
11 | | - view :by_author, emit_key: :author |
12 | | - |
13 | | - # Generates two functions: |
14 | | - # * the by_author view above |
15 | | - # * def find_by_author(author); end |
16 | | - index_view :author |
17 | | - |
18 | | - # You can make compound keys by passing an array to :emit_key |
19 | | - # this allow to query by read/unread comments |
20 | | - view :by_read, emit_key: [:user_id, :read] |
21 | | - # this allow to query by view_count |
22 | | - view :by_view_count, emit_key: [:user_id, :view_count] |
23 | | - |
24 | | - validates_presence_of :author, :body |
25 | | - end |
| 36 | +view :by_name_and_location, |
| 37 | + map: %{ |
| 38 | + function(doc) { |
| 39 | + if (doc.type === "factory") { |
| 40 | + emit([doc.name, doc.location], null); |
| 41 | + } |
| 42 | + } |
| 43 | + } |
26 | 44 | ``` |
27 | 45 |
|
28 | | -You can use `Comment.find_by_author('name')` to obtain all the comments by |
29 | | -a particular author. The same thing, using the view directly would be: |
30 | | -`Comment.by_author(key: 'name')` |
| 46 | +In this example, we define a view named `by_name_and_location` that emits a composite key consisting of the `name` and `location` attributes. |
31 | 47 |
|
32 | | -When using a compound key, the usage is the same, you just give the full key : |
| 48 | +## 8.3 Querying Views |
| 49 | + |
| 50 | +Once you have defined your views, you can query them using the corresponding view methods. The view methods are automatically generated based on the view names. Here are some examples: |
33 | 51 |
|
34 | 52 | ```ruby |
35 | | - Comment.by_read(key: '["'+user_id+'",false]') # gives all unread comments for one particular user |
| 53 | +# Query factories by name |
| 54 | +Factory.by_name(key: 'Factory A').each do |factory| |
| 55 | + puts "- #{factory.name}" |
| 56 | +end |
| 57 | + |
| 58 | +# Query factories by location |
| 59 | +Factory.by_location(key: 'City X').each do |factory| |
| 60 | + puts "- #{factory.name}" |
| 61 | +end |
36 | 62 |
|
37 | | - # or even a range ! |
| 63 | +# Query factories by established year |
| 64 | +Factory.by_established_year(key: 2010).each do |factory| |
| 65 | + puts "- #{factory.name}" |
| 66 | +end |
38 | 67 |
|
39 | | - Comment.by_view_count(startkey: '["'+user_id+'",10]', endkey: '["'+user_id+'",20]') |
40 | | - |
41 | | - # gives all comments that have been seen more than 10 times but less than 20 |
| 68 | +# Query active factories |
| 69 | +Factory.by_active(key: true).each do |factory| |
| 70 | + puts "- #{factory.name}" |
| 71 | +end |
42 | 72 | ``` |
43 | 73 |
|
44 | | -Ex : Compound keys allows to decide the order of the results, and you can reverse it by passing `descending: true` |
| 74 | +These examples demonstrate how to query views based on specific keys using the generated view methods. |
| 75 | + |
| 76 | +## Indexing Views |
| 77 | + |
| 78 | +In this section, let's explore the `index_view` function and the methods it generates for the `location` attribute. |
| 79 | + |
| 80 | +Here's an updated version of the `Factory` model using `index_view` for the `location` attribute: |
45 | 81 |
|
46 | 82 | ```ruby |
47 | | - class Comment < CouchbaseOrm::Base19 |
48 | | - self.ignored_properties = [:old_name] # ignore old_name property in the model |
49 | | - self.properties_always_exists_in_document = true # use is null for nil value instead of not valued for performance purpose, only possible if all properties always exists in document |
50 | | - end |
51 | | -``` |
52 | | -You can specify `properties_always_exists_in_document` to true if all properties always exists in document, this will allow to use `is null` instead of `not valued` for nil value, this will improve performance. |
53 | | - |
54 | | -WARNING: If a document exists without a property, the query will failed! So you must be sure that all documents have all properties. |
| 83 | +class Factory < CouchbaseOrm::Base |
| 84 | + attribute :name, type: String |
| 85 | + attribute :location, type: String |
| 86 | + attribute :established_year, type: Integer |
| 87 | + attribute :active, type: Boolean |
| 88 | + |
| 89 | + view :by_name, emit_key: :name |
| 90 | + index_view :location |
| 91 | + view :by_established_year, emit_key: :established_year |
| 92 | + view :by_active, emit_key: :active |
| 93 | + |
| 94 | + view :by_name_and_location, |
| 95 | + map: %{ |
| 96 | + function(doc) { |
| 97 | + if (doc.type === "factory") { |
| 98 | + emit([doc.name, doc.location], null); |
| 99 | + } |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + view :by_established_year_range, |
| 104 | + map: %{ |
| 105 | + function(doc) { |
| 106 | + if (doc.type === "factory" && doc.established_year) { |
| 107 | + emit(doc.established_year, null); |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | +end |
| 112 | +``` |
| 113 | + |
| 114 | +In this updated code, we replaced `view :by_location, emit_key: :location` with `index_view :location`. The `index_view` function generates two methods for querying based on the `location` attribute: |
| 115 | + |
| 116 | +1. `find_by_location(location)`: This method allows you to find factories by a specific location. It returns an array of factories that match the given location. |
| 117 | + |
| 118 | +2. `by_location(key: location)`: This method is similar to `find_by_location` but returns a `CouchbaseOrm::ResultsProxy` object, which provides an enumerable interface to access the view results. |
| 119 | + |
| 120 | +Now, let's see how we can use these generated methods to query factories by location: |
| 121 | + |
| 122 | +```ruby |
| 123 | +# Find factories by location using find_by_location |
| 124 | +factories = Factory.find_by_location('City X') |
| 125 | +factories.each do |factory| |
| 126 | + puts "- #{factory.name} (#{factory.location})" |
| 127 | +end |
| 128 | + |
| 129 | +# Find factories by location using by_location |
| 130 | +Factory.by_location(key: 'City X').each do |factory| |
| 131 | + puts "- #{factory.name} (#{factory.location})" |
| 132 | +end |
| 133 | +``` |
| 134 | + |
| 135 | +Both `find_by_location` and `by_location` achieve the same result of finding factories by a specific location. The difference is that `find_by_location` returns an array of factories directly, while `by_location` returns a `CouchbaseOrm::ResultsProxy` object that you can further chain or iterate over. |
| 136 | + |
| 137 | +Using `index_view` provides a convenient way to generate commonly used query methods for a specific attribute. It simplifies the process of querying based on that attribute and enhances the readability of your code. |
| 138 | + |
| 139 | +<!-- ## 8.5 Range Queries (Experimental) |
| 140 | +
|
| 141 | +You can perform range queries on views by specifying the `startkey` and `endkey` options. Here's an example: |
| 142 | +
|
| 143 | +```ruby |
| 144 | +# Query factories established between 2005 and 2015 |
| 145 | +Factory.by_established_year_range(startkey: 2005, endkey: 2015).each do |factory| |
| 146 | + puts "- #{factory.name} (#{factory.established_year})" |
| 147 | +end |
| 148 | +``` |
| 149 | +
|
| 150 | +In this example, we query the `by_established_year_range` view to retrieve factories established between 2005 and 2015. --> |
| 151 | + |
| 152 | +## 8.5 Conclusion |
| 153 | + |
| 154 | +Views in the Ruby Couchbase ORM provide a powerful way to define and query data based on specific attributes and custom map functions. By defining views, you can easily retrieve subsets of your data and perform efficient queries based on your application's requirements. |
| 155 | + |
| 156 | +Remember to ensure that your design documents are up to date by calling `ensure_design_document!` on your models before running queries. |
| 157 | + |
| 158 | +With the flexibility and ease of use provided by the Ruby Couchbase ORM's view functionality, you can efficiently query and retrieve data from your Couchbase database. |
0 commit comments