appdb
appdb 是一个给 Tauri 嵌入式 SurrealDB 使用的轻量辅助库。
目标只有几个:
- 用
#[derive(Store)]给模型挂上直接可用的模型级 CRUD 能力 - 用 prelude 导出一个简单的使用面
- 保持推荐 API 以模型自身的
save、get、list这种直观名字为主 - 给需要加密的字段提供
Sensitive派生支持
工作区结构
core: 主库,发布后通过cargo add appdb使用macros: 过程宏,供core复用并一并导出
常用入口
appdb::prelude::*: 常用类型和能力的集中导出appdb::connection: 数据库初始化和运行时#[derive(Store)]: 业务模型的主入口,推荐直接通过模型类型调用 CRUDappdb::graph::GraphRepo: relation table 辅助appdb::query: 原始 SQL 与带 bind 的查询辅助
最小示例
use *;
use Store;
use ;
use SurrealValue;
async
Store + Sensitive 联动
use *;
use ;
use ;
use SurrealValue;
对这种模型,业务代码仍然直接使用 Profile 调 save / get / list 等 Store API;#[secure] 字段会在仓储边界自动加密落库、读回时自动解密。第一版里 create_return_id 不支持敏感模型,且 #[secure] 字段不能参与 #[unique] 或自动 lookup。
推荐 public API vs internal helpers
推荐给业务调用方的主路径:
- 在模型上直接调用
save/save_many/create/get/list - 需要实例方法时,通过
appdb::Crudtrait 提供的包装调用 - 图关系继续使用
GraphRepo/ relation helpers
不推荐把下面这些当成日常业务入口:
appdb::repository::Repo::<T>:这是仓储内部构建层,主要给库内部、测试和少量高级集成 seam 使用- 直接围绕 internal helper 组合 public CRUD 流程
换句话说,Repo 仍然公开以保留扩展能力,但文档约定的主 API 已经收敛到模型级 Store/Crud surface,而不是 Repo::<T> 组合层。
#[store(ref)] 嵌套引用
Store 现在支持显式嵌套引用字段:
use *;
use Store;
use ;
use SurrealValue;
当前语义:
- 只有显式标注
#[store(ref)]的字段才走嵌套引用路径 - 首版支持
Child、Option<Child>、Vec<Child> - 保存父对象时,会先按子对象的
id或现有 lookup 规则解析子记录;找不到时自动创建子记录 - 父表里只保存子记录的
RecordId(或其数组),读取父对象时会自动 hydrate 回完整子对象 save/get/get_record/list/list_limit会保持一致的 hydrate 结果- 失败的
save/save_many不应留下父子半成功残留;成功返回的对象应与后续读取结果一致 - 原始 query 读路径支持字符串形态的 record link(例如 ``child:`c1```)回到正常 hydrate 流程
#[table_as(...)]模型参与嵌套引用时,仍通过目标表落库并保持同样的 roundtrip 语义- 这一层不会扩展到
merge/patch
图关系示例
use *;
let rel = ;
relate_at.await?;
let targets = out_ids.await?;
原始查询
优先用带 bind 的形式:
use *;
let stmt = new.bind;
let value: = query_bound_return.await?;
说明
DbRuntime::open*/init_db*走的是 schema-managed 启动路径:运行时会先应用通过 schema inventory 注册的 DDL(例如#[unique]生成的索引)。- 直接用默认的嵌入式运行时做首次
save/upsert_at时,库仍然保证 schemaless 持久化可用;这条承诺是独立的,不能把 managed 启动时顺带应用的 schema side effects 当作它的证明。 - 因此文档里的推荐调用顺序是:把
init_db*/DbRuntime::open*视为 managed schema 启动入口;把模型级save/get/list视为稳定的 public CRUD surface。两者分别表达启动契约与持久化契约,不要混成“必须先走 internal repo helper 才能正确保存”。 - 更细的行为说明已经写进源码里的 rustdoc,直接看对应函数和结构体即可。
- 这个库偏向单机嵌入式使用场景,不追求大而全的抽象层。