SuperAPI AI 工具集

searchable-events

数据与分析

查看项目

更新于 10-16 06:16

技能介绍

用途:定义 Quartz 中 NIP-50 全文搜索的索引表面。事件类实现 SearchableEvent 接口(方法 indexableContent(): String)即表示该事件种类可被搜索,前提是它已在 EventFactory 中注册;存储通过 EventFactory.create 按 kind 探测可搜索性,未注册的实现不可见。

主要能力与规则:indexableContent() 返回人类可读字段并用换行连接(少数元数据类用空格);不做标记剥离,markdown/asciidoc 原样进入;JSON 内容类(kind 0 元数据、市场摊位、频道信息)先解析再拼接提取字段,绝不索引原始 JSON。实现不得抛异常、不得返回 null,因为没有防御性包装,抛异常会中止插入事务;解析 JSON 的实现使用 ?.let { … } ?: ""。只索引公开数据,加密内容不入索引(如 kind 30382 只索引公开的 petname/summary/topics,不含 NIP-44 载荷)。常见形态:仅 content(约 33 种)、listOfNotNull(title(), content)、listOfNotNull(title(), summary(), content)、列表类索引 title() + description()。

维护与使用方式:资料在 references/searchable-kinds.md 提供权威种类表(截至 2026-08 共 126 个具体类、129 个 kind 值),含 kind 号、类名与确切的 indexableContent() 表达式;版本升级时对比该文件可判断可搜索集合或索引文本是否变化。新增、移除或修改 indexableContent() 必须在同一 PR 更新该文件,因为外部搜索引擎(如 Vespa 存储的 SearchExtractors)按 pin 版本镜像该表。既有数据库不会自动重建索引,旧行保留旧索引文本,直到运行 IEventStore.reindexFullTextSearch()(其 KDoc 即契约);应用侧应在发布此类变更后调度可恢复的重载。新实现必须注册进 EventFactory,否则重建索引扫描和 FullTextSearchModule.isSearchableKind 的 kind 预过滤都看不到它。

存储消费:SQLite 侧用 FTS5 建 contentless 虚拟表 event_fts(rowid = event_headers.row_id,AFTER DELETE 触发器同步),插入时若事件是 SearchableEvent 则绑定 indexableContent(),这是唯一被调用的方法;分词完全用 SQLite 默认 FTS5 unicode61,查询始终用参数绑定的 event_fts MATCH,按 bm25 排名再按 created_at DESC。文件系统存储自行分词,近似 unicode61(按非字母数字切分并小写化),查询用同一分词器。

NIP-50 客户端:SearchQuery 把搜索过滤字符串解析为词项与扩展;形如小写 key:value 且值不以 // 开头的空白词视为扩展,重复键保留最后一个,未知扩展保留;类型化访问器有 includeSpam、domain、language、sentiment、nsfw;stripExtensions() 是内置存储使用的桥接,不支持的扩展被忽略,因此只有扩展的搜索会退化为无约束查询而非匹配空。自行实现扩展的服务端存储应使用 SearchQuery.parse 以保持语法兼容。kind 10007 是搜索中继列表事件(非 SearchableEvent 本身)。

限制:分类策略上只有携带人类撰写且有意义的文本的种类才可搜索,纯机器种类(反应、关注列表、去除评论的 zap、中继列表)不纳入以保持索引精简。kind 9735 索引内嵌 zap 请求的评论;kind 0/31990 空格连接多个资料字段;kind 30063 被两个类占用,EventFactory 解析为 ReleaseArtifactSetEvent,另一实现的 indexableContent() 在存储路径上不生效;投票类(1068、6969)每个选项标签独占一行。测试文件即可执行规范,覆盖扩展语法、逐 kind 索引、重索引与 FTS 关闭契约。