为超过 42,000 个阿尔卑斯山屋、露营地和山峰进行映射面临着独特的技术挑战:许多地点共享相同的名称,例如“Jagdhütte”或“Rosskopf”。为了解决这个问题,biwak.info 实施了一套层级化的 slug 系统,在生成唯一的、可分享的 URL 的同时,确保一旦链接发布,即使底层地理数据发生变化,链接依然有效。

biwak.info 平台开发者 Peter Hoffmann 的肖像
开发了 biwak.info 层级化 slug 系统的 Peter Hoffmann。peter-hoffmann.com

这是负责 slug 注册系统技术实现的开发者的肖像。

非唯一阿尔卑斯名称的挑战

在阿尔卑斯地理中,名称在不同山谷中频繁重复。例如,“Jagdhütte”这一名称出现在 70 个不同的地点。使用简单的名称到 URL 的映射会导致冲突,即像 /p/jagdhuette 这样的单一 URL 只能指向这 70 个地点中的一个。

开发者测量了不同命名后缀的影响,发现添加国家名称几乎没有帮助,因为大多数重复发生在同一个国家内。添加区域大约减少了一半的重复数量,但仍需要海拔数据来解决大部分剩余的冲突。

层级化 Slug 生成逻辑

系统遵循特定的优先级顺序来生成最短的唯一 slug。它首先尝试纯名称;如果已被占用,则附加区域;如果仍被占用,则附加海拔;作为最后的手段,则附加唯一的 OpenStreetMap ID。

为了确定正确的区域,系统使用简化的 Natural Earth 边界执行“点在多边形内”查询。如果由于简化的边界导致某个地点落在多边形之外,系统将识别最近的多边形以分配正确的区域后缀。

该过程通过一个按优先级顺序产出候选值的生成器实现自动化。这确保了像蒂罗尔的“Rosskopf”这样的地点变为 /p/rosskopf-tirol,而更具体的地点可能会变为 /p/rosskopf-tirol-2257。

通过注册系统确保 URL 稳定性

动态数据系统中的一个重大风险是“slug 漂移”,即添加新条目会导致现有条目更改其 URL 以适应新数据。为了防止这种情况,biwak.info 维护了一个承诺制的 slug 注册表,将每个发布的 slug 映射到特定的 OpenStreetMap ID。

该注册表遵循三条严格规则:注册表中的 slug 永远不会移动到不同的地点;只要地点仍符合其当前名称,它就保留其注册表 slug;新地点或重命名地点接收来自层级结构的第一个可用候选值。

这种架构确保了即使在源数据中添加第二个“Matterhorn”,原始的“Matterhorn”仍将保留其 /p/matterhorn URL,而新加入的地点将被分配带有后缀的 slug。

权衡与局限性

虽然系统优先考虑可读性,但它无法保证为每个地点提供完全符合人类习惯的 URL。目前,有 458 个地点需要在 slug 中包含 OpenStreetMap ID,因为它们共享名称且在源记录中缺乏海拔数据。

注册表是一个不断增长的文件,记录了所有发放过的 slug。因为它是一个排序后的 JSON 文件,任何对 slug 生成规则的更改——例如开发者将德语变音符号从“u”切换到“ue”——都会产生可读的 git diff,从而允许轻松审计数千个受影响的 URL。