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。
  • MCP サーバー、CLI、SSR 向けの Node ランタイム(rangefind/node):ローカルのインデックスディレクトリ(位置指定ファイル読み取り)またはリモート HTTP(S) インデックス上で、同じクエリエンジンを使用します。ブラウザーと同等のキャッシュ機能を備え、コンテンツアドレス方式の不変オブジェクトをディスクおよびメモリ内 LRU にキャッシュし、マニフェストを ETag で再検証します。
  • React Native/Hermes などの組み込み JS ホスト向けモバイルランタイム(rangefind/mobile):バンドル済みまたはダウンロード済みのインデックスを位置指定ファイル読み取りで検索する完全オフライン検索、またはメモリ+永続キャッシュを備えたリモート HTTP(S) インデックスに対応します。WebView、Flutter、ネイティブアプリにも対応しています。docs/mobile.md を参照してください。
  • terms/directory-root.<hash>.bin.gz および terms/directory-pages/*.bin.gz に格納される、遅延読み込みのページ分割バイナリ範囲ディレクトリ。
  • ZFS に着想を得たオブジェクトポインター。解凍前に SHA-256 で検証します。
  • CDN キャッシュ向けの、コンテンツアドレス方式による不変のパックおよびディレクトリのファイル名。
  • インデックス構築時の圧縮オブジェクトの完全重複排除。
  • ローカリティ順のドキュメントパック。小さな doc-id 序数テーブルと密なポインターレコードにより、結果取得時の転送量を抑えます。
  • 元のドキュメント ID がまとまっている場合に、ブラウズ、フィルター、ソート結果のページに使う、密なバイナリ doc-page ペイロードパック。
  • 表示フィールド数を上限付きにした、範囲パック形式の結果ペイロード。
  • df の高い用語向けの、範囲アドレス指定可能なポスティングブロックのサイドカー。
  • メインの転置インデックスを変更せずに、タイトル/エンティティ/別名の完全一致検索を補助する任意の権威サイドカー。単一のタイトル権威インデックスは、大規模な第 2 ビルド構造を追加せずに、上限付きの前方一致オートコンプリートも提供します。
  • カーディナリティの高いメタデータ向けの、範囲パック形式のバイナリファセット辞書。
  • 不変のポスティングセグメント、選択された用語のスプール、未加工/圧縮ドキュメントのスプール、ヒープベースのセグメントマージ、サンプリングされたビルドテレメトリーを備えた、ファイルベースのビルドパイプライン。
  • 完全なマニフェストとテレメトリーのサイドカーを遅延読み込みする、最小限のランタイムマニフェスト。
  • アダプティブな HTTP Range 統合と、上限付きの過剰取得に対応するブラウザーランタイム。
  • メインインデックスでの誤字修正。範囲を限定した語彙シャードのプローブを使用し、最初のページの結果が空または不十分な場合に限り、最も有力な修正候補の検索を実行します。
  • 辞書を遅延読み込みする、複数値キーワードファセット。
  • フィルターとソート用の、範囲アドレス指定可能な型付き数値、日付、真偽値の doc-values。
  • 範囲を枝刈りした top-k ソートと、ドキュメント順ブラウズの早期終了に対応する、遅延読み込みのバイナリソート済み doc-value ツリー。
  • 範囲アドレス指定可能な静的 KD ツリーを備えた、Lucene 形式の地理座標フィールド:バウンディングボックスおよび半径フィルター、早期終了証明付きの正確な最近傍距離ソート(テキストクエリの有無を問わず)、テキストと地理条件の組み合わせによるフィルタリング、セル単位のフィルター要約、距離ブーストに対応します。
  • 任意のコンパクトな地理検索結果カプセル、および複数解像度のファセット・セルインデックス。1 回の範囲リクエストでの地図結果取得と、カテゴリを考慮した空間ルーティングに対応し、任意の名前やブランド向けのワイルドカード占有レーンも備えます。
  • エンコードされたポリライン、座標配列、または GeoJSON を対象にした汎用ルートコリドー検索:正確なポイント・ルート間フィルタリング、複数解像度のコリドーセル、進捗/方向/表示範囲によるランキング、各結果の再合流地点に対応します。
  • 静的ルーティングと旅程計画(rangefind/route):OSM 道路網から構築した CRP/MLD ルートグラフを、範囲アドレス指定可能なセルおよびオーバーレイオブジェクトとして格納します。形状と名前付きステップを含む地点間の自動車ルート、移動時間行列、複数地点の旅程最適化に対応し、完全なグラフを使った Dijkstra と完全一致する結果を、クエリごとの取得セットを上限付きにして静的ファイルから提供します。シャード分割ビルドでは、共通のトップオーバーレイを共有し、モノリシックビルドと同じルートを返します。docs/route-graph.md を参照してください。
  • 範囲アドレス指定可能な権威インデックス内での、入力中のオートコンプリート検索:発音区別符号を除去した前方一致およびラベル途中のトークン照合、人気度またはカスタム重み、シャードごとの重みの証明を伴う正確な best-first top-k、事前計算済みのホットリストに対応し、2 つ目のサジェスションパック群やコーパス規模のヒープマップを追加せず、最初のキー入力を小さなフェッチ 1 回で処理します。
  • 範囲アドレス指定可能な IVF ベクトルインデックスによるハイブリッドセマンティック検索:int8 量子化埋め込み、粗い次元の候補ページと固定幅の全次元リファインストア、コサイン top-k、テキストレーンとの逆順位融合に対応します。サーバーもベクトルデータベースも不要です。
  • 検索結果のスニペットとハイライト範囲は、アナライザーの設定と一致します。
  • 多言語解析(analysis 設定):ドキュメントごとの言語検出または明示的な言語フィールド、20 以上の言語向けの軽量な言語別ステマーとストップワード、スクリプトを考慮した Unicode 正規化(ラテン文字、ギリシャ文字、キリル文字、アラビア文字、ヘブライ文字、デーヴァナーガリー文字)、Node とすべてのブラウザーで決定性のある辞書不要の CJK バイグラムトークン化、インデックス内の証拠に基づく基本プラン選択を伴う言語横断クエリ拡張に対応します。プロファイル全体をマニフェストに固定するため、クエリ解析は常にインデックスと一致します。
  • 増分公開: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 事前値をソート可能な数値 doc-value に組み込みます。リンクの多いページはランキングの僅差で優先されます(オプトアウト可能な乗算ブースト score *= 1 + boost*linkRank を、スコアリングのホットループを変更せず、上限付きのウィンドウ再ランキングとして適用します)。権威性によるソートも可能で、クエリ時のグラフ走査は一切ありません。独自のエッジ情報を持つコーパスであれば、すぐに使える examples/link-graph-enrich.mjs エンリッチャー(またはエクスポートされた rangefind/link-graph ヘルパー)で有効化できます。docs/link-graph.md を参照してください。
  • 実行可能な小さなサンプル。

このプロジェクトの目的

多くのブラウザー検索ライブラリは、ブラウザーがインデックス全体をダウンロードすることを前提としているか、小規模なサイト向けに最適化されています。Rangefind は、次のような要件がある場合に役立ちます。

  • バックエンドサービスを使わない
  • ホスト型の検索プロバイダーを使わない
  • 静的ホスティングに対応する
  • 検索品質を測定できる
  • 論理シャードが多くてもリクエスト数を抑える
  • top-k 検索を前提に設計されたインデックス形式を使う

クイックスタート

デモ:https://rangefind.dev/ — ローリング方式の公開シャードインデックスを利用した OpenStreetMap の地図検索(オートコンプリート、表示範囲の地理クエリ、最近傍検索)と、最小限のサンプルを、どちらも純粋な静的ファイルとして提供しています。

すでに生成済みの惑星規模の OSM インデックスは、アカウントや API キーなしで、ブラウザーまたは Node から直接無料でクエリできます。

import { createSearch } from "rangefind";

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

稼働状況とデータの鮮度は https://osm.rangefind.dev/ で確認できます。これはベストエフォートの公開サービスです。容量の保証、バージョンの固定、カスタムフィールド、または運用 SLA が必要な場合は、インデックスをセルフホストしてください。OSM の検索結果を表示する UI では、 © 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> は常に除外します。<header>/<footer> を除外するのは、フォールバックとして <body> 領域を使用する場合(<main>/<article> がない場合)のみです。そのため、記事内の投稿ヘッダーとその <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 コンポーネントです。HTML を 2 行追加するだけで、あらゆる静的サイトに、洗練されたアクセシブルな検索ボックス(WAI-ARIA コンボボックス、キーボード操作、即時検索結果、オートコンプリート)を追加できます。

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

これはライト DOMに描画され、独自のスタイルは一切含まれないため、すでに使用している CSS と組み合わせられます。スタイルは 3 通りの方法で設定できます。オプションのテーマスタイルシートを使う方法、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 コンポーネントはあらゆる環境で使用できます。rangefind:search、rangefind:select、rangefind:error イベントを発行し、プログラムから使用するための .classNames/.searchOptions JavaScript プロパティを受け取ります。npm run build:element でバンドルをビルドしてください。基本的な例(npm run build:example && npm run serve:example)を /component.html(テーマ)と /component-tailwind.html(Tailwind)で試せます。すべての属性、クラスフックの一覧、イベント、アクセシビリティに関する注意事項については、リファレンスガイドをご覧ください。

静的サイトジェネレーターアダプター

クローラーと検索コンポーネントは、どの静的サイトジェネレーターのビルドライフサイクルにも自然に組み込めます。アダプターがジェネレーターによる出力の書き込み直後にクロールを実行し、クライアント用アセットを配置するため、インストール後に手動で配線する必要はありません。

ジェネレーターパッケージ組み込み方法
Astrorangefind-astroastro:build:done で実行される実際のインテグレーションと、<RangefindSearch /> コンポーネントを提供します。
Eleventyeleventy-plugin-rangefindeleventy.after で実行される実際のプラグインと、汎用的な {% rangefindSearch %} ショートコードを提供します。
Docusaurusdocusaurus-plugin-rangefindpostBuild と injectHtmlTags で実行される実際のプラグインを提供します。ナビゲーションバーの項目または MDX に <rangefind-search> を追加できます。
Hugointegrations/hugoHugo にはプラグインローダーがないため、hugo && rangefind build public のレシピと、コピーして使えるパーシャルを提供します。
MkDocsmkdocs-rangefind(PyPI)Node CLI をシェル経由で実行し、すべてのページにウィジェットを自動挿入する、実際の Python プラグイン(on_post_build / on_post_page)です。

Astro、Eleventy、Docusaurus 向けのものは、それぞれ独立してインストールできる実際のパッケージとして packages/ の下にあります(対象のジェネレーター以外の依存関係なしでビルドできます)。Hugo と MkDocs にはそれぞれ独自の npm ベースのプラグインシステムがないため、integrations/ の下にあります。どれも実際のツールを使って、エンドツーエンドで検証済みです。実際の 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 を使用してください。フランス語版 Wikipedia のダンプ全体をビルドするには npm run build:wiki-site:fr:full を使用します。生成されたサイトは examples/wiki-search/public/ に置かれ、すべての Rangefind インデックスと同じ静的ホスティング要件があります。ホストは .bin.gz ファイルへの HTTP Range リクエストをサポートしている必要があります。

フランス語版 Wikipedia のスケーラビリティフィクスチャ

Rangefind には、Wikimedia の公式記事ダンプをストリーミングし、静的サイトをビルドして、クエリのレイテンシー、リクエスト数、転送サイズをベンチマークする、再現可能なフランス語版 Wikipedia のフィクスチャが含まれています。

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 を正確な検索経路と照合して検証し、複数の 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" は、別個の typo サイドカーを構築する代わりに、通常の用語彙をタイプミス候補に使用します。訂正を無効にするには、typoMode: "off" を使用します。

authority フィールドは、タイトル、エンティティ名、製品名、スラッグ、別名などの正規ラベル向けに、パック形式のレキシコンを構築します。ランタイムはまず発音区別符号を保持した表記の完全一致キーを試し、必要な場合にのみ、折りたたみ後の完全一致キーとトークンキーにフォールバックします。これにより、すべてのラベル処理を 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);

フィルターとソートには、範囲アドレス方式の doc-value 列を使用します。

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 固定精度)に、ドキュメントごとに 1 点をインデックス化します。

{
  "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、...)が報告されます。OpenStreetMap 規模の例とベンチマークについては、examples/osm-geo/ を参照してください。

重み付けに対応し、ラベル途中からも検索できる入力補完候補には、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(たとえば、人口や重要度)を使用し、指定がない場合は、直接の前方一致の優先度と人気度(同じ表記を持つドキュメントの数)を使用します。キー入力 1 回あたりのコストは、最大でも少数の小さな範囲リクエストです。セッション中の繰り返しのキー入力は、通常すべてキャッシュから提供されます。

候補には、各表記に対応する最適なドキュメント(doc/docs)も含まれます。また、hydrate: true を指定すると、それらを実際の検索ヒットに解決します。検索ヒットには search() が返すものと同じ表示ペイロードが含まれるため、ドロップダウンに結果カードを表示でき、選択した候補はテキストをクエリとして再検索しなくても即座に描画できます(候補テキストを再検索する場合の 215 KB、32 ms に対し、測定値は 0.5 KB、4 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

ユニットテストでは、アナライザーの正規化、バイナリーの可変長整数/固定幅コーデック、設定の解決、シャード/範囲の計画、用語/コードのバイナリーラウンドトリップ、ローカル HTTP Range サーバーに対するビルドとブラウザーランタイムクエリーのエンドツーエンドテストを網羅しています。 エンドツーエンドのビルドテストでは、マニフェストに出力されるビルドテレメトリーと、ファイルベースのスプールカウンターも検証します。

ベンチマークスクリプトは依存関係がなく、サンプルの静的サイトに対して実行されます。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

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。