couchbase-orm 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/deploy.yml +45 -0
- data/.github/workflows/test.yml +18 -13
- data/README.md +12 -0
- data/couchbase-orm.gemspec +2 -2
- data/docusaurus/.gitignore +20 -0
- data/docusaurus/README.md +41 -0
- data/docusaurus/babel.config.js +3 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/01-introduction.md +49 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/02-installation.md +108 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/03-defining-models.md +239 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/04-querying.md +154 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/05-persistence.md +93 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/06-associations-and-validations.md +236 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md +180 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/08-views.md +158 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/09-nested-documents.md +138 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/10-enums.md +91 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/11-encryption.md +114 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/12-logging.md +48 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/13-troubleshooting.md +41 -0
- data/docusaurus/docs/tutorial-ruby-couchbase-orm/_category_.json +8 -0
- data/docusaurus/docusaurus.config.ts +122 -0
- data/docusaurus/package-lock.json +14540 -0
- data/docusaurus/package.json +47 -0
- data/docusaurus/sidebars.ts +31 -0
- data/docusaurus/src/components/HomepageFeatures/index.tsx +69 -0
- data/docusaurus/src/components/HomepageFeatures/styles.module.css +11 -0
- data/docusaurus/src/css/custom.css +30 -0
- data/docusaurus/src/pages/index.module.css +23 -0
- data/docusaurus/src/pages/index.tsx +43 -0
- data/docusaurus/src/pages/markdown-page.md +7 -0
- data/docusaurus/static/.nojekyll +0 -0
- data/docusaurus/static/CNAME +1 -0
- data/docusaurus/static/img/familiar.svg +1 -0
- data/docusaurus/static/img/fast.svg +1 -0
- data/docusaurus/static/img/logo.svg +1 -0
- data/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
- data/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
- data/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
- data/docusaurus/tsconfig.json +7 -0
- data/lib/couchbase-orm/types/date_time.rb +2 -1
- data/lib/couchbase-orm/utilities/query_helper.rb +1 -1
- data/lib/couchbase-orm/version.rb +1 -1
- metadata +43 -10
@@ -0,0 +1,154 @@
|
|
1
|
+
# Querying
|
2
|
+
|
3
|
+
CouchbaseOrm provides a powerful and expressive query interface for retrieving data from Couchbase Server. With CouchbaseOrm, you can easily construct queries using a fluent and intuitive API that resembles the querying capabilities of ActiveRecord.
|
4
|
+
|
5
|
+
## 4.1. Finding Records
|
6
|
+
|
7
|
+
CouchbaseOrm offers various methods to find records based on different criteria. Here are some commonly used methods:
|
8
|
+
|
9
|
+
- `find`: Finds a record by its primary key (ID).
|
10
|
+
- `find_by`: Finds the first record that matches the specified attribute-value pair.
|
11
|
+
- `where`: Retrieves records that match the specified conditions.
|
12
|
+
|
13
|
+
Here are some examples of finding records:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# Define an Author model
|
17
|
+
class Author < CouchbaseOrm::Base
|
18
|
+
attribute :name, :string
|
19
|
+
attribute :age, :integer
|
20
|
+
attribute :active, :boolean, default: true
|
21
|
+
|
22
|
+
validates :name, presence: true
|
23
|
+
validates :age, numericality: { greater_than_or_equal_to: 18 }
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create new authors
|
28
|
+
author1 = Author.new(name: 'John Doe', age: 30, active: true)
|
29
|
+
author2 = Author.new(name: 'Jane Smith', age: 25, active: false)
|
30
|
+
author3 = Author.new(name: 'Alice Brown', age: 40, active: true)
|
31
|
+
author4 = Author.new(name: 'Bob Johnson', age: 17, active: true)
|
32
|
+
|
33
|
+
# Save authors
|
34
|
+
author1.save
|
35
|
+
author2.save
|
36
|
+
author3.save
|
37
|
+
author4.save
|
38
|
+
|
39
|
+
# Find authors by ID
|
40
|
+
puts Author.find(author1.id).inspect
|
41
|
+
|
42
|
+
# Find the first author with a specific name
|
43
|
+
puts Author.find_by(name: 'John Doe').inspect
|
44
|
+
|
45
|
+
# Where
|
46
|
+
puts Author.where(active: true).to_a.inspect
|
47
|
+
```
|
48
|
+
|
49
|
+
## 4.2. Where Clauses
|
50
|
+
|
51
|
+
The `where` method allows you to specify conditions to filter the records based on attribute values. You can chain multiple `where` clauses together to build more complex queries.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# Where
|
55
|
+
puts Author.where(active: true).where('age >= 30').to_a.inspect
|
56
|
+
|
57
|
+
puts Author.where("name like '%John%'").to_a.inspect
|
58
|
+
```
|
59
|
+
|
60
|
+
CouchbaseOrm supports various comparison operators and placeholders in the `where` clauses, such as `=`, `>`, `<`, `>=`, `<=`, `LIKE`, and more.
|
61
|
+
|
62
|
+
## 4.3. Ordering
|
63
|
+
|
64
|
+
You can specify the order in which the retrieved records should be sorted using the `order` method. Pass the attribute name and the desired sort direction (`:asc` for ascending, `:desc` for descending).
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# Order authors by name
|
68
|
+
puts Author.order(:name).to_a.inspect
|
69
|
+
|
70
|
+
# Order authors by age in descending order
|
71
|
+
puts Author.order(age: :desc).to_a.inspect
|
72
|
+
```
|
73
|
+
|
74
|
+
You can also chain multiple `order` clauses to sort by multiple attributes.
|
75
|
+
|
76
|
+
|
77
|
+
Scopes provide a clean and DRY way to encapsulate commonly used query conditions.
|
78
|
+
|
79
|
+
## 4.4. Pluck
|
80
|
+
|
81
|
+
The `pluck` method allows you to retrieve specific attributes from the matched records instead of loading the entire objects. It returns an array of values for the specified attributes.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# Pluck names of all authors
|
85
|
+
puts Author.order(:name).pluck(:name).inspect
|
86
|
+
```
|
87
|
+
|
88
|
+
## 4.5. Destroying Records
|
89
|
+
|
90
|
+
To delete multiple records that match specific conditions, you can use the `.each(&:destroy)` method. It deletes the records from the database and returns the number of records deleted.
|
91
|
+
|
92
|
+
- `:destroy`: Deletes a single record.
|
93
|
+
- `delete_all`: Deletes all records that match the specified conditions.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# Destroy all inactive authors
|
97
|
+
Author.where(active: false).each(&:destroy)
|
98
|
+
|
99
|
+
# Destroy all authors who are inactive
|
100
|
+
Author.where(active: false).delete_all
|
101
|
+
|
102
|
+
```
|
103
|
+
|
104
|
+
|
105
|
+
These are just a few examples of the querying capabilities provided by CouchbaseOrm. You can combine these methods in various ways to construct complex and specific queries based on your application's requirements.
|
106
|
+
|
107
|
+
In the next section, we'll explore how to use CouchbaseOrm to create, update, and delete records in Couchbase Server.
|
108
|
+
|
109
|
+
## 4.6. Scopes
|
110
|
+
|
111
|
+
Scopes allow you to define reusable query snippets that can be chained with other query methods. Scopes are defined as class methods within your model.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# Define a Comment model
|
115
|
+
class Comment < CouchbaseOrm::Base
|
116
|
+
attribute :title, :string
|
117
|
+
attribute :author, :string
|
118
|
+
attribute :category, :string
|
119
|
+
attribute :ratings, :integer
|
120
|
+
|
121
|
+
def self.by_author(author)
|
122
|
+
where(author: author)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.highly_rated
|
126
|
+
where('ratings > 3')
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.in_category(category)
|
130
|
+
where(category: category)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# Create some comments
|
136
|
+
comment1 = Comment.new(title: 'First Comment', author: 'Anne McCaffrey', category: 'S-F', ratings: 5)
|
137
|
+
comment2 = Comment.new(title: 'Second Comment', author: 'Anne McCaffrey', category: 'S-F', ratings: 4)
|
138
|
+
comment3 = Comment.new(title: 'Third Comment', author: 'Anne McCaffrey', category: 'S-F', ratings: 3)
|
139
|
+
comment4 = Comment.new(title: 'Fourth Comment', author: 'Anne McCaffrey', category: 'S-F', ratings: 2)
|
140
|
+
|
141
|
+
# Save the comments
|
142
|
+
comment1.save
|
143
|
+
comment2.save
|
144
|
+
comment3.save
|
145
|
+
comment4.save
|
146
|
+
|
147
|
+
# Example usage of scopes
|
148
|
+
comments = Comment.by_author("Anne McCaffrey").in_category('S-F').highly_rated.order(:title).limit(10)
|
149
|
+
|
150
|
+
# Iterate over the comments
|
151
|
+
comments.each do |comment|
|
152
|
+
puts "Title: #{comment.title}, Author: #{comment.author}, Category: #{comment.category}, Ratings: #{comment.ratings}"
|
153
|
+
end
|
154
|
+
```
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Persistence
|
2
|
+
|
3
|
+
CouchbaseOrm provides a simple and intuitive way to persist data to Couchbase Server. With CouchbaseOrm, you can easily create, update, save, and destroy records using a set of built-in methods.
|
4
|
+
|
5
|
+
## 5.1. Creating Records
|
6
|
+
|
7
|
+
To create a new record, you can instantiate a new instance of your model class and then call the `save` method to persist it to the database.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class Task < CouchbaseOrm::Base
|
11
|
+
attribute :title, :string
|
12
|
+
attribute :description, :string
|
13
|
+
attribute :completed, :boolean, default: false
|
14
|
+
end
|
15
|
+
|
16
|
+
task = Task.new(title: 'Task 1', description: 'Description of Task 1')
|
17
|
+
task.save
|
18
|
+
```
|
19
|
+
|
20
|
+
Alternatively, you can use the `create` method to create a new record in a single step:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
task = Task.create(title: 'Task 1', description: 'Description of Task 1')
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
The `create` method instantiates a new instance of the model, sets the attributes, and saves it to the database.
|
28
|
+
|
29
|
+
## 5.2. Saving Records
|
30
|
+
|
31
|
+
The `save` method is used to persist a record to the database, whether it's a new record or an existing one with modifications.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# Update an existing task
|
35
|
+
task = Task.create(title: 'Task 2', description: 'Description of Task 2')
|
36
|
+
task.description = 'Updated description of Task 2'
|
37
|
+
task.save
|
38
|
+
```
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
If the record is new (i.e., it doesn't have an ID), `save` will create a new document in Couchbase Server. If the record already exists, `save` will update the existing document with the modified attributes.
|
43
|
+
|
44
|
+
## 5.3. Updating Records
|
45
|
+
|
46
|
+
To update an existing record, you can modify its attributes and then call the `save` method to persist the changes.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# Update specific fields of a task
|
50
|
+
task = Task.create(title: 'Task 3', description: 'Description of Task 3')
|
51
|
+
task.update(description: 'Updated description of Task 3', completed: true)
|
52
|
+
```
|
53
|
+
|
54
|
+
CouchbaseOrm automatically tracks the changes made to the attributes and updates only the modified fields in the database.
|
55
|
+
|
56
|
+
## 5.4. Destroying Records
|
57
|
+
|
58
|
+
To delete a record from the database, you can call the `destroy` method on an instance of your model.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
user = User.find('user_id_123')
|
62
|
+
user.destroy
|
63
|
+
```
|
64
|
+
|
65
|
+
The `destroy` method removes the corresponding document from Couchbase Server and freezes the model instance to prevent further modifications.
|
66
|
+
|
67
|
+
|
68
|
+
## 5.5. Callbacks
|
69
|
+
|
70
|
+
As mentioned in the previous section on defining models, CouchbaseOrm supports lifecycle callbacks that allow you to execute code at certain points in a record's persistence lifecycle.
|
71
|
+
|
72
|
+
Callbacks can be used to perform actions before or after specific events, such as saving or updating a record. Some commonly used callbacks include `before_save`, `after_save`, `before_create`, `after_create`, `before_update`, `after_update`, `before_destroy`, and `after_destroy`.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
class User < CouchbaseOrm::Base
|
76
|
+
before_save :encrypt_password
|
77
|
+
after_create :send_welcome_email
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def encrypt_password
|
82
|
+
self.password = encrypt(password) if password_changed?
|
83
|
+
end
|
84
|
+
|
85
|
+
def send_welcome_email
|
86
|
+
UserMailer.welcome_email(self).deliver_now
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
In this example, the `encrypt_password` callback is executed before saving a user record, encrypting the password if it has been changed. The `send_welcome_email` callback is executed after creating a new user record, sending a welcome email to the user.
|
92
|
+
|
93
|
+
Callbacks provide a way to encapsulate and reuse common logic related to the persistence lifecycle of your records.
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# Associations and Validations
|
2
|
+
|
3
|
+
## 6. Associations
|
4
|
+
|
5
|
+
CouchbaseOrm provides a way to define and work with associations between models. Associations allow you to establish relationships between different entities in your application, making it easier to manage and query related data.
|
6
|
+
|
7
|
+
CouchbaseOrm supports several types of associations, including:
|
8
|
+
|
9
|
+
- One-to-One (belongs_to)
|
10
|
+
- One-to-Many (has_many)
|
11
|
+
- Many-to-Many (has_and_belongs_to_many)
|
12
|
+
|
13
|
+
## 6.1. Belongs To
|
14
|
+
|
15
|
+
The `belongs_to` association is used to define a one-to-one relationship between two models, where the model containing the association "belongs to" the other model.
|
16
|
+
|
17
|
+
In the belongs to, `class_name` is the name of the class that the association points to. `foreign_key` is the name of the field in the current model that references the associated model.
|
18
|
+
|
19
|
+
They are optional and will be inferred from the association name if not provided.
|
20
|
+
|
21
|
+
Class name follows Pascal case and foreign key follows snake case.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class Teacher < CouchbaseOrm::Base
|
25
|
+
...
|
26
|
+
end
|
27
|
+
class Student < CouchbaseOrm::Base
|
28
|
+
attribute :name, :string
|
29
|
+
attribute :grade, :integer
|
30
|
+
attribute :teacher_id, :string
|
31
|
+
|
32
|
+
belongs_to :teacher, class_name: 'Teacher', foreign_key: :teacher_id
|
33
|
+
|
34
|
+
validates_presence_of :name, :grade, :teacher_id
|
35
|
+
end
|
36
|
+
|
37
|
+
```
|
38
|
+
|
39
|
+
In this example, a `Student` belongs to a `Teacher`. CouchbaseOrm assumes that the `students` documents contain a `teacher_id` field that references the associated teacher document.
|
40
|
+
|
41
|
+
|
42
|
+
## 6.2. Has Many
|
43
|
+
|
44
|
+
The `has_many` association is used to define a one-to-many relationship between two models, where one model can have multiple associated records of another model.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class Teacher < CouchbaseOrm::Base
|
48
|
+
attribute :name, :string
|
49
|
+
attribute :subject, :string
|
50
|
+
|
51
|
+
has_many :students, class_name: 'Student', foreign_key: :teacher_id, type: :n1ql, dependent: :destroy
|
52
|
+
|
53
|
+
validates_presence_of :name, :subject
|
54
|
+
end
|
55
|
+
|
56
|
+
class Student < CouchbaseOrm::Base
|
57
|
+
...
|
58
|
+
end
|
59
|
+
|
60
|
+
```
|
61
|
+
|
62
|
+
In this example, a `Teacher` has many `Student`s, and a `Student` belongs to a `Teacher`. CouchbaseOrm assumes that the `students` documents contain a `teacher_id` field that references the associated teacher document.
|
63
|
+
|
64
|
+
The class name and foreign key are optional and will be inferred from the association name if not provided.
|
65
|
+
|
66
|
+
In the following example, we demonstrate how to work with associations in CouchbaseOrm:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
# Creating a new teacher
|
70
|
+
teacher1 = Teacher.create(name: 'Mr. Smith', subject: 'Mathematics')
|
71
|
+
|
72
|
+
# Creating new students
|
73
|
+
student1 = Student.create(name: 'John Doe', grade: 9, teacher_id: teacher1.id)
|
74
|
+
student2 = Student.create(name: 'Jane Roe', grade: 10, teacher_id: teacher1.id)
|
75
|
+
|
76
|
+
# Associating students with teacher
|
77
|
+
puts "Teacher's students: #{teacher1.students.inspect}"
|
78
|
+
|
79
|
+
# Find a teacher by a student's teacher_id
|
80
|
+
found_teacher = Teacher.find(student1.teacher_id)
|
81
|
+
puts found_teacher.inspect
|
82
|
+
|
83
|
+
# List students of a teacher
|
84
|
+
teacher1.reload
|
85
|
+
teacher_students = teacher1.students
|
86
|
+
teacher_students.each { |student| puts student.inspect }
|
87
|
+
```
|
88
|
+
|
89
|
+
## 6.3. Has And Belongs To Many
|
90
|
+
|
91
|
+
The `has_and_belongs_to_many` association is used to define a many-to-many relationship between two models, where each model can have multiple associated records of the other model.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class Publisher < CouchbaseOrm::Base
|
95
|
+
attribute :name, :string
|
96
|
+
has_and_belongs_to_many :magazines, join_class: 'PublishersMagazines'
|
97
|
+
|
98
|
+
validates :name, presence: true
|
99
|
+
end
|
100
|
+
|
101
|
+
class Magazine < CouchbaseOrm::Base
|
102
|
+
attribute :title, :string
|
103
|
+
attribute :genre, :string
|
104
|
+
has_and_belongs_to_many :publishers, join_class: 'PublishersMagazines'
|
105
|
+
|
106
|
+
validates :title, presence: true
|
107
|
+
validates :genre, presence: true
|
108
|
+
end
|
109
|
+
|
110
|
+
class PublishersMagazines < CouchbaseOrm::Base
|
111
|
+
attribute :publisher_id, :string
|
112
|
+
attribute :magazine_id, :string
|
113
|
+
|
114
|
+
validates :publisher_id, presence: true
|
115
|
+
validates :magazine_id, presence: true
|
116
|
+
end
|
117
|
+
|
118
|
+
```
|
119
|
+
|
120
|
+
In this example, a `Publisher` has and belongs to many `Magazine`s, and a `Magazine` has and belongs to many `Publisher`s. The `PublishersMagazines` class serves as the join class that connects the `Publisher` and `Magazine` models.
|
121
|
+
|
122
|
+
You can customize the association name, class name, and foreign key if needed:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class Publisher < CouchbaseOrm::Base
|
126
|
+
has_and_belongs_to_many :books, class_name: 'Book', join_table: 'publishers_books', foreign_key: 'publisher_id', association_foreign_key: 'book_id'
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
In the following example, we demonstrate how to work with many-to-many associations in CouchbaseOrm:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# Create magazines
|
134
|
+
magazine1 = Magazine.create(title: 'Vogue', genre: 'Fashion')
|
135
|
+
magazine2 = Magazine.create(title: 'National Geographic', genre: 'Science')
|
136
|
+
|
137
|
+
# Create publishers
|
138
|
+
publisher1 = Publisher.create(name: 'Penguin Random House')
|
139
|
+
publisher2 = Publisher.create(name: 'Hearst Communications')
|
140
|
+
|
141
|
+
# Associate publishers with magazines
|
142
|
+
publisher1.magazines = [magazine1, magazine2]
|
143
|
+
publisher2.magazines = [magazine1]
|
144
|
+
publisher1.save
|
145
|
+
publisher2.save
|
146
|
+
|
147
|
+
magazine1.publishers = [publisher1, publisher2]
|
148
|
+
magazine2.publishers = [publisher1]
|
149
|
+
magazine1.save
|
150
|
+
magazine2.save
|
151
|
+
|
152
|
+
# Print publishers and their magazines
|
153
|
+
puts Publisher.all.map { |publisher| "#{publisher.name} (ID: #{publisher.id})" }
|
154
|
+
|
155
|
+
# Print magazines and their publishers
|
156
|
+
puts Magazine.all.map { |magazine| "#{magazine.title} (Genre: #{magazine.genre}) by #{magazine.publishers.map(&:name).join(', ')} (ID: #{magazine.id})" }
|
157
|
+
|
158
|
+
# print magazine and tojson
|
159
|
+
puts Magazine.all.map { |magazine| "#{magazine.to_json}" }
|
160
|
+
```
|
161
|
+
|
162
|
+
## 6.4. Polymorphic Associations
|
163
|
+
|
164
|
+
CouchbaseOrm supports polymorphic associations, which allow a model to belong to multiple other models through a single association.
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
class Comment < CouchbaseOrm::Base
|
168
|
+
belongs_to :commentable, polymorphic: true
|
169
|
+
end
|
170
|
+
|
171
|
+
class Post < CouchbaseOrm::Base
|
172
|
+
has_many :comments, as: :commentable
|
173
|
+
end
|
174
|
+
|
175
|
+
class Photo < CouchbaseOrm::Base
|
176
|
+
has_many :comments, as: :commentable
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
In this example, a `Comment` can belong to either a `Post` or a `Photo` through the `commentable` association. The `commentable_type` field in the `comments` document stores the type of the associated record (`Post` or `Photo`), and the `commentable_id` field stores the ID of the associated record.
|
181
|
+
|
182
|
+
## 6.5. Dependent Associations
|
183
|
+
|
184
|
+
CouchbaseOrm allows you to specify what should happen to associated records when the parent record is destroyed. You can use the `dependent` option to control this behavior.
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
class User < CouchbaseOrm::Base
|
188
|
+
has_many :posts, dependent: :destroy
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
In this example, when a `User` is destroyed, all associated `Post`s will also be destroyed. Other options for `dependent` include `:nullify` (sets the foreign key to null), `:restrict_with_exception` (raises an exception if there are associated records), and `:delete_all` (deletes associated records without running callbacks).
|
193
|
+
|
194
|
+
## 6.6. Autosave
|
195
|
+
|
196
|
+
CouchbaseOrm provides an `autosave` option that automatically saves associated records when saving the parent record.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
class User < CouchbaseOrm::Base
|
200
|
+
has_many :posts, autosave: true
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
With `autosave` set to `true`, saving a `User` will also save any new or modified associated `Post`s.
|
205
|
+
|
206
|
+
## 6.7. Querying Associations
|
207
|
+
|
208
|
+
CouchbaseOrm allows you to easily query and retrieve associated records using the defined associations.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
user = User.find('user_id_123')
|
212
|
+
posts = user.posts
|
213
|
+
```
|
214
|
+
|
215
|
+
In this example, `user.posts` retrieves all the associated `Post`s for the given `User`.
|
216
|
+
|
217
|
+
You can also chain query methods on associations:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
recent_posts = user.posts.where('created_at >= ?', 1.week.ago).order(created_at: :desc)
|
221
|
+
```
|
222
|
+
|
223
|
+
This query retrieves the associated `Post`s for the user that were created within the last week, ordered by the most recent first.
|
224
|
+
|
225
|
+
|
226
|
+
<!-- ## 6.9. Strict Loading
|
227
|
+
|
228
|
+
CouchbaseOrm provides a `strict_loading` option that allows you to control how associations are loaded. When `strict_loading` is enabled, associations are loaded eagerly when the parent record is loaded, reducing the number of queries needed to fetch associated records.
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
class User < CouchbaseOrm::Base
|
232
|
+
has_many :posts, strict_loading: true
|
233
|
+
end
|
234
|
+
``` -->
|
235
|
+
|
236
|
+
Now, let's move on to the next section, where we'll explore how to use N1QL queries in CouchbaseOrm for more advanced querying capabilities.
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# SQL++ (formerly N1QL) Queries
|
2
|
+
|
3
|
+
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.
|
4
|
+
|
5
|
+
## 7.1 Defining SQL++ Queries
|
6
|
+
|
7
|
+
To define an SQL++ query in your model, you can use the `n1ql` macro provided by CouchbaseOrm. Here are a few examples:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class N1QLTest < CouchbaseOrm::Base
|
11
|
+
attribute :name, type: String
|
12
|
+
attribute :lastname, type: String
|
13
|
+
enum rating: %i[awesome good okay bad], default: :okay
|
14
|
+
attribute :country, type: String
|
15
|
+
|
16
|
+
# Example 1: Custom query with specific rating values
|
17
|
+
n1ql :by_custom_rating, emit_key: [:rating], query_fn: proc { |bucket, _values, options|
|
18
|
+
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` WHERE type = 'n1_ql_test' AND rating IN [1, 2] ORDER BY name ASC", options)
|
19
|
+
}
|
20
|
+
|
21
|
+
# Example 2: Simple query by name
|
22
|
+
n1ql :by_name, emit_key: [:name]
|
23
|
+
|
24
|
+
# Example 3: Simple query by lastname
|
25
|
+
n1ql :by_lastname, emit_key: [:lastname]
|
26
|
+
|
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
|
36
|
+
n1ql :by_rating_emit, emit_key: :rating
|
37
|
+
|
38
|
+
# Example 6: Custom query by rating with parameter binding
|
39
|
+
n1ql :by_custom_rating_values, emit_key: [:rating], query_fn: proc { |bucket, values, options|
|
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
|
44
|
+
n1ql :by_rating_reverse, emit_key: :rating, custom_order: 'name DESC'
|
45
|
+
|
46
|
+
# Example 8: Simple query by rating without including documents
|
47
|
+
n1ql :by_rating_without_docs, emit_key: :rating, include_docs: false
|
48
|
+
|
49
|
+
# Index definition for the rating attribute
|
50
|
+
index_n1ql :rating
|
51
|
+
end
|
52
|
+
|
53
|
+
```
|
54
|
+
|
55
|
+
In these examples:
|
56
|
+
|
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.
|
58
|
+
|
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.
|
60
|
+
|
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.
|
62
|
+
|
63
|
+
- The `by_rating_reverse` query uses the custom_order option to specify a custom ordering for the query results.
|
64
|
+
|
65
|
+
- The `by_rating_without_docs` query sets include_docs to false to retrieve only the document IDs without fetching the full documents.
|
66
|
+
|
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.
|
68
|
+
|
69
|
+
## 7.2 Query Parameters
|
70
|
+
|
71
|
+
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.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
n1ql :by_custom_rating_values, emit_key: [:rating], query_fn: proc { |bucket, values, options|
|
75
|
+
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)
|
76
|
+
}
|
77
|
+
```
|
78
|
+
|
79
|
+
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.
|
80
|
+
|
81
|
+
## 7.3 Executing SQL++ Queries
|
82
|
+
|
83
|
+
To execute an SQL++ query, you simply call the defined query method on your model class.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
docs = N1QLTest.by_custom_rating().collect { |ob| ob.name }
|
87
|
+
```
|
88
|
+
|
89
|
+
CouchbaseOrm automatically executes the SQL++ query and returns the result set, which you can then process as needed.
|
90
|
+
|
91
|
+
## 7.4 Query Options
|
92
|
+
|
93
|
+
CouchbaseOrm provides various options to customize the execution of SQL++ queries. These options can be passed as a hash parameter to the query method.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
docs = N1QLTest.by_rating_reverse(key: 1)
|
97
|
+
```
|
98
|
+
|
99
|
+
<!-- Some commonly used query options include:
|
100
|
+
|
101
|
+
- `key`: Specifies the key value(s) to filter the results.
|
102
|
+
- `limit`: Specifies the maximum number of results to return.
|
103
|
+
- `offset`: Specifies the number of results to skip before starting to return results.
|
104
|
+
- `include_docs`: Specifies whether to include the full document content in the results.
|
105
|
+
- `scan_consistency`: Specifies the consistency level for the query (`request_plus` by default). -->
|
106
|
+
|
107
|
+
## 7.5 Placeholder Values
|
108
|
+
|
109
|
+
You can use placeholder values in your SQL++ queries and pass the actual values when executing the query.
|
110
|
+
|
111
|
+
```ruby
|
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
|
+
}
|
118
|
+
|
119
|
+
docs = N1QLTest.by_country(key: 'USA').collect { |ob| ob.name }
|
120
|
+
```
|
121
|
+
|
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.
|
123
|
+
|
124
|
+
## 7.6 Query Result Processing
|
125
|
+
|
126
|
+
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.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
n1ql :by_custom_rating, emit_key: [:rating], query_fn: proc { |bucket, _values, options|
|
130
|
+
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` WHERE type = 'n1_ql_test' AND rating IN [1, 2] ORDER BY name ASC", options)
|
131
|
+
}
|
132
|
+
|
133
|
+
n1ql :by_custom_rating_values, emit_key: [:rating], query_fn: proc { |bucket, values, options|
|
134
|
+
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)
|
135
|
+
}
|
136
|
+
```
|
137
|
+
|
138
|
+
In the above example, the `by_custom_rating` and `by_custom_rating_values` queries are executed, and the results are processed manually to extract the `name` attribute from each document.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
# Query by custom rating
|
142
|
+
docs = N1QLTest.by_custom_rating.collect { |ob| ob.name }
|
143
|
+
|
144
|
+
# Query by custom rating values
|
145
|
+
docs = N1QLTest.by_custom_rating_values(key: [[1, 2]]).collect { |ob| ob.name }
|
146
|
+
```
|
147
|
+
|
148
|
+
In the above examples, the `collect` method is used to extract the `name` attribute from each document in the result set.
|
149
|
+
|
150
|
+
## 7.7 Indexing for SQL++
|
151
|
+
|
152
|
+
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.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
|
156
|
+
class N1QLTest < CouchbaseOrm::Base
|
157
|
+
...
|
158
|
+
|
159
|
+
# Index definition for the rating attribute
|
160
|
+
index_n1ql :rating
|
161
|
+
end
|
162
|
+
|
163
|
+
# Query using index_n1ql
|
164
|
+
docs = N1QLTest.find_by_rating(2).collect { |ob| ob.name }
|
165
|
+
|
166
|
+
# Query using index_n1ql
|
167
|
+
docs = N1QLTest.by_rating.to_a.collect { |ob| ob.name }
|
168
|
+
```
|
169
|
+
|
170
|
+
In this example, indexes are defined on the `rating` attribute using the `index_n1ql` macro.
|
171
|
+
|
172
|
+
Indexing helps improve the efficiency of SQL++ queries by allowing Couchbase Server to quickly locate the relevant documents based on the indexed fields.
|
173
|
+
|
174
|
+
---
|
175
|
+
|
176
|
+
SQL++ queries in CouchbaseOrm provide a powerful and flexible way to retrieve data from Couchbase Server. By leveraging the expressive power of SQL++, you can perform complex queries, aggregations, and data manipulations directly from your Ruby code.
|
177
|
+
|
178
|
+
Remember to ensure that you have the necessary indexes created on the fields used in your queries for optimal performance. Additionally, consider the query consistency level and other options based on your application's requirements.
|
179
|
+
|
180
|
+
With CouchbaseOrm's SQL++ support, you can easily integrate advanced querying capabilities into your Ruby application, enabling you to retrieve and manipulate data stored in Couchbase Server efficiently.
|