3. 实验记录数据模型设计
ELN 的数据模型决定了系统能否既忠实还原实验过程,又支撑检索、审计与复用。设计时先要区分两类信息:一类是实验本身的结构(实验、步骤、样品、仪器、试剂、附件、版本),另一类是实验过程中产生的数据(测量值、图谱、观察记录)。前者适合用关系模式保证一致性,后者往往结构多变,适合用 JSON 文档承载。本节围绕这两条线展开。
核心实体与关系模式
把实验记录拆成实体,关键是找到“谁被谁引用”。一个实验(experiment)包含有序的步骤(step);每个步骤消耗或产出样品(sample);步骤使用仪器(instrument)和试剂(reagent);附件(attachment)挂在实验或步骤上;版本(version)记录实验的每次修订。用关系模式表达时,主键与外键构成骨架:
experiment(id PK, title, owner_id, status, created_at, current_version)
step(id PK, experiment_id FK, seq_no, name, started_at, ended_at)
sample(id PK, experiment_id FK, parent_sample_id FK, name, type, amount, unit)
step_sample(step_id FK, sample_id FK, role) -- role: input/output
step_instrument(step_id FK, instrument_id FK, setting_json)
step_reagent(step_id FK, reagent_id FK, amount, unit)
attachment(id PK, experiment_id FK, step_id FK NULL, file_uri, mime, sha256)
version(id PK, experiment_id FK, version_no, diff_json, author_id, created_at)
这里有两个设计要点。第一,step_sample 用 role 区分输入与输出,而不是给样品表加“方向”字段,因为同一样品可能既是某步的产物又是下一步的原料,用关联表更自然。第二,version 不存整份快照,而存 diff_json,既节省空间,又能精确回答“谁在什么时候改了哪一步的哪个字段”。
JSON 文档模式承载步骤数据
步骤内的具体数据(如反应温度曲线、称量记录、观察描述)结构差异大,硬塞进关系表会导致大量空列。更合适的做法是:关系表保存可检索的骨架字段,步骤数据以 JSON 文档存入 step.data_json。例如一个加热步骤:
{
"step_type": "heating",
"params": { "target_temp_C": 80, "ramp_min": 15, "hold_min": 30 },
"observations": [
{ "t_min": 0, "temp_C": 25, "note": "开始升温" },
{ "t_min": 15, "temp_C": 80, "note": "到达目标温度" },
{ "t_min": 45, "temp_C": 80, "note": "保温结束,溶液澄清" }
],
"deviations": []
}
为了让 JSON 可校验,应约定 step_type 对应的 schema,例如 heating 必须含 target_temp_C 与 hold_min。这样既保留灵活性,又能在录入时拦截缺字段、单位错误等问题。
版本与附件的一致性
版本号建议采用“实验级递增 + 步骤级 diff”的组合:每次提交生成新的 version_no,diff_json 记录被修改的步骤 ID 与字段路径。附件则通过 sha256 去重,同一文件被多个实验引用时只存一份,避免重复占用存储。审计时,用 version 表可以重建任意历史状态,用 attachment 表可以追溯原始图谱文件。
step_type 定义 JSON schema,最后补版本与附件的审计字段。不要一开始就把所有字段塞进一张宽表。一个可落地的建模检查清单
把上面的设计落到具体项目时,可以用下面这份清单逐项核对,避免遗漏关键字段:
| 实体 | 必填字段 | 关键约束 |
|---|---|---|
| experiment | id, title, owner_id, status | status 取值 draft/running/done/archived |
| step | id, experiment_id, seq_no, name | seq_no 在同一实验内唯一且连续 |
| sample | id, name, type, amount, unit | amount 为正数,unit 受控词表 |
| instrument | id, name, model, calib_due | calib_due 早于步骤开始时间时告警 |
| reagent | id, name, lot_no, purity | lot_no 用于追溯批次 |
| attachment | id, file_uri, sha256 | sha256 唯一,支持去重 |
| version | id, experiment_id, version_no, diff_json | version_no 递增,diff_json 可解析 |
实际录入时,一个常见错误是把“样品”和“试剂”混为一谈。试剂是库存中的物料,样品是实验中流转的对象;同一种试剂可以生成多个样品,因此两者应分表存储,通过 step_reagent 与 step_sample 分别关联。另一个错误是忽略 parent_sample_id,导致无法追溯样品从哪一步、哪一批次派生而来。补上这个自引用外键后,就能回答“这批产物来自哪次实验的哪一步”这类审计问题。
综合来看,关系模式负责骨架与一致性,JSON 文档负责步骤数据的弹性,版本与附件负责审计与追溯。三者配合,才能让 ELN 既严谨又实用。