Queries
Cot provides a Query interface that allows you to write queries
to the database. In this guide, we’ll cover the basics of writing queries after you’ve setup your
database and models. For a complete reference, see the Query docs.
For the rest of this guide, we’ll use the following models which show a simple e-commerce application.
use ;
use ;
use ;
pub
pub
pub
Creating an object
To create a new model instance, cot provides the insert method.
In the example below, we create a new Customer instance and save it to the database.
use ;
use ;
async
Keep in mind that the insert method will return a UniqueViolation error if a record with the same primary key already exists.
The example below shows an attempt to insert a new Customer instance with the same primary key as an existing one.
use ;
use ;
async
Creating multiple objects
If you need to create multiple objects, cot provides the bulk_insert method for this purpose.
It is recommended to prefer bulk_insert for multiple insertions as it performs the operations in a single database query which is much more efficient than performing multiple individual insert or save calls.
The example below shows how to create multiple Customer instances.
use ;
use ;
async
Keep in mind that bulk_insert takes a mutable slice of models, because it needs to update the primary keys of the inserted models with the values generated by the database.
Similarly, there is also bulk_insert_or_update method, which works like bulk_insert, but updates the existing rows if they conflict with the new ones.
Updating an object
Cot provides the update method to update an existing model instance.
The example below shows how to update the full_name field of a Customer instance.
= ;
?;
Creating or Updating an object
Cot provides the save method to create a new model instance if it doesn’t exist, or update it if it does.
In the example below, we create a Customer instance if it doesnt exist and then update it’s verified status.
use ;
use ;
async
Saving ForeignKey fields
Saving a foreign key field is similar to saving a regular field, and Cot provides two variants for foreign key fields: ForeignKey::Model and ForeignKey::PrimaryKey.
The key thing to keep in mind when saving any foreign key field is that the referenced model instance must already exist in the database before the relation can be saved.
ForeignKey::Model
This is the most common variant, which lets you associate a model instance directly as a foreign key field. The example below shows how to first persist a Customer and a Product instance, then reference them as foreign key fields on an Order.
use ;
use ;
async
ForeignKey::PrimaryKey
This variant lets you set a foreign key field using only the primary key of the referenced model, without needing to have the model instance in hand. This is useful when you already know the ID of the related record.
The example below saves an Order referencing existing Customer and Product records by their primary keys directly.
use ;
async
Keep in mind that if the provided primary key does not correspond to an existing record in the database, the save will fail.
Retrieving objects
To retrieve objects from the database, cot provides the query! macro which offers a convenient and declarative way to write queries.
The macro takes two arguments: the model to query as the first argument, and a filter condition as the second, written using a natural expression syntax where fields are prefixed with $. The example below shows how to retrieve a Customer instance with the primary key of 5.
use ;
async
The query! macro returns a Query object, on which you can call terminal methods (such as get which returns the first matching result, and all which returns all matching results) to retrieve the final results.
Using the Query struct
The query! macro is syntactic sugar for manually constructing a Query with Expr expressions. The Query object can be accessed directly by calling the objects method on the model, and filtered using the filter method.
You may prefer this approach when you need more control over how expressions are constructed. The example below is equivalent to the one above:
use ;
use ;
async
The filter method takes a filter expression. In the example above, the expression Expr::eq(Expr::field("id"), Expr::value("5")) is evaluated as id = 5.
Retrieving all objects
One way to retrieve all objects of a model is to call the all method after filtering the query results.
use ;
use ;
async
The example above retrieves all customers with a primary key greater than 5. This returns a list of Customer instances.
Chaining filters
The filter method returns a new Query instance which makes it convenient to chain multiple filters.
use ;
use ;
async
The example above shows how to retrieve all customers with a primary key greater than 5 and whose full name is Jon Doe.
Note: Although this example works, the idiomatic way to do this is to use the
Expr::andexpression instead.
Similarly, the query macro returns a new Query instance which can be used to chain multiple filters.
Searching within a field (pattern matching)
Sometimes an exact match isn’t what you want. You might want to find all customers whose name contains a certain word, or all products whose name starts with a certain prefix. Cot supports this kind of substring, prefix, and suffix search directly in the query! macro (and, if you’re building expressions by hand, on Expr as well).
use ;
async
Alongside contains, there’s starts_with and ends_with, which anchor the match to the beginning or the end of the field’s value instead of allowing it anywhere:
use ;
async
By default, all three of these are case-sensitive, so contains("Doe") won’t match a customer named “jane doe”. If you’d rather the match ignore case, each method has an i-prefixed counterpart: icontains, istarts_with, and iends_with.
use ;
async
These combine with the rest of the filter naturally too. Use them alongside boolean and comparison operators just like any other condition:
use ;
async
Matching a custom pattern
contains, starts_with, and ends_with cover most everyday searches, but they can’t express every shape of match. What if you need to check both the start and the end of a value at once, or match a value that follows a specific structure? For cases like this, Cot provides raw_like (and its case-insensitive counterpart, iraw_like), which let you write the match pattern yourself, using a small glob-style syntax:
*matches zero or more of any character?matches exactly one character\escapes the character after it, so it’s matched literally instead of as a wildcard
Say you’re searching for shipment tracking codes that start with "PKG" and end with "US", with anything in between:
use ;
async
raw_like is more flexible than contains, starts_with, and ends_with, but it also means you’re responsible for escaping any wildcard characters you want to match literally. See the Expr::raw_like docs for the full pattern syntax and escaping rules.
Using the Expr struct directly
As with the other operators in this guide, all of these have an equivalent on Expr for when you’re building queries by hand rather than through the query! macro:
use ;
use ;
async
For the complete list of pattern-matching methods, their case-insensitive counterparts, and the glob pattern syntax used by raw_like, see the Expr and ExprLike docs.
Removing an object
The delete method can be used to remove an object from the database. The example below shows how to remove a Customer instance with the primary key of 5.
use ;
async
Other Query methods
The methods listed on this page are the most commonly used query methods. For a complete comprehensive list of supported query methods, see the Query docs.
Raw SQL queries
While the Query interface and the query! macro should cover the vast majority of use cases, sometimes you may need to run a query that they don’t support, such as a complex join or a database-specific function. For these cases, Cot provides an escape hatch to run raw SQL directly through the Database struct.
Warning: These methods execute the given SQL string as-is, without any sanitization. Never build the query string by interpolating untrusted input directly into it, as that would expose your application to SQL injection. Use the parameterized variants below whenever the query depends on external data.
raw and raw_with
The raw method executes a raw SQL statement and returns a StatementResult, which contains the number of affected rows (and, when available, the ID of the last inserted row). It’s most useful for statements that don’t return rows, such as CREATE TABLE, INSERT, UPDATE, or DELETE.
use ;
async
If your query takes parameters, use raw_with instead of interpolating the values into the query string yourself. The parameters are passed separately and bound safely by the underlying database driver, so this is the preferred way of running raw statements that depend on runtime values.
use ;
async
raw_as and raw_as_with
To run a raw SELECT query and map the returned rows onto a model, use the raw_as method. It runs the query and applies the model’s Model::from_db mapping to every returned row, so the columns selected by the query must match the model’s fields.
This is handy for queries that are difficult or impossible to express with the Query interface, such as joins across multiple tables or aggregate expressions:
use ;
async
Just like raw_with is the parameterized counterpart of raw, raw_as_with is the parameterized counterpart of raw_as. Prefer it whenever the query depends on runtime values, instead of interpolating them into the query string yourself:
use ;
async