mcpField
R
rangefind

rangefind

本地服务
rangefind.dev搜索与检索311 周前更新

关于 rangefind

Rangefind 是面向大型网站和数据集的静态搜索引擎,无需专用搜索服务器。它构建可按范围读取的索引,支持浏览器、Node 和移动端运行时,以及文本、地理和自动补全等搜索。

主要功能

  1. 01构建静态稀疏索引
  2. 02BM25F 风格字段加权评分
  3. 03文本、地理和分面搜索
  4. 04自动补全与拼写纠正
  5. 05浏览器、Node 和移动端运行时
  6. 06文本与向量混合搜索

适用场景

  1. 01为静态网站添加搜索
  2. 02无搜索服务器地检索大型语料
  3. 03提供地图地点和半径搜索
  4. 04在移动应用中实现离线搜索

MCP 相关

Node 运行时可用于 MCP 服务,并支持在本地索引目录或远程 HTTP(S) 索引上运行查询引擎。输入未说明具体 MCP 服务会暴露哪些工具或资源。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"xjodoin-rangefind": {
"command": "npx",
"args": [
"-y",
"rangefind-mcp"
]
}
}
}
启动命令npx -y rangefind-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmrangefind-mcp0.3.2

工具

4

收录的工具列表 · 更新于 2 周前

参数类型必填说明
indexstring是要查询的索引:已配置的索引名称(参见 rangefind_list_indexes),或在开放模式下,任意 rangefind 索引的本地目录路径或 http(s) URL。

详细介绍

Rangefind

Rangefind 是一款面向大型网站的静态搜索引擎,适用于无法或不应运行搜索服务的网站。

它会将稀疏倒排索引构建为静态文件,然后让浏览器通过 HTTP Range 请求查询该索引。核心思路很简单:保持逻辑搜索分片较小,同时将它们打包到少量静态文件中,从而让部署和冷启动切实可行。

此代码库是一个生产原型的独立提取版本,该原型最初为大型论文语料库构建。

已实现的功能

以下列表是简要概览。完整功能指南 通过可运行示例,详细介绍了所有受支持的搜索路径、构建模式、运行时、集成方式、功能组合、回退机制和有意设定的边界。

  • JSONL 文档输入。
  • 静态站点爬虫:rangefind build ./dist 可直接为构建后的 HTML 编制索引,并通过 data-rangefind-* 属性限定正文范围、设置元数据、分面和排序。
  • 由架构驱动的加权字段。
  • BM25F 风格的字段评分。
  • 标题/标题字段的短语信号。
  • 自适应倒排列表分段。
  • 独立压缩的倒排列表分段打包到 terms/packs/*.bin.gz 中。
  • 单一 ESM 浏览器运行时包,位于 dist/runtime.browser.js。
  • Node 运行时(rangefind/node),适用于 MCP 服务、CLI 和 SSR:对本地索引目录(通过位置文件读取)或远程 HTTP(S) 索引使用相同的查询引擎,并提供与浏览器等效的缓存——不可变的内容寻址对象缓存在磁盘和内存 LRU 中,清单则通过 ETag 重新验证。
  • 移动端运行时(rangefind/mobile),适用于 React Native/Hermes 和其他嵌入式 JS 宿主:通过位置文件读取,对捆绑或下载的索引进行完全离线搜索;也支持带内存和持久化缓存的远程 HTTP(S) 索引——另请参阅 docs/mobile.md(也涵盖 WebView、Flutter 和原生应用)。
  • 延迟分页的二进制范围目录,位于 terms/directory-root.<hash>.bin.gz 和 terms/directory-pages/*.bin.gz。
  • 受 ZFS 启发的对象指针,在解压缩前进行 SHA-256 校验。
  • 内容寻址的不可变包和目录文件名,便于 CDN 缓存。
  • 构建索引时对完全相同的压缩对象进行去重。
  • 按局部性排序的文档包,配有小型文档 ID 序号表和密集指针记录,以减少结果获取的数据传输量。
  • 用于浏览、筛选和排序结果页的密集二进制文档页负载包,适用于原始文档 ID 聚集的情况。
  • 带显示字段上限的范围打包结果负载。
  • 面向高 df 词项、可按范围寻址的倒排列表块侧车文件。
  • 可选的权威侧车文件,用于精确的标题/实体/别名补救,而无需更改主倒排索引;单个标题权威索引还能提供有界前缀自动补全,无需第二个大型构建结构。
  • 用于高基数元数据的范围打包二进制分面字典。
  • 基于文件的构建流水线,包含不可变倒排列表分段、选定词项暂存文件、原始/压缩文档暂存文件、基于堆的分段合并和采样构建遥测。
  • 最精简的运行时清单,完整清单和遥测侧车文件均按需加载。
  • 浏览器运行时支持自适应 HTTP Range 合并和有界超量获取。
  • 主索引拼写纠正使用有界词汇表分片探测,并且仅在首页结果为空或质量较低时执行得分最高的纠正搜索。
  • 多值关键词分面,字典按需加载。
  • 可按范围寻址、类型化的数值、日期和布尔文档值,用于筛选和排序。
  • 延迟加载的二进制排序文档值树,用于按范围剪枝的 top-k 排序和按文档顺序提前终止的浏览。
  • Lucene 风格的地理点字段,配有可按范围寻址的静态 KD 树:边界框和半径筛选、带提前终止证明的精确最近邻距离排序(支持有文本查询和无文本查询)、文本与地理筛选、单元格筛选摘要和距离加权。
  • 可选的紧凑型地理结果封装和多分辨率分面到单元格索引,用于单次范围读取的地图结果和类别感知空间路由,并提供适用于任意名称和品牌的通配符占用通道。
  • 通用路线走廊搜索,支持编码折线、坐标数组或 GeoJSON:精确的点到路线筛选、多分辨率走廊单元格、进度/方向/视口排序,以及每个结果的重汇合点。
  • 静态路由和行程规划(rangefind/route):将 OSM 道路网络构建为 CRP/MLD 路由图,其中单元格和覆盖对象可按范围寻址——支持带几何信息和命名步骤的点到点驾车路线、行程时间矩阵和多站点行程优化;所有结果都与完整图上的 Dijkstra 算法精确一致,并通过静态文件提供,每次查询的获取量有上限;分片构建共享一个顶层覆盖对象,路由结果与单体构建完全一致——请参阅 docs/route-graph.md。
  • 范围寻址的权威索引内支持输入即搜索的自动补全:支持折叠变音符号的前缀和词中标签匹配、热度或自定义权重、带分片权重证明的精确 best-first top-k,以及预计算的热门列表,让首次按键只需一次小型获取——无需第二套建议包,也无需与语料库规模相当的堆映射。
  • 基于可按范围寻址的 IVF 向量索引进行混合语义搜索:int8 量化嵌入、粗维度候选页和固定宽度全维度精排存储、余弦 top-k,以及与文本通道进行倒数排名融合——无需服务端或向量数据库。
  • 搜索结果提供与分析器一致的摘要和高亮范围。
  • 多语言分析(analysis 配置):按文档检测语言或指定显式语言字段;为 20 多种语言提供轻量词干提取器和停用词;支持感知文字系统的 Unicode 折叠(拉丁文、希腊文、西里尔文、阿拉伯文、希伯来文、天城文);无需词典的 CJK 双字节分词可在 Node 和所有浏览器中保持确定性;跨语言查询扩展会依据索引证据选择基础方案;整个配置会固化到清单中,确保查询分析始终与索引一致。
  • 增量发布:rangefind build --update 可在现有索引上添加小型增量代次——未变更的包字节保留其内容寻址名称(以及 CDN 缓存条目),被替换的文档会将旧版本标记为墓碑;运行时会合并各代中的每个通道(文本、排序浏览、地理、向量、混合、建议、分面),评分始终完全可比。代次累积过多时,运行 rangefind build --compact 可将其折叠回单一索引。
  • 每次查询均提供分面计数,并明确标记精确性:基于字典的全局计数、对预算内匹配集合进行精确计数,以及对超大结果集进行有界分块采样估算。
  • OpenStreetMap 地点和地址测试数据,支持规范化的零倒排列表地址查找、紧凑的 addr:interpolation 范围、结构化正向和反向地理编码、有界的多提供方市政/邮政信息补充(包括 Québec RQA 适配器)、可复用的 rangefind/osm 和 rangefind/osm/node 集成 API,以及穷举式地理测试预言机。
  • OSM 类型化和自然语言约束,涵盖无障碍、服务、支付、座位、预订、费用和当前营业状态;保守地在客户端评估 opening_hours;支持路线感知地点搜索、紧凑地点详情,以及用于真实多边形/折线地图渲染的简化编码区域几何。
  • 面向行星级语料的地理索引分片:按区域独立构建和更新的分片共享一个冻结的评分统计数据工件(scoringStats 配置 + rangefind/scoring-stats);提供精简的分片根清单(rangefind/shards)、支持边界框地理路由和由近及远扩展搜索的延迟加载分片引擎,以及合并后的通道,其排名与同一语料的单体构建完全一致——请参阅 docs/sharded-osm.md。
  • 链接图权威度(linkRank):静态站点爬虫将内部 <a href> 链接解析为文档图,并将构建时的 PageRank 先验折叠到可排序的数值文档值中。链接良好的页面在排名接近时更靠前(可选择关闭的乘法加权,score *= 1 + boost*linkRank,通过有界窗口重排应用,不会改变评分热循环);你还可以按权威度排序,查询时无需遍历图。任何了解自身边关系的语料都可以使用即用型 examples/link-graph-enrich.mjs 增强器(或导出的 rangefind/link-graph 辅助工具)启用此功能;请参阅 docs/link-graph.md。
  • 小型可运行示例。

存在的原因

大多数浏览器搜索库都假设浏览器会下载整个索引,或者针对较小的网站进行优化。Rangefind 适用于以下需求:

  • 无需后端服务,
  • 无需托管搜索服务提供商,
  • 兼容静态托管,
  • 可衡量检索质量,
  • 即使逻辑分片众多,请求数仍然很少,
  • 索引格式专为 top-k 检索设计。

快速开始

演示:https://rangefind.dev/ — 一个基于滚动更新的公开分片索引的 OpenStreetMap 地图搜索(支持自动补全、视口地理查询和最近邻搜索),以及一个最简示例;两者都以纯静态文件形式提供。

已生成的行星级 OSM 索引可供浏览器或 Node 直接免费查询,无需账户或 API 密钥:

import { createSearch } from "rangefind";

const engine = await createSearch({
  baseUrl: "https://osm.rangefind.dev/"
});

请在 https://osm.rangefind.dev/ 查看实时覆盖范围和数据新鲜度。这是一项尽力而为的公共服务;如果你需要有保障的容量、固定版本、自定义字段或运营 SLA,请自行托管该索引。OSM 结果界面必须显示 © OpenStreetMap contributors。

npm install
npm run build:example
npm run test:smoke
npm run serve:example

打开 http://localhost:5178/。

文档

根据你的任务选择相应指南:

指南适用场景
功能指南完整的功能目录、示例、支持的组合、兼容性和限制。
参考配置属性、构建器/运行时 API、调优选项、UI 组件和部署要求。
架构静态文件布局、范围目录、倒排列表检索、地理功能、权威度和向量内部机制。
自动补全指南边输入边搜索模式、每次击键的实测开销、填充预览、即时选择和反模式。
OSM 示例地图搜索、自动补全、地理编码、约束条件、路线走廊、几何、数据提取和 RQA。
替代 Google Maps API免费公开的 OSM 索引、迁移适配器、API 映射、完整用例、生产环境检查清单、署名要求和功能对等边界。
Node / 移动端本地、远程、缓存、嵌入式和离线运行时。
分片 OSM行星级区域构建、根路由和精确的分数可比性。
路线图基于范围寻址道路网络单元及叠加层的静态路由、出行时间矩阵和行程规划。
生产级地图基准测试Google Maps 风格的工作负载覆盖、质量断言和手机端预算。

抓取静态站点

如果你已有构建好的静态站点(由任意生成器生成),只需将 Rangefind 指向输出目录,它就会直接为 HTML 建立索引,无需编写 JSONL:

npx rangefind build ./dist

这会抓取 ./dist 下的所有 .html/.htm 文件,提取标题、标题层级和正文文本,并将索引写入 ./dist/rangefind(可通过 --output 更改)。结果 URL 默认使用站点根路径(--base-url 可设置前缀或源,例如 --base-url https://example.com/)。部署站点后,使用 createSearch({ baseUrl: "/rangefind/" }) 加载索引。

内容选择遵循文档结构:爬虫会为 <main> 建立索引;如果没有,则使用 <article>;再没有,则使用 <body>,并始终排除 <script>、<style>、<nav> 和 <aside>。只有使用回退的 <body> 区域时(不存在 <main>/<article>),才会排除 <header>/<footer>;因此,文章内部的页眉及其 <h1> 仍会被编入索引。可选择启用 data-rangefind-* 属性(沿用 Pagefind 的词汇)进行调整:

属性效果
data-rangefind-body仅为标记元素内部的文本建立索引。
data-rangefind-ignore排除此子树(用于 <html>/<body> 时会跳过整个页面)。
data-rangefind-meta="name"将元素文本捕获为元数据 name("name:attr" 会读取属性)。
data-rangefind-filter="key"将元素文本作为 key 下的一个分面值添加。
data-rangefind-sort="key"将元素文本作为可排序值 key。

每个文档的语言取自 <html lang="…">,页面描述取自 <meta name="description">。完整规则请参阅参考指南。

搜索 UI 组件

将爬虫与即插即用的搜索框搭配使用。<rangefind-search> 是一个与框架无关的 Web Component:只需两行 HTML,就能为任何静态站点添加精致且易于访问的搜索框(WAI-ARIA combobox、键盘导航、即时结果、自动补全)。

<script type="module" src="https://unpkg.com/rangefind/dist/rangefind-search.js"></script>
<rangefind-search src="/rangefind/"></rangefind-search>

它会渲染到自己的 light DOM 中,且不附带 任何样式,因此可以与现有 CSS 配合使用。可通过三种方式设置样式:可选主题样式表、使用 rf-search* 钩子类编写自己的 CSS,或为各个部分传入工具类。下面仅使用 Tailwind 设置样式,不含主题 CSS:

<script src="https://cdn.tailwindcss.com"></script>
<script type="module" src="https://unpkg.com/rangefind/dist/rangefind-search.js"></script>

<rangefind-search
  src="/rangefind/"
  placeholder="Search…"
  input-class="w-full rounded-lg border px-3 py-2"
  panel-class="absolute inset-x-0 mt-2 rounded-lg border bg-white shadow-xl"
  option-class="block rounded px-3 py-2 hover:bg-slate-100 aria-selected:bg-blue-50"
  option-title-class="font-semibold"
  option-snippet-class="text-sm text-slate-500"
  mark-class="bg-yellow-200"></rangefind-search>

更喜欢开箱即用的外观?引入可选主题(浅色 + 深色),并省略 *-class 属性:

<link rel="stylesheet" href="https://unpkg.com/rangefind/dist/rangefind-search.css">
<rangefind-search src="/rangefind/" placeholder="Search…"></rangefind-search>

可用于纯 HTML、React、Vue、Svelte 和 Angular——Web Components 通用。它会触发 rangefind:search、rangefind:select 和 rangefind:error 事件,并提供 .classNames / .searchOptions JS 属性,供程序化使用。使用 npm run build:element 构建 bundle;可针对基础示例进行测试(npm run build:example && npm run serve:example),访问 /component.html(主题)和 /component-tailwind.html(Tailwind)。所有属性、完整的类钩子列表、事件和无障碍说明,请参阅参考指南。

静态站点生成器适配器

爬虫和搜索组件可以自然地融入任何静态站点生成器自身的构建生命周期——适配器会在生成器写入输出后立即运行爬取,并为你放入客户端资源,因此除了安装适配器,无需手动进行任何连接配置。

生成器软件包接入方式
Astrorangefind-astro在 astro:build:done 上运行的真实集成,并提供 <RangefindSearch /> 组件。
Eleventyeleventy-plugin-rangefind在 eleventy.after 上运行的真实插件,并提供通用的 {% rangefindSearch %} 短代码。
Docusaurusdocusaurus-plugin-rangefind在 postBuild 上运行的真实插件,并通过 injectHtmlTags 注入;将 <rangefind-search> 放入导航栏项或 MDX 即可。
Hugointegrations/hugoHugo 没有插件加载器,因此这里提供的是 hugo && rangefind build public 配方以及可复制粘贴的局部模板。
MkDocsmkdocs-rangefind(PyPI)一个真实的 Python 插件(on_post_build / on_post_page),会调用 Node 命令行工具,并自动将小组件注入每个页面。

Astro、Eleventy 和 Docusaurus 都是在 packages/ 下的真实、可独立安装的软件包(除目标生成器本身外零依赖);Hugo 和 MkDocs 则位于 integrations/ 下,因为它们都没有自己的 npm 插件系统。所有适配器都经过端到端验证,使用真实工具构建——真实的 Astro/Eleventy/Docusaurus 构建、通过 Homebrew 安装的 Hugo 二进制文件,以及通过 pip 安装的 MkDocs——爬取测试站点,并确认生成的索引确实能响应搜索查询。安装说明和选项请参阅各软件包自己的 README。

完整的 Wikipedia 搜索站点

examples/wiki-search 项目是一个更完整的静态搜索应用,适用于 Wikimedia 文章转储文件。默认使用最新的英文 Wikipedia 文章转储文件,也可以针对有界样本或完整转储文件运行:

npm run build:wiki-site -- --limit=50000
npm run serve:wiki-site

使用 npm run build:wiki-site:full 构建默认转储文件的完整版本,并应用有界正文上限;或者使用 npm run build:wiki-site:fr:full 构建完整的法语 Wikipedia 转储文件。生成的站点位于 examples/wiki-search/public/,并遵循与所有 Rangefind 索引相同的静态托管要求:主机必须支持对 .bin.gz 文件发起 HTTP Range 请求。

法语 Wikipedia 可扩展性测试夹具

Rangefind 提供了一个可复现的法语 Wikipedia 测试夹具,它会流式读取 Wikimedia 官方文章转储文件、构建静态站点,并对查询延迟、请求次数和传输大小进行基准测试:

npm run build:browser
node scripts/frwiki_fixture.mjs all --limit=50000 --runs=3

对于仅涉及运行时的更改,请复用已有的生成索引,只重新运行请求和传输基准测试:

node scripts/frwiki_fixture.mjs runtime-bench --limit=50000 --runs=3

使用 --limit=0 对完整转储文件运行。生成的站点位于 examples/frwiki/public/。默认情况下,该测试夹具会针对精确检索路径验证文本查询的 top-k 结果,并记录冷启动请求次数、传输字节数、运行时 posting 块统计数据、类型化过滤器和排序验证,以及多个 Wikipedia 样本大小下的规模报告。 提取出的 JSONL 会缓存在 examples/frwiki/data/ 下,因此后续运行只要使用相同的转储文件和正文上限,并且限制值相等或更小,就不会再次流式读取转储文件。每个生成的索引都包含 manifest.min.json、manifest.full.json 和 debug/build-telemetry.json。完整诊断文件 manifest.json 仍会写入,供本地检查,并记录各阶段耗时、采样内存峰值、CPU 时间、磁盘字节数变化和分段计数器。

构建自定义索引

完整的配置架构、所有调优选项、完整的运行时 API 和部署要求,请参阅 参考指南。以下演练是 快速版。

创建以换行符分隔的 JSON:

{"id":"1","url":"/a","title":"Static search","body":"Search without a server","category":"docs","tags":["static","range"],"year":2026,"published":"2026-01-10","featured":true}
{"id":"2","url":"/b","title":"Range packs","body":"Use HTTP byte ranges","category":"index","tags":["range"],"year":2026,"published":"2026-02-01","featured":false}

创建 rangefind.config.json:

{
  "input": "docs.jsonl",
  "output": "public/rangefind",
  "idPath": "id",
  "urlPath": "url",
  "indexProfile": "static-large",
  "targetPostingsPerDoc": 12,
  "bodyIndexChars": 6000,
  "alwaysIndexFields": ["title", "category"],
  "typoMode": "main-index",
  "typoTrigger": "zero-or-weak",
  "typoMaxEdits": 2,
  "typoMaxTokenCandidates": 8,
  "typoMaxQueryPlans": 5,
  "typoMaxCorrectedSearches": 3,
  "typoMaxShardLookups": 12,
  "display": ["id", "url", "title", "body", "category", "tags", "year", "published", "featured"],
  "fields": [
    { "name": "title", "path": "title", "weight": 4.5, "b": 0.55, "phrase": true },
    { "name": "body", "path": "body", "weight": 1.0, "b": 0.75 }
  ],
  "authority": [
    { "name": "title", "path": "title" },
    { "name": "aliases", "path": "aliases" }
  ],
  "facets": [
    { "name": "category", "path": "category" },
    { "name": "tags", "path": "tags" }
  ],
  "numbers": [
    { "name": "year", "path": "year", "type": "int" },
    { "name": "published", "path": "published", "type": "date" }
  ],
  "booleans": [
    { "name": "featured", "path": "featured" }
  ]
}

display 仅控制随搜索结果返回的负载。索引字段可以保留较长内容,而返回字段可以设置上限,例如:

{ "name": "body", "path": "body", "maxChars": 640 }

对于大型静态语料库,targetPostingsPerDoc 是正文词项预算。 bodyIndexChars 仅限制索引器处理的文本,而显示负载大小仍由 display 条目控制。应用正文预算之前,会先索引 alwaysIndexFields 中的词项。

typoMode: "main-index" 使用常规词项词汇表生成拼写错误候选项, 而不单独构建拼写错误 sidecar。使用 typoMode: "off" 可禁用纠错。

authority 字段会为规范标签构建打包词典,例如标题、实体名称、产品名称、slug 和别名。运行时会先尝试保留变音符号的表面精确键,只有在需要时才回退到折叠后的精确键和词元键,因此常见的标题补全既精准又高效,无需将所有标签逻辑都放进 BM25 倒排列表。suggest 字段会在自动补全命名空间下流式传入同一个外部运行归并器,因此数百万文档的构建过程不会创建重复的内存标题映射或单独的 suggest/ 打包系列。

构建:

npx rangefind build --config rangefind.config.json

在浏览器中查询:

import { createSearch } from "rangefind";

const engine = await createSearch({ baseUrl: "/rangefind/" });
const result = await engine.search({ q: "static search", size: 10 });
console.log(result.results);

筛选和排序使用按范围寻址的文档值列:

const result = await engine.search({
  q: "static search",
  filters: {
    facets: { tags: ["range"] },
    numbers: { published: { min: "2026-01-01" } },
    booleans: { featured: true }
  },
  sort: { field: "published", order: "desc" }
});

地理字段会将每个文档中的一个点索引到按范围寻址的静态 KD 树中(类似 Lucene LatLonPoint,采用 E7 固定精度):

{
  "geo": [{ "name": "location", "latPath": "lat", "lonPath": "lon" }]
}
// Places inside a map viewport (empty-query browse).
await engine.search({ q: "", geo: { box: { minLat: 45.45, maxLat: 45.62, minLon: -73.7, maxLon: -73.45 } } });

// Exact nearest neighbors, sorted by distance with early-stop proofs.
await engine.search({ q: "", geo: { near: { lat: 45.5017, lon: -73.5673 }, sort: "distance" } });

// Exact nearest matches for a text query ("closest bakeries first").
await engine.search({ q: "bakery", geo: { near: { lat: 45.5017, lon: -73.5673 }, sort: "distance" } });

// Text search restricted to a radius, plus a Lucene-style distance boost.
await engine.search({
  q: "bakery",
  geo: {
    near: { lat: 45.5017, lon: -73.5673, radiusMeters: 5000 },
    boost: { weight: 2, pivotMeters: 500 }
  }
});

只要存在 geo.near,结果就会包含 distanceMeters。半径和边界框筛选均为精确筛选(先通过边界框剪枝,再使用 Haversine 公式验证),stats 会报告树遍历情况(geoLane、geoLeavesVisited、geoPointsScanned,等等)。请参阅 examples/osm-geo/,了解 OpenStreetMap 规模的示例和基准测试。

加权的标签中间部分即时搜索建议使用 authority 词典:

{
  "suggest": [{ "path": "title" }]
}
const { suggestions } = await engine.suggest({ q: "boul", size: 8, hydrate: true });
// [{ text: "Boulangerie Fischer", weight: 12, count: 12, doc: 41,
//    result: { title: "Boulangerie Fischer", url: "…", … } }, ...]

匹配基于折叠变音符号后的键进行前缀匹配("montre" 可找到 "Montréal"),并支持标签中的词元("eiffel" 可找到 "Tour Eiffel")。 排序使用可选的 weightPath(例如人口或重要性),并以直接前缀优先级 加人气度作为回退方式——即共享该表面的文档数量。每次按键最多只需进行少量小范围请求;同一会话中的后续按键通常完全由缓存提供服务。

建议还会携带每个表面对应的最佳文档(doc/docs),而 hydrate: true 会将它们解析为实际搜索结果,也就是 search() 返回的相同显示负载。这样,下拉菜单可以显示结果卡片,选中的建议也可以立即呈现,而无需将其作为搜索再次运行查询(实测为 0.5 KB 和 4 ms,而重新搜索其文本则为 215 KB 和 32 ms)。**自动补全指南**介绍了这些模式、每次按键的开销,以及何时优先使用聚焦预览而不是为每一行都进行填充。

对于较旧索引上的标题前缀补全,单个 authority: [{ "name": "title", "path": "title" }] 字段也足够。 运行时会按键顺序遍历首批匹配的 authority 分片,并仅填充所请求的标题。 新构建应配置 suggest,以便在相同的 authority 包中使用自定义权重、人气度回退、标签中间部分补全和热门前缀列表。

筛选 UI 所需的分面计数会随搜索响应一并返回:

const result = await engine.search({ q: "static", facets: ["tags"] });
// result.facets.tags = { values: [{ value: "range", label: "range", count: 12 }, ...], exact: true }

混合语义搜索会从宿主获取查询嵌入(例如在浏览器中使用 transformers.js),并将其与文本通道融合:

const result = await engine.search({ q: "map search", vector: queryEmbedding });
const nearest = await engine.vectorSearch({ vector: queryEmbedding, k: 10 });

拼写错误回退是自动的。例如,如果 statik search 没有精确的首页结果,但 static search 有,响应会包含:

{
  correctedQuery: "static search",
  corrections: [{ from: "statik", to: "static", surface: "static" }]
}

静态托管要求

运行时要求托管服务支持对 .bin.gz 文件发起 HTTP Range 请求。 GitHub Pages 支持此功能。随附的本地服务也支持此功能。

开发

npm run check
npm test
npm run test:smoke
npm run test:all
npm run bench:quality
npm run bench:performance
npm run bench:directories -- --index=/path/to/public/rangefind
npm run bench:osm-maps

单元测试涵盖分析器规范化、二进制 varint/定长编解码、配置解析、分片/范围规划、词项/代码二进制往返,以及针对本地 HTTP Range 服务的端到端构建和浏览器运行时查询。 端到端构建测试还会验证清单中输出的构建遥测数据和基于文件的 spool 计数器。

基准测试脚本无需依赖项,并针对示例静态站点运行。bench:quality 会报告已知条目和拼写错误恢复的 Hit@k/MRR,以及针对分面、日期、布尔值和带符号数字的结构化筛选/排序检查。 bench:performance 会报告查询延迟、HTTP 请求数和传输大小。 bench:directories 会针对现有的已构建索引,比较全局、朴素前缀和分页范围目录布局。 bench:osm-maps 会运行 docs/osm-maps-benchmark.md 中记载的加权常见地图搜索工作负载,涵盖局部性、最近优先、视口、分片扇出和手机预算检查。 docs/performance-research.md 记录了目前指导格式决策的 top-k 检索论文。

项目方向

计划中的下一个里程碑:

  • Pagefind/Lucene/SQLite 基准测试包,
  • 构建时稀疏扩展钩子,
  • 无 WASM 与借助 WASM 的运行时比较,
  • CI 发布工作流 + 已发布的 npm 包。

欢迎贡献,详见 CONTRIBUTING.md。

许可证

MIT © Xavier Jodoin

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。