Skip to content

Searching loaded records

The optional @genome-spy/core/search entry matches terms against fields declared through encoding.search. It returns bounded, detached matches and their record-derived navigation targets. Lookup does not navigate, select, brush, fetch, or parse coordinate input. Coordinate-looking terms are ordinary strings.

import { createDatumSearch } from "@genome-spy/core/search";

const search = createDatumSearch(api.views);
const result = search.findDatums("TP53", { limit: 20 });
for (const match of result.matches) {
  console.log(match.view.selector, match.datum, match.targets);
}

Matching uses English search collation with base sensitivity, so case and accents are ignored. Exact matching is the default; mode: "prefix" matches the start of a stringified non-null field value. A row matching several search fields appears once, with those fields listed in match.fields.

view scopes lookup to a live unit or container handle. field restricts matching to that declared field; other views are excluded. An unknown field, a foreign or removed handle, or an explicitly scoped unit without search fields throws. Container scopes without searchable descendants return no matches.

limit is a required nonnegative safe integer and bounds rows returned, not rows examined. truncated means at least one additional match exists. Zero performs an existence check. Results follow hierarchy and collector order, without stable row identity across updates. Distinct transcripts, views, and loci remain distinct matches. facetId preserves a record's facet membership when present.

Targets and caller navigation

A match can have x and y targets on independent scales, or no supported target. Each target contains a public scale capability, channel, interval, zoomable, and kind: "locus" | "numeric". Conditional positions, missing numeric endpoint pairs, and invalid coordinates produce targetIssues rather than guessed targets. Locus points without an explicit secondary position represent one base. Reversed encoded endpoints are normalized; locus intervals are checked against the resolved assembly. Numeric intervals require explicit endpoints.

The caller chooses a match and target. Locus target intervals are continuous whole-genome half-open values, while existing locus zoomTo() takes an inclusive upper endpoint:

const target = selectedMatch.targets.find((candidate) => candidate.zoomable);
if (target) {
  const [begin, end] = target.interval;
  await target.scale.zoomTo([begin, target.kind === "locus" ? end - 1 : end]);
}

This legacy locus convention is retained for compatibility. Standardizing genomic interval APIs on half-open endpoints is tracked for v2.0 (#620).

Padding, animation, candidate ranking, and selection belong to the caller. target: "zoomable" filters out matches without a zoomable target before applying the row limit, so a non-navigable first match cannot hide a later one.

Loaded coverage, revisions, and suggestions

Every result includes sources: participating view handles, collector dataRevision, readiness, scan status, and rowsExamined. Pending views are reported and skipped. An early result bound leaves later sources unscanned; field restrictions mark other sources excluded. These statuses describe currently loaded transformed records, not complete lazy-source coverage. Execution is synchronous, and the row limit does not bound elapsed time.

Rows use the same structured-clone contract as data readback, without picking identifiers. Lookup never clones all scanned input rows.

const sources = search.listSearchableViews({ exampleLimit: 3 });
for (const source of sources) {
  console.log(source.view, source.fields, source.source.dataRevision);
}

Metadata refreshes against the current publication. Set exampleLimit: 0 to inspect handles, declared fields, readiness, and revisions without scanning rows. Examples are bounded suggestions, not a vocabulary index.

A typeahead can read complete bounded snapshots with view.data.read({scope: "all", limit}), extract symbols, and build its own sorted array or other index. Handle truncation and loaded-only coverage explicitly, and refresh when source revisions change. Sorting, normalization, prefix rules, deduplication, and ranking are consumer choices. On selection, call findDatums() in the original source/field scope to obtain current records and targets. No built-in index or typeahead UI is provided.

Search is independent of coordinate parsing. A combined input field decides which operation to invoke and whether to navigate. Cytoband parent resolution and provenance replay are separate features.

See the datum-search example.