Query API
One search() call covers text, filters, facets, sorting, pagination, geo, vectors, and hybrid — with TypeScript types included.
import { createSearch } from "rangefind";
const rf = await createSearch({ baseUrl: "/rangefind/" });
createSearch fetches the manifest and returns an engine. The same call
handles single, generational, and
sharded indexes. TypeScript declarations ship with
the package.
search(params)
const res = await rf.search({
q: "harbor lights",
page: 1,
size: 10,
facets: ["topic"],
filters: {
facets: { topic: ["ports"] },
numbers: { year: { min: 1990 } }
},
sort: "-year",
highlight: true
});
| Parameter | Notes |
|---|---|
q |
query text; analyzed with the index's frozen profile |
page, size |
pagination (size ≤ 100) |
facets |
facet names to count for this query |
filters |
{ facets: {field: [values]}, numbers: {field: {min, max}}, booleans: {field: bool} } |
sort |
"field" / "-field" over a number or boolean field |
geo |
see geo queries |
shards |
sharded indexes: scope to shard ids or group labels |
vector, hybrid |
see vectors |
highlight |
analyzer-consistent snippets and match ranges |
The response:
{
total: 1284, // may be approximate under early termination
approximate: false,
results: [{
id, url, title, // your display fields
score, // BM25F, comparable across generations & shards
highlights, // when requested
distanceMeters, // geo queries
shard, generation // when federated
}],
facets: { topic: { exact: true, values: [{ value, count }] } },
correctedQuery, // when typo correction rescued the query
stats: { ... }
}
total under early termination is a fast lower bound flagged
approximate; use count() when you need the exact number.
suggest(params)
Search-as-you-type over the suggest configuration:
const { suggestions } = await rf.suggest({ q: "harb", size: 8 });
// [{ text: "Harbor Lights", weight: 12040, count: 3 }, ...]
Exact best-first top-k with per-shard weight proofs — the first keystroke costs one small fetch from a precomputed hot list.
count(params)
const { total, totalExact } = await rf.count({ q: "harbor" });
Exact totals for text queries without paying for result hydration.
Geo queries
// everything within 5 km, nearest first
await rf.search({ q: "café", geo: { near: { lat: 46.81, lon: -71.21, radiusMeters: 5000 }, sort: "distance" } });
// bounding box (a map viewport)
await rf.search({ q: "", geo: { box: { minLat: 46.7, maxLat: 46.9, minLon: -71.4, maxLon: -71.0 } } });
// keep relevance ranking, prefer nearby
await rf.search({ q: "pizza", geo: { near: { lat, lon, radiusMeters: 20000 }, boost: { weight: 2, pivotMeters: 2000 } } });
Nearest-neighbor sort is exact, with early-stop proofs — see Geo search for how few bytes that costs.
Vectors and hybrid
// pure ANN
await rf.vectorSearch({ vector: embedding, k: 10 });
// hybrid: BM25F text lane + vector lane, fused by reciprocal rank
await rf.search({ q: "cozy waterfront dinner", vector: embedding, hybrid: { rrfK: 60 } });
Bring your own embeddings (dims fixed at build time); similarities are
cosine over int8-quantized vectors with full-dimension refinement.
Typo correction
When first-page results are empty or weak, the runtime probes a bounded set
of vocabulary shards for close spellings and retries the strongest corrected
plans. Successful corrections surface as correctedQuery + corrections
so your UI can show "showing results for…". Tune or disable with
typoMode / typoTrigger in the build config.
Node
The identical engine runs server-side over local directories or remote indexes with disk caching — see Node runtime.