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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy.yml +45 -0
  3. data/.github/workflows/test.yml +18 -13
  4. data/README.md +12 -0
  5. data/couchbase-orm.gemspec +2 -2
  6. data/docusaurus/.gitignore +20 -0
  7. data/docusaurus/README.md +41 -0
  8. data/docusaurus/babel.config.js +3 -0
  9. data/docusaurus/docs/tutorial-ruby-couchbase-orm/01-introduction.md +49 -0
  10. data/docusaurus/docs/tutorial-ruby-couchbase-orm/02-installation.md +108 -0
  11. data/docusaurus/docs/tutorial-ruby-couchbase-orm/03-defining-models.md +239 -0
  12. data/docusaurus/docs/tutorial-ruby-couchbase-orm/04-querying.md +154 -0
  13. data/docusaurus/docs/tutorial-ruby-couchbase-orm/05-persistence.md +93 -0
  14. data/docusaurus/docs/tutorial-ruby-couchbase-orm/06-associations-and-validations.md +236 -0
  15. data/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md +180 -0
  16. data/docusaurus/docs/tutorial-ruby-couchbase-orm/08-views.md +158 -0
  17. data/docusaurus/docs/tutorial-ruby-couchbase-orm/09-nested-documents.md +138 -0
  18. data/docusaurus/docs/tutorial-ruby-couchbase-orm/10-enums.md +91 -0
  19. data/docusaurus/docs/tutorial-ruby-couchbase-orm/11-encryption.md +114 -0
  20. data/docusaurus/docs/tutorial-ruby-couchbase-orm/12-logging.md +48 -0
  21. data/docusaurus/docs/tutorial-ruby-couchbase-orm/13-troubleshooting.md +41 -0
  22. data/docusaurus/docs/tutorial-ruby-couchbase-orm/_category_.json +8 -0
  23. data/docusaurus/docusaurus.config.ts +122 -0
  24. data/docusaurus/package-lock.json +14540 -0
  25. data/docusaurus/package.json +47 -0
  26. data/docusaurus/sidebars.ts +31 -0
  27. data/docusaurus/src/components/HomepageFeatures/index.tsx +69 -0
  28. data/docusaurus/src/components/HomepageFeatures/styles.module.css +11 -0
  29. data/docusaurus/src/css/custom.css +30 -0
  30. data/docusaurus/src/pages/index.module.css +23 -0
  31. data/docusaurus/src/pages/index.tsx +43 -0
  32. data/docusaurus/src/pages/markdown-page.md +7 -0
  33. data/docusaurus/static/.nojekyll +0 -0
  34. data/docusaurus/static/CNAME +1 -0
  35. data/docusaurus/static/img/familiar.svg +1 -0
  36. data/docusaurus/static/img/fast.svg +1 -0
  37. data/docusaurus/static/img/logo.svg +1 -0
  38. data/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
  39. data/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
  40. data/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
  41. data/docusaurus/tsconfig.json +7 -0
  42. data/lib/couchbase-orm/types/date_time.rb +2 -1
  43. data/lib/couchbase-orm/utilities/query_helper.rb +1 -1
  44. data/lib/couchbase-orm/version.rb +1 -1
  45. 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.