- created
- 2026-07-26
- updated
- 2026-10-30
Searching notes
Searching notes
track search looks through the vault three ways, chosen with --scope:
| Scope | What it matches |
|---|---|
title | Note titles and #tag filters, ranked by how closely the title matches. |
body | The full text of every note body, ranked by relevance. |
path | The note whose file the query names. |
all (default) | Title hits, then body hits not already found, then the named file. |
Back to track. The CLI page lists the surrounding commands.
Readable editor filenames
The opt-in editor view keeps the real note at note/<id>.md and creates a local symlink
named <safe-title>--<id>.md for editing. The suffix is intentional: titles can collide
after filename sanitization or truncation. Track's Telescope labels can still show just
the title while opening the full alias path.
Choose an absolute, machine-local directory outside the vault and outside OneDrive or
other synchronized folders. Set TRACK_EDITOR_VIEW in the environment used to start
Track and Neovim, then build the view:
export TRACK_EDITOR_VIEW="$HOME/.local/state/track-editor-view"
track editor-view
track editor-path --path /absolute/vault/note/1785024000000.md
track editor-path --path /absolute/vault/note/1785024000000.md --checkThe ordinary JSON path remains the canonical file; editor_path is the view path.
--check validates without creating an alias. Use the returned view root when configuring
a generic Telescope file picker, and ensure its finder includes file symlinks. Searching
the original vault still lists ID filenames. LSP reference pickers get their filenames
from the server's returned URIs, not from Telescope's working directory. An already
open canonical buffer keeps its existing URI to avoid opening a second editing owner;
close it before reopening through the view if you want the readable filename.
Names are pinned after creation, including across editor restarts: changing a title does not retitle or delete an existing alias. The filename may therefore show an older title. This initial mode has no automatic cleanup or retitling; do not delete the view while an editor holds its paths open. Keep using ordinary canonical paths if this tradeoff does not suit your workflow.
The Neovim integration checks alias identity around saves and requires link-preserving save settings. If an external formatter replaces a symlink with a regular file, Track reports a conflict and preserves that file rather than copying it over the master or deleting it. Do not use arbitrary atomic-replacement formatters against alias paths. Missing or changed targets must be resolved before saving; a stale alias is not a way to recreate a deleted note. The view is local generated state, never a second copy to sync.
This mode is available on Linux and macOS; Windows is rejected. Linux is the tested platform for this initial implementation, not a promise about every filesystem. The guard detects duplicate buffers within one Neovim process, not concurrent edits from other processes. Use one editing session per note. This is a Track/Neovim integration, not a guarantee for other language servers or editors. Each vault has a separate LSP server: cross-vault heading/block positions are read from disk, not another server’s unsaved text. Keep canonical editing as the fallback on untested filesystems. Body links are independent of filenames: see Linking notes for opt-in ID targets with title labels.
Title and tag search
A plain query matches titles with implicit-AND terms, an uppercase OR between
alternatives, and a #tag prefix to filter by tag:
track search --query "graph OR web"
track search --query "#zettel workspace"Finding a note by its file
A note's file is named from its id, so naming the file names the note:
track search --query "note/1785024006000.md"
track search --query "1785024006000.md"
track search --query "1785024006000"All three are the same lookup. The rule is exact — a whole id, with or without a
directory and a .md suffix — so a query that merely contains digits is still an
ordinary text search rather than a shower of near-misses.
This is here for the times a note arrives named as a file: a coding agent that has been reading the vault refers to notes the way the filesystem does, and pasting what it said should find the note rather than nothing.
Full-text body search
--scope body runs against a full-text index kept in the SQLite cache, so it
does not re-read every file on each query. Terms combine like they do for
titles: space-separated terms are an implicit AND — a note matches only when its
body contains all of them — and an uppercase OR separates alternatives, so
containerd OR podman matches either. Results are ranked by relevance (denser,
more focused matches first). Each hit still reports the first matching line and a
snippet, so a picker can jump straight to it:
track search --scope body --query "containerd runtime"
track search --scope body --query "containerd OR podman"{"results":[{"note_id":900,"title":"Deploy notes","line":3,"snippet":"We run containerd as the runtime."}]}The index stores the body verbatim, so text inside fenced code blocks is searchable too:
track search --scope body --query "nginx" # finds a note that only mentions nginx in a YAML blockBecause the body index is part of the same rebuildable cache as titles and links, an edit made outside track — a save from your editor, or a change pulled in by file sync — is reconciled automatically the next time you search. You never run a separate "rebuild search" step.
CJK and short queries
The body index tokenizes on character trigrams, so it matches substrings and works for scripts without spaces, such as Japanese or Chinese:
track search --scope body --query "テスト"A query term shorter than three characters — a two-letter word, or a two-character word like 世界 — cannot form a trigram. Those queries fall back to a direct file scan, so they keep working; only very short queries pay that cost, and the common case stays on the fast index.
In the workspace
The Web workspace's search box is the same search, unscoped: it shows the title matches first, then, under Full text, the notes whose body matches, each with the line it matched on, and last, under File name, the note the query named by its file. Typing is debounced, so a query runs when you pause rather than on every keystroke. The group captions only appear once there is more than one group — a search that only matches titles looks exactly as it always has.
A published static site searches bodies too. It has no server to ask, so it runs
the search in the browser over the bodies its bundle carries: the same grammar,
the same #tag filter, the same substring matching, the same snippet — it finds
the same notes. What it cannot do is rank by relevance, which lives in the index,
so its full-text group is ordered most recently updated first instead. That is
what the search above falls back to whenever a query cannot use the index either.
The bodies are fetched the first time you search, not when the page opens.
It has no File name group at all, and cannot: a published bundle carries no
source paths, because a note's file name is derived from its id and publishing it
would give back the id that a slug: is there to keep out of the URL.