文献标本:一个默认隔离的标本大类

记录把「文献里引用的标本」正式接进昆虫物种数据库的一天。

关键词:默认隔离、链接而不是复制、回收站、没有 GPS 的分布怎么画。


一、起因:文献里的标本该住在哪

文献里常常直接引用标本,一行记录大概是这样:

2♂,云南 昆明 西山区 西山,2018.08.11,采集人 A、B

这些标本是真实存在的研究材料。但如果直接把它们录进标本库,问题立刻出现:

  • 它们不是站主亲手检视过的馆藏标本,却会在统计里冒充实体;
  • 分布地图上会多出一批「本库从没见过」的点;
  • 一旦想区分「我研究过的」和「文献提到的」,就再没有依据了。

站主给的两条底线:

绝不删除馆藏标本。

来自文献的标本默认隔离。

这两句话基本决定了后面所有的架构选择。


二、数据模型:两个预置字段 + 一张回收站表

2.1 隔离靠一个字段

标本表里其实早就预留了两个字段(站主自己设计的,之前一直没有数据):

字段含义
specimens_LitSpec1 = 这条记录来自文献(文献标本)
specimens_LitList这号标本被哪些文献引用(JSON 数组,可以多篇)

于是「默认隔离」就变成一句可复用的过滤条件:

def not_lit_specimen():
    """默认隔离"来自文献的标本"的过滤条件。"""
    return or_(Specimens.litspec.is_(None), Specimens.litspec != 1)

这里有一个必须写对的地方:

-- 错:会漏掉所有 NULL
WHERE specimens_LitSpec != 1

-- 对:
WHERE specimens_LitSpec IS NULL OR specimens_LitSpec != 1

SQL 里 NULL != 1 的结果是 NULL(不是真)。所以只要写成第一种,所有没标过的老标本会一次性从统计里消失 —— 这种错不会抛异常,只会在数字里静静地沉下去。

2.2 回收站是一张新表

这个库一直有「改动前先备份」的习惯,今天把「删除的文献标本」也纳入了体系:

列用途
trash_Kind类型,如 lit_specimen
trash_RefText来源文献:年份 + 作者 + 标题(字符串)
trash_DeletedAt删除时间
trash_Payload完整数据(JSON),便于日后人工恢复

RefText 为什么不用文献 ID?因为文献一旦删除,ID 就作废了,将来拿这个 ID 去找,只会找到另一个不相干的文献。用「年份+作者+标题」拼成字符串,哪怕文献不在了,人也看得懂这条数据是从哪来的。


三、录入:三种命中,一个按钮

录入一篇文献的标本时,系统依次判定:

情况处理是否计入统计
库里已有完全一致的馆藏标本(物种 / 采集地 / 采集信息 / 采集人一致)直接建立链接计入(它确实是库里的实体,也确实被用在文献里)
命中一条已有的文献标本直接链接不计入
都没有新建一条文献标本(LitSpec = 1)不计入

「完全一致」这四个字在实践中非常麻烦:采集地、日期、采集人只要有一个空格、一个标点不同,程序就认为不是同一条。

所以录入窗口上多了一个「搜索并链接已有标本」的按钮:不要求你把字符串抄对,而是让你搜出来点一下。补全联想与标本录入页同款,并且把文献标本的取值也一起纳入;但字段本身是自由输入 —— 文献里出现的记录,不一定在本库有对应标本。


四、展示:三处入口

4.1 文献详情页:「本文献标本」

  • 默认收起,展开后按物种分组:物种名占一行,下面是这号标本的卡片;
  • 一行可以放多张卡片,鼠标悬停可以预览标本照片(有图片时);
  • 点卡片直接跳到标本详情页;
  • 卡片上不再显示 GPS 字符串 —— 人眼无法从一串经纬度里读出「云南昆明西山」,这和删掉「详情 ↗」按钮是同一个理由:让条目本身就是入口。

4.2 标本详情页:「📚 本号标本相关文献」

只有当这号标本真的参与过文献时,按钮才出现;点开就是引用它的文献清单。没有数据的标本不会多出一个空栏目。

4.3 阶元库页面:「文献标本」

与「同种团」「检视标本」并列的新栏目:点开列出该阶元下的文献标本,并按国家 / 行政区域分组。

这一栏和地图的「本种 / 属 / 种团文献坐标」共用同一个数据入口 —— 数据还没录入,图层与栏目先把位置留出来,录入之后不需要再改代码。


五、删除:让数据可回收

删除一篇文献时,涉及三类东西,处理方式完全不同:

对象处理
馆藏标本只解除与文献的链接,标本本体绝不动
文献标本进回收站(含来源字符串、删除时间、完整数据)
文献本身正常删除

「绝不删除馆藏标本」在实现层面就是一句话:删除流程里没有一条 SQL 能删掉 specimens 里的馆藏记录。


六、B8:没有 GPS 的分布怎么画

文献标本最麻烦的地方是 GPS 常常没有,但采集地写得清清楚楚。所以这一类分布分三种画法:

数据情况画法
有 GPS橙色空心点位
没有 GPS,但采集地能识别到省该省的多边形填色
没有 GPS,采集地是国外国家色块
都识别不出归入「其它 / 未识别」,不猜

地名匹配这件事,上一篇已经踩过一次坑(「内蒙古」被识别成「蒙古」)。这次的要求更实际:文献里的地名不一定写中文或英文。

现在的策略是:

  • 简体中文:沿用原有逻辑;
  • 正式英文 / 拼音:用词边界匹配((?<![a-z])…(?![a-z])),避免 Yunnan 命中 Yunnanomonticola 这类属名;
  • 其它语言:暂不处理 —— 宁可归进「未识别」,也不猜。

顺带一提,匹配前同样做了「先遮蔽中国省名、再做国家匹配」的那一步,否则「内蒙古」还会被算进蒙古。


七、几处小修

「解除关联」改成右键菜单。 一个按钮贴在一行条目上,鼠标划过就能点到,误碰的代价是「这号标本突然不属于这篇文献了」。改成右键菜单之后,误操作需要两个明确动作。

物种胶囊右键菜单。 现在有两项:📄 添加标本信息 和 🗑 删除物种关联。后者只把物种从这篇文献的「涉及物种」里摘掉,不删物种、不动标本。

雄性数量字段的历史数据清理。 这个字段最初的设计是:空值 = 未指定,-1 = 无雄性,0 = 该号标本全是雌性。后来 0 的含义在一次升级中被确定下来,但数据没有跟着改 —— 一批原本代表「未指定」的 0 留在库里,等于把「不知道」写成了「确定全雌」。这次把它们改回空值:86 条,改动前有整库备份。

地图弹窗文案。 行政区划显示改为「省 地级市 县/区」,更符合中文语境(原来只写省)。


八、用一张标签照片做最后一道校验

标本标签的照片是可以「读」的。今天试了一次图片核对:

照片读出的编号 / 采集地 / 日期 / 采集人
        ↕ 逐项比对
数据库里这一条记录

结果完全吻合。

它替代不了人工复核,但作为交叉校验很有用:一条记录如果在库里是错的,照片会立刻把它指出来。


九、写在最后

今天真正的工作量不在功能上,而在口径上:

  • 「来自文献」和「馆藏」是两种东西,必须在数据层面分开,而不是在报表里绕开;
  • 「默认隔离」的价值在于默认值是安全的 —— 忘记加条件的代码,得到的是「保守」的结果,而不是「污染」的结果;
  • 删除前先想「以后怎么把它找回来」:回收站里存的不是 ID,而是人看得懂的来源。

以及一条今天又验证了一遍的规律:口径的每一次漂移,都会在数据里留下痕迹,而痕迹不会自己消失。 雄性字段那 86 条数据就是一次漂移的化石 —— 逻辑改对了,数据还得另外去修。

文献标本的真实数据目前还没有开始录入(库里 0 条)。今天把位置、图层、入口、回收站先建好了:录入之后,地图上会多出一层「橙色空心点 + 省多边形 + 国家色块」的文献坐标,而统计里的数字不会因此动一下。


本文由 Codex 整理,记录 2026-10-07 的 InsectDB 文献标本接入工作。

Comments

No comments yet. Why don’t you start the discussion?