Skip to content

Commit adbb630

Browse files
committed
Changed sqlpp and views pages
1 parent 2f17cc4 commit adbb630

File tree

2 files changed

+193
-58
lines changed

2 files changed

+193
-58
lines changed

docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
CouchbaseOrm provides support for executing SQL++ queries directly from your Ruby code. SQL++ (or formerly N1QL,i.e. Non-First Normal Form Query Language) is a powerful query language that allows you to perform complex queries and aggregations on your Couchbase data.
44

5-
## Defining SQL++ Queries
5+
## 7.1 Defining SQL++ Queries
66

77
To define an SQL++ query in your model, you can use the `n1ql` macro provided by CouchbaseOrm. Here are a few examples:
88

@@ -11,44 +11,62 @@ class N1QLTest < CouchbaseOrm::Base
1111
attribute :name, type: String
1212
attribute :lastname, type: String
1313
enum rating: %i[awesome good okay bad], default: :okay
14+
attribute :country, type: String
1415

16+
# Example 1: Custom query with specific rating values
1517
n1ql :by_custom_rating, emit_key: [:rating], query_fn: proc { |bucket, _values, options|
1618
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` WHERE type = 'n1_ql_test' AND rating IN [1, 2] ORDER BY name ASC", options)
1719
}
1820

21+
# Example 2: Simple query by name
1922
n1ql :by_name, emit_key: [:name]
2023

24+
# Example 3: Simple query by lastname
2125
n1ql :by_lastname, emit_key: [:lastname]
2226

27+
# Example 4: Custom query by country with parameter binding
28+
n1ql :by_country, emit_key: [:country], query_fn: proc { |bucket, values, options|
29+
cluster.query(
30+
"SELECT raw meta().id FROM `#{bucket.name}` WHERE type = 'n1_ql_test' AND country = $country ORDER BY name ASC",
31+
Couchbase::Options::Query(named_parameters: { country: values[0] })
32+
)
33+
}
34+
35+
# Example 5: Simple query by rating
2336
n1ql :by_rating_emit, emit_key: :rating
2437

38+
# Example 6: Custom query by rating with parameter binding
2539
n1ql :by_custom_rating_values, emit_key: [:rating], query_fn: proc { |bucket, values, options|
26-
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` where type = 'n1_ql_test' AND rating IN #{quote(values[0])} ORDER BY name ASC", options)
27-
}
40+
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` where type = 'n1_ql_test' AND rating IN #{quote(values[0])} ORDER BY name ASC", options)
41+
}
42+
43+
# Example 7: Custom query by rating with custom ordering
2844
n1ql :by_rating_reverse, emit_key: :rating, custom_order: 'name DESC'
2945

46+
# Example 8: Simple query by rating without including documents
3047
n1ql :by_rating_without_docs, emit_key: :rating, include_docs: false
3148

49+
# Index definition for the rating attribute
3250
index_n1ql :rating
3351
end
3452

3553
```
3654

3755
In these examples:
3856

39-
- The `by_custom_rating` query selects the document IDs where the `type` is `'n1_ql_test'` and the `rating` is either 1 or 2, ordered by the `name` attribute in ascending order.
57+
- The `by_custom_rating` query selects the document IDs where the type is 'n1_ql_test' and the rating is either 1 or 2, ordered by the name attribute in ascending order.
4058

41-
- The `by_name`, `by_lastname`, and `by_rating_emit` queries use the `emit_key` option to specify the attribute to be used as the key for the query.
59+
- The `by_name`, `by_lastname`, and `by_rating_emit` queries use the emit_key option to specify the attribute to be used as the key for the query.
4260

43-
- The `by_custom_rating_values` query demonstrates passing values to the query using placeholders (`$1`, `$2`, etc.) and the `quote` method to properly escape the values.
61+
- The `by_custom_rating_values` query demonstrates passing values to the query using placeholders ($1, $2, etc.) and the quote method to properly escape the values.
4462

45-
- The `by_rating_reverse` query uses the `custom_order` option to specify a custom ordering for the query results.
63+
- The `by_rating_reverse` query uses the custom_order option to specify a custom ordering for the query results.
4664

47-
- The `by_rating_without_docs` query sets `include_docs` to `false` to retrieve only the document IDs without fetching the full documents.
65+
- The `by_rating_without_docs` query sets include_docs to false to retrieve only the document IDs without fetching the full documents.
4866

49-
- The `index_n1ql` macro is used to define an index on the `rating` attribute and generate a corresponding query method.
67+
- The `index_n1ql` macro is used to define an index on the rating attribute and generate a corresponding query method. Note that the index_n1ql macro does not create the index in the Couchbase Dashboard directly; you would need to create the index separately using the Couchbase Dashboard or the Couchbase Query UI.
5068

51-
## Query Parameters
69+
## 7.2 Query Parameters
5270

5371
SQL++ queries often require parameters to be passed in order to filter or customize the results. CouchbaseOrm allows you to define queries with placeholders and provide the parameter values when executing the query.
5472

@@ -60,7 +78,7 @@ n1ql :by_custom_rating_values, emit_key: [:rating], query_fn: proc { |bucket, va
6078

6179
In this example, the `values` parameter represents an array of values passed to the query. The `quote` method is used to properly escape and format the values for use in the query.
6280

63-
## Executing SQL++ Queries
81+
## 7.3 Executing SQL++ Queries
6482

6583
To execute an SQL++ query, you simply call the defined query method on your model class.
6684

@@ -70,7 +88,7 @@ docs = N1QLTest.by_custom_rating().collect { |ob| ob.name }
7088

7189
CouchbaseOrm automatically executes the SQL++ query and returns the result set, which you can then process as needed.
7290

73-
## Query Options
91+
## 7.4 Query Options
7492

7593
CouchbaseOrm provides various options to customize the execution of SQL++ queries. These options can be passed as a hash parameter to the query method.
7694

@@ -86,19 +104,24 @@ docs = N1QLTest.by_rating_reverse(key: 1)
86104
- `include_docs`: Specifies whether to include the full document content in the results.
87105
- `scan_consistency`: Specifies the consistency level for the query (`request_plus` by default). -->
88106

89-
## Placeholder Values
107+
## 7.5 Placeholder Values
90108

91-
In addition to using positional placeholders (`$1`, `$2`, etc.), CouchbaseOrm also supports named placeholders for better readability.
109+
You can use placeholder values in your SQL++ queries and pass the actual values when executing the query.
92110

93111
```ruby
94-
n1ql :by_name_and_rating, "SELECT * FROM `#{bucket.name}` WHERE name = $name AND rating = $rating"
112+
n1ql :by_country, emit_key: [:country], query_fn: proc { |bucket, values, options|
113+
cluster.query(
114+
"SELECT raw meta().id FROM `#{bucket.name}` WHERE type = 'n1_ql_test' AND country = $country ORDER BY name ASC",
115+
Couchbase::Options::Query(named_parameters: { country: values[0] })
116+
)
117+
}
95118

96-
docs = N1QLTest.by_name_and_rating(name: 'John', rating: 'awesome')
119+
docs = N1QLTest.by_country(key: 'USA').collect { |ob| ob.name }
97120
```
98121

99-
In this example, the `$name` and `$rating` placeholders are used in the query, and the values are provided using a hash parameter with the corresponding keys.
122+
In this example, the `by_country` query uses a placeholder `$country` in the query string and passes the actual value `'USA'` as the parameter value when executing the query.
100123

101-
## Query Result Processing
124+
## 7.6 Query Result Processing
102125

103126
By default, CouchbaseOrm automatically maps the query result to instances of your model class. However, you can also process the query result manually if needed.
104127

@@ -124,11 +147,19 @@ docs = N1QLTest.by_custom_rating_values(key: [[1, 2]]).collect { |ob| ob.name }
124147

125148
In the above examples, the `collect` method is used to extract the `name` attribute from each document in the result set.
126149

127-
## Indexing for SQL++
150+
## 7.7 Indexing for SQL++
128151

129152
To optimize the performance of SQL++ queries, it's important to create appropriate indexes on the fields used in the query conditions. Couchbase Server provides a way to create indexes using the Index service.
130153

131154
```ruby
155+
156+
class N1QLTest < CouchbaseOrm::Base
157+
...
158+
159+
# Index definition for the rating attribute
160+
index_n1ql :rating
161+
end
162+
132163
# Query using index_n1ql
133164
docs = N1QLTest.find_by_rating(2).collect { |ob| ob.name }
134165

Lines changed: 143 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,158 @@
11
# Views (aka Map/Reduce indexes)
22

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:
534

635
```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+
}
2644
```
2745

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.
3147

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:
3351

3452
```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
3662

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
3867

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
4272
```
4373

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:
4581

4682
```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

Comments
 (0)