SurrealDB Rust SDK关联字段FETCH子句序列化问题求助
问题描述
我在SurrealDB项目中使用Rust SDK处理关联关系时遇到了问题,定义的实体结构体如下:
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct LiturgicalDateEntity { pub id: Thing, pub time: LiturgicalTime, pub day: u8, pub month: u8, pub year: i32, pub commemorations_ids: Vec<Thing>, pub commemorations: Option<Vec<CommemorationEntity>>, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct CommemorationEntity { pub id: Thing, pub category: CommemorationCategory, pub color: LiturgicalColor, pub gospel_id: String, }
当前存储逻辑不符合直觉:用commemorations字段存关联ID,查询时通过SELECT *, commemorations as commemorations_ids FROM liturgical_dates:⟨11-6-2024⟩ FETCH commemorations把关联实体映射到commemorations_ids,理想状态是commemorations_ids存ID、commemorations存关联实体。
另外不添加FETCH子句查询时,会触发反序列化错误:
"Failed to convert
[{ commemorations: [commemorations:st_barnabas_apostle], commemorations_ids: [commemorations:st_barnabas_apostle], day: 11, id: liturgical_dates:⟨11-6-2024⟩, month: 6, time: 0, year: 2024 }]toT: missing fieldtb"
即使把commemorations设为Option也无效,因为反序列化器期望的是Vec<CommemorationEntity>,但数据库返回的是Vec<Thing>。
尝试过两种枚举方案均失败:
- 用
CommemorationsRef枚举包含Ids和Records变体,报错同上; - 用
CommemorationRef枚举包含Id和Record变体,报错:
"Failed to convert
[{ commemorations: [commemorations:st_barnabas_apostle], day: 11, id: liturgical_dates:⟨11-6-2024⟩, month: 6, time: 0, year: 2024 }]toT: invalid value: map, expected map with a single key"
可行解决方案
方案一:调整查询语句实现字段映射
SurrealDB支持通过查询语句的别名与FETCH组合,直接将关联实体映射到目标字段,同时保留ID字段。
- 修改存储逻辑:将关联ID存入
commemorations_ids字段(而非commemorations); - 使用目标查询语句:
SELECT *, FETCH commemorations_ids AS commemorations FROM liturgical_dates:⟨11-6-2024⟩
这条语句会:
- 返回
liturgical_dates的所有字段(包括commemorations_ids); - 自动拉取
commemorations_ids对应的CommemorationEntity数据,并将其存入commemorations字段。
查询结果可直接匹配你的结构体定义,无需额外处理反序列化问题。
方案二:自定义反序列化逻辑兼容两种类型
如果需要让commemorations字段同时支持Vec<Thing>(未FETCH时)和Vec<CommemorationEntity>(FETCH后),可以通过#[serde(untagged)]属性实现自动类型匹配:
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(untagged)] // 关键:自动匹配无标签枚举的变体 enum CommemorationRef { Id(Thing), Record(CommemorationEntity), } // 修改LiturgicalDateEntity的字段类型 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct LiturgicalDateEntity { pub id: Thing, pub time: LiturgicalTime, pub day: u8, pub month: u8, pub year: i32, pub commemorations_ids: Vec<Thing>, pub commemorations: Option<Vec<CommemorationRef>>, }
#[serde(untagged)]会让Serde自动尝试匹配数据类型:
- 若为Thing格式(如
commemorations:st_barnabas_apostle),反序列化为CommemorationRef::Id; - 若为完整实体对象,反序列化为
CommemorationRef::Record。
之前的枚举方案失败大概率是因为缺少该属性,导致Serde期望带标签的枚举格式(如{"Id": "..."}),与数据库返回的原始值不匹配。
方案三:分离存储与查询用结构体
如果不想修改反序列化逻辑,可以定义两个结构体拆分职责:
// 存储用结构体:仅保留写入数据库所需字段 #[derive(Debug, Serialize, Clone, PartialEq)] pub struct LiturgicalDateCreateDto { pub time: LiturgicalTime, pub day: u8, pub month: u8, pub year: i32, pub commemorations_ids: Vec<Thing>, } // 查询用结构体:用于读取包含关联实体的数据 #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct LiturgicalDateEntity { pub id: Thing, pub time: LiturgicalTime, pub day: u8, pub month: u8, pub year: i32, pub commemorations_ids: Vec<Thing>, pub commemorations: Option<Vec<CommemorationEntity>>, }
写入数据库时使用LiturgicalDateCreateDto,查询时使用LiturgicalDateEntity,配合方案一的查询语句即可完美匹配。
内容的提问来源于stack exchange,提问作者Dinaiscoding

