Ormer
Home
快速开始
GitHub
  • 简体中文
  • English
Home
快速开始
GitHub
  • 简体中文
  • English
  • Ormer 简介
  • 快速开始
  • 模型定义
  • 数据库连接
  • 数据操作
  • 查询构建器
  • 高级查询
  • 事务管理
  • 连接池
  • 钩子系统 (Hooks)
  • 数据库迁移

查询构建器

基本查询

let users: Vec<User> = db.select::<User>().collect().await?;

let user: Vec<User> = db
    .select::<User>()
    .filter(|u| u.id.eq(1))
    .range(..1)
    .collect()
    .await?;

过滤条件

比较运算

.filter(|u| u.name.eq("Alice".to_string()))
.filter(|u| u.age.ne(18))          // != 不等
.filter(|u| u.age.ge(18))          // >= 大于等于
.filter(|u| u.age.gt(18))          // > 大于
.filter(|u| u.age.le(65))          // <= 小于等于
.filter(|u| u.age.lt(65))          // < 小于

LIKE 模糊匹配

.filter(|u| u.name.like("Al%"))           // 自定义模式
.filter(|u| u.name.contains("alice"))      // 包含
.filter(|u| u.name.starts_with("Al"))      // 以...开头
.filter(|u| u.name.ends_with("ce"))        // 以...结尾

可与其他条件组合:

.filter(|u| u.name.contains("li").and(u.age.gt(29)))

数组包含(SQL 级下推)

对于 Vec<String> 字段,contains / contains_all / overlaps 会下推为 SQL 谓词 参与分页与计数,不会退化为内存过滤:

let users: Vec<User> = db
    .select::<User>()
    .filter(|u| u.tags.contains("admin"))          // 包含该元素
    .filter(|u| u.tags.contains_all(vec!["admin".to_string(), "ops".to_string()])) // 全部包含
    .filter(|u| u.tags.overlaps(vec!["ops".to_string(), "dev".to_string()]))       // 任一交集
    .collect()
    .await?;

各后端生成等价语义的方言 SQL:PostgreSQL @> / &&,MySQL JSON_CONTAINS / JSON_OVERLAPS,SQLite json_each,MSSQL OPENJSON,ClickHouse has/hasAll, DuckDB list_contains/list_has_any。

QuestDB 与 InfluxDB 不支持数组谓词,构造查询时返回 UnsupportedFeature 错误。

NULL 判断

.filter(|u| u.email.is_null())       // IS NULL
.filter(|u| u.email.is_not_null())   // IS NOT NULL

BETWEEN 范围

.filter(|u| u.age.between(18, 30))  // age BETWEEN 18 AND 30

IN 与 NOT IN

.filter(|u| u.age.is_in(&vec![18, 20, 22]))
.filter(|u| u.name.is_in(&vec!["Alice".to_string(), "Bob".to_string()]))

.filter(|u| u.age.is_not_in(&vec![18, 20]))   // NOT IN

is_in 和 is_not_in 也支持子查询:

.filter(|u| u.id.is_in(db.select::<Role>().map_to(|r| r.user_id)))
.filter(|u| u.id.is_not_in(db.select::<Role>().map_to(|r| r.user_id)))

组合条件

.filter(|u| u.age.ge(18))
.filter(|u| u.age.le(65))

.filter(|u| u.age.ge(18).and(u.name.eq("Alice".to_string())))
.filter(|u| u.age.lt(18).or(u.age.gt(65)))

模型级过滤器

#[filter] 会为模型生成同名链式方法(扩展 trait 由派生生成本地作用域,同模块内无需导入):

let orders: Vec<Order> = db
    .select::<Order>()
    .filter_tenant(tenant_id)
    .filter_valid()
    .collect()
    .await?;

运行时动态字段

field 可把运行时字段名或列名转换为安全查询条件;字段不存在时,执行查询时返回错误:

let users: Vec<User> = db
    .select::<User>()
    .filter(|u| u.field("email").ne("a@example.com"))
    .order_by_dynamic(|u| u.field("age").desc())
    .collect()
    .await?;

排序

.order_by(|u| u.name.asc())

.order_by_desc(|u| u.age)

.order_by(|u| u.age.desc())
.order_by(|u| u.name.asc())

分页

.range(0..10)
.range(10..20)
.range(..5)
.range(10..)

深分页可用游标分页:cursor_by 声明游标列(需与排序一致),fetch_page 返回带 next_cursor() 的 CursorPage,游标传给 after() 取下一页:

let page = db
    .select::<User>()
    .order_by_desc(|u| u.id)
    .cursor_by(|u| u.id)
    .limit(10)
    .fetch_page()
    .await?;

let next = db
    .select::<User>()
    .order_by_desc(|u| u.id)
    .cursor_by(|u| u.id)
    .after(page.next_cursor().expect("还有下一页"))
    .limit(10)
    .fetch_page()
    .await?;

去重查询

// SELECT DISTINCT *
let users = db.select::<User>().distinct().collect().await?;

// SELECT DISTINCT name
let names: Vec<String> = db.select::<User>().distinct().map_to(|u| u.name).collect().await?;

可与 filter、order_by、range 等组合使用。

忽略字段

使用 ignore 查询完整模型时,可以用常量替代指定字段的真实值。字段仍然存在于返回模型中,但不会从数据库读取:

let users: Vec<User> = db
    .select::<User>()
    .ignore(|u| (u.id, u.email))
    .collect()
    .await?;

例如,整数主键会读为 0,可空字段会读为 None。该方法适合不需要读取敏感或大字段的查询。

单条查询

// 等价于 range(..1),只取第一条
let user: Option<User> = db.select::<User>().filter(|u| u.age.ge(18)).first().await?;

递归 CTE

自关联树可用 descendants / ancestors 生成递归 CTE。第一个字段是节点 id,第二个字段是父节点 id:

let nodes: Vec<Category> = db
    .select::<Category>()
    .descendants(|c| (c.id, c.parent_id), root_id)
    .order_by(|c| c.id.asc())
    .collect()
    .await?;

SQLite 当前 turso 后端可能不支持执行递归 CTE,可使用 to_sql() 生成 SQL 后交给支持的后端执行。

流式查询 (stream)

let mut stream = db
    .select::<User>()
    .filter(|u| u.age.ge(18))
    .stream()
    .into_iter()
    .await?;

while let Some(user_result) = stream.next().await {
    let user = user_result?;
    println!("{:?}", user);
}

字段投影 (map_to)

let names: Vec<String> = db
    .select::<User>()
    .map_to(|u| u.name)
    .collect::<Vec<String>>()
    .await?;

let name_age: Vec<(String, i32)> = db
    .select::<User>()
    .map_to(|u| (u.name, u.age))
    .collect()
    .await?;

let labeled: Vec<(i32, String, String)> = db
    .select::<User>()
    .map_to(|u| {
        (
            u.id.alias("user_id"),
            u.email,
            ormer::expr!(match u.status {
                "paid" => "done",
                "new" => "open",
                _ => "other",
            })
            .alias("status_label"),
        )
    })
    .collect()
    .await?;

let user_ids: Vec<UserId> = db
    .select::<User>()
    .map_to(|u| u.id)
    .collect_with(|id| UserId { id })
    .await?;

单个投影项可以直接调用 .alias("...") 改名。

如果目标仍是另一个 Model,可以使用 map_to_model,框架会按目标模型的列名生成别名:

let archived: Vec<ArchiveUser> = db
    .select::<User>()
    .map_to_model::<_, ArchiveUser>(|u| u.id)
    .collect()
    .await?;

查询组合

let users: Vec<User> = db
    .select::<User>()
    .filter(|u| u.age.ge(18))
    .order_by(|u| u.name.asc())
    .range(0..10)
    .collect()
    .await?;

let base_query = db.select::<User>().filter(|u| u.age.ge(18));

let adults_cn = base_query.clone()
    .filter(|u| u.country.eq("CN".to_string()))
    .collect::<Vec<_>>()
    .await?;

let adults_us = base_query
    .filter(|u| u.country.eq("US".to_string()))
    .collect::<Vec<_>>()
    .await?;

完整示例

use ormer::{Database, DbType, Model};

#[derive(Debug, Model)]
#[table = "users"]
struct User {
    #[primary(auto)]
    id: i32,
    name: String,
    age: i32,
    email: Option<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Database::connect(DbType::Sqlite, "file:test.db").await?;
    db.create_table::<User>().execute().await?;
    
    db.insert(&vec![
        User { id: 1, name: "Alice".to_string(), age: 25, email: None },
        User { id: 2, name: "Bob".to_string(), age: 30, email: None },
        User { id: 3, name: "Charlie".to_string(), age: 35, email: None },
    ]).await?;
    
    let all: Vec<User> = db.select::<User>().collect().await?;
    
    let adults: Vec<User> = db
        .select::<User>()
        .filter(|u| u.age.ge(18))
        .collect()
        .await?;
    
    let sorted: Vec<User> = db
        .select::<User>()
        .order_by_desc(|u| u.age)
        .collect()
        .await?;
    
    let page: Vec<User> = db
        .select::<User>()
        .order_by(|u| u.id.asc())
        .range(0..2)
        .collect()
        .await?;
    
    let names: Vec<String> = db
        .select::<User>()
        .map_to(|u| u.name)
        .collect::<Vec<String>>()
        .await?;
    
    db.drop_table::<User>().execute().await?;
    Ok(())
}
最近更新: 2026/9/11 07:25
Contributors: fawdlstty
Prev
数据操作
Next
高级查询