RocketBoost Orbit logo Orbit rocketboost.rs

Queries

A query starts with Model::query(), or with Query::table() when you have no model. Builder methods return the query, and a terminal method runs it. A terminal method takes the database, as &db, or a transaction, as &mut *tx:

use orbit_orm::{Database, Direction, Model, Operator, Query};

let db = Database::connect("mysql://user:password@127.0.0.1/app").await?;

let popular = Post::query()
    .where_op("views", Operator::Ge, 100)
    .order_by("views", Direction::Desc)
    .limit(10)
    .get(&db)
    .await?;

Filters

Each filter has an or_ form that joins it with OR. A filter without the prefix joins with AND.

  • where_eq(column, value) and or_where_eq compare for equality.
  • where_op(column, operator, value) and or_where_op take an Operator: Lt, Le, Gt, Ge, Ne or Like.
  • where_in(column, values) and or_where_in match any of a list. An empty list matches no rows.
  • where_null(column), where_not_null(column) and their or_ forms check for NULL.

A None value follows Laravel. where_eq(column, None) is IS NULL, and where_op with Operator::Ne and None is IS NOT NULL. Any other operator with None matches no rows.

Grouped conditions

where_group and or_where_group put their conditions in parentheses. The closure receives an empty query and adds its conditions to it:

let posts = Post::query()
    .where_op("views", Operator::Ge, 100)
    .where_group(|q| q.where_eq("title", "Hello").or_where_eq("title", "Hi"))
    .get(&db)
    .await?;

Only the conditions are kept from the group. order_by, limit and the other settings inside it are ignored. An empty group adds nothing.

Order, columns and paging

  • order_by(column, Direction::Asc) or Direction::Desc sorts the rows. Later calls sort within the earlier ones.
  • select(&[columns]) chooses the columns that get, first and the methods built on them return. Without it, every column is selected.
  • limit(n) returns at most n rows, and offset(n) skips the first n. An offset without a limit is allowed.

Reading rows

  • get(db) returns every matching row, as a Vec.
  • first(db) returns the first row, or None. It has no default order, so call order_by when the row matters.
  • first_or_fail(db) is first, and returns Error::NotFound when no row matches.
  • find(db, id) returns the row whose primary key is id, or None.
  • find_or_fail(db, id) is find, and returns Error::NotFound when no row matches.
  • count(db) returns the number of matching rows, as an i64.
  • exists(db) returns whether any row matches.
let post = Post::query().find_or_fail(&db, 1).await?;
let newest = Post::query().order_by("id", Direction::Desc).first(&db).await?;

Writing rows

The writes take the columns and their values as pairs. Each column must be in the model's FILLABLE list, or the write returns Error::NotFillable.

  • insert(db, values) inserts one row and returns its auto-increment id, as a u64.
  • replace(db, values) runs MySQL's REPLACE INTO. It deletes the row that has the same unique or primary key, then inserts the new one, and returns the rows affected. Because the old row is deleted, its id changes, and columns that are not in values take their defaults. This is not Laravel's upsert.
  • update(db, values) sets the columns on every matching row, and returns the number of rows changed. An empty list of values changes nothing and returns 0. Order and limit are ignored.
  • delete(db) deletes every matching row, and returns the number deleted. Order and limit are ignored.

A query with no conditions applies update and delete to every row of the table.

let id = Query::<Post>::table()
    .insert(&db, &[("title", "Hello".into())])
    .await?;

let affected = Query::<Post>::table()
    .replace(&db, &[("title", "Hello".into())])
    .await?;

Values and names

Every value is sent as a bound parameter, never as SQL text. Table and column names are quoted with backticks. A name that is empty or contains a backtick is an error, Error::InvalidIdentifier.

Row locks

for_update() adds FOR UPDATE to get, first, find, count and exists. Inside a transaction, the lock lasts until the transaction ends. Outside one, the statement runs in autocommit and the lock lasts only for that statement. See Transactions.

let post = Post::query()
    .where_eq("id", 1)
    .for_update()
    .first_or_fail(&mut *tx)
    .await?;