README file from
GithubSuper Smart Random Note
Open random notes matched by saved filters, written in (a subset of) Obsidian's own search query syntax.
- Default command — "Open random note (default)", always available. Query is empty by default, meaning "any note in the vault".
- Custom commands — define as many named filters as you want in the plugin settings. Each one becomes its own command in the command palette (bindable to a hotkey, launcher plugins, etc.).
Why a reimplementation instead of Obsidian's real search engine?
Obsidian doesn't expose its internal search engine through the public plugin API — there's no documented way to hand it a query string and get back a list of matching files. This plugin ships its own parser/evaluator that follows the same documented operators (see "Supported syntax" below), evaluated directly against each note's content (frontmatter included) plus its tags from the metadata cache. It is a close but not pixel-perfect clone of core search — see "Known differences" below.
Supported syntax
| Operator | Meaning | Example |
|---|---|---|
| (bare word) | Note content contains the word | meeting |
"phrase" |
Note content contains the exact phrase | "kickoff meeting" |
/regex/ |
Note content matches the regex (flags allowed, e.g. /foo/i) |
/\d{4}-\d{2}-\d{2}/ |
file: |
Match against the file name | file:.png |
path: |
Match against the full file path | path:"Journal/2026" |
content: |
Match against the full note content (same as bare word) | content:"happy cat" |
tag: |
Note has this tag (nested tags included, e.g. tag:#work matches #work/meeting) |
tag:#idea |
line: |
At least one line matches | line:(mix flour) |
section: |
At least one heading section matches (best-effort split on heading lines) | section:(dog cat) |
block: |
At least one paragraph/block matches (best-effort split on blank lines) | block:(dog cat) |
task: |
At least one task line (- [ ]/- [x]) matches |
task:call |
task-todo: |
At least one unchecked task line matches | task-todo:call |
task-done: |
At least one checked ([x]) task line matches |
task-done:call |
[key] |
Frontmatter property key exists and is truthy |
[status] |
[key:value] |
Frontmatter property key equals value (or contains it, for list properties). Wikilink-valued properties (- "[[Poems]]") match on the link's target/alias text, not the bracket syntax |
[status:Draft], [types:poems] |
[key:v1 OR v2] |
Property equals any of the given values | [status:Draft OR Published] |
OR |
Logical or (must be uppercase, space-separated) | meeting OR work |
- |
Exclude (prefix, no space before the term) | meeting -work |
| (space) | Implicit AND | meeting work |
(...) |
Grouping | meeting (work OR personal) |
field:(a b) |
Distributes the field over each grouped term | tag:(idea work) → has both #idea and #work |
Matching is always case-insensitive, except inside /regex/ literals (add
the i flag yourself if you want a case-insensitive regex).
Known differences from Obsidian's built-in search
- No "Match case" toggle — everything but explicit regex is case-insensitive.
section:andblock:use a simple heuristic (split on heading lines / blank lines) rather than Obsidian's real block/section parser.- No fuzzy matching — all text matching is plain substring (or regex).
- Bare words and
content:only look at note content, not the file name — usefile:/path:explicitly if you want to match on the name. - Reads every markdown file's content on each random-note request (via the cached vault read), so very large vaults may see a brief delay.
[key:value]properties are read with a small built-in YAML frontmatter parser (not Obsidian's metadataCache, which updates on a debounce and can briefly miss a property you just added) — it covers scalars, quoted strings, inline lists ([a, b]), and block lists (- item), but not full YAML (no nested maps, multi-line strings, anchors, etc.).- An unrecognized
word:immediately followed by a space (or the end of the query) — e.g.types: poems— is a query error, not a search: it looks exactly like an attempted property filter but silently splitting it into two unrelated bare-word terms (types:andpoems, found anywhere in the file independently) would return a plausible-looking but wrong count. Use[types:poems]for a real property filter, or quote it —"types: poems"— to search that literal text as a phrase.foo:bar(no space) still works as one literal token, e.g. for a URL.
Settings
- General — "Open notes in" sets the vault-wide default (same tab / new tab / new window) for every command.
- Commands — one row per command: the always-present "Default" row plus every custom filter you add. Click a row's name (or its gear icon) to edit its query and optionally override where that command opens notes; the search icon previews how many notes currently match; custom rows also get duplicate and delete icons. Type a name at the bottom and hit "Add command" to create a new filter, then edit its query from its row.
Development
npm install
npm run dev # watch build
npm run build # type-check + production build
npm run lint # eslint (typescript-eslint recommended-type-checked)
Copy main.js, manifest.json, and styles.css into
<vault>/.obsidian/plugins/super-smart-random-note/ to install manually, or add
the repo to BRAT.
The settings tab implements Obsidian's declarative getSettingDefinitions()
API (1.13.0+) with the old imperative display() kept as a fallback for
older versions — both paths share the same row-rendering code.
Prior art
The command-row-plus-modal editing UX is modeled after obsidian-advanced-random-note. obsidian-lottery was also a reference for the general idea of filtered random-note picking.