| name | ovq |
| description | Query Obsidian vault files by frontmatter properties using Dataview-style syntax, AND discover what property names and values actually exist in the vault. Use when the user asks to find notes, search the vault, or filter notes by metadata (tags, status, dates, categories, projects). ALSO use proactively before writing or editing a vault note when the canonical schema is not 100% certain: run `ovq --values <field> --count` to discover real category and property values, and run a `categories contains` query plus read 1-2 sample files to confirm property names before assuming them. Triggers include "find notes where", "search vault for", "which notes have", "find meetings", "find projects with status", and any agent task that touches Obsidian frontmatter on a vault you do not own. |
ovq - Obsidian Vault Query
Query markdown files by YAML frontmatter properties using Dataview-style syntax.
Quick Start
ovq --vault "/path/to/vault" 'status = "active"'
Set OVQ_VAULT environment variable to skip --vault flag.
Discovery: what actually exists in the vault
Before writing queries (or new notes) on an unfamiliar vault, use the value-listing modes to learn the canonical property names and value enums. This is the single most useful agent move when you are about to touch frontmatter you did not write yourself.
ovq --values categories --count
ovq --values status --count
ovq --values tags --count
ovq --values genre --count
Use this to:
- Find the real spelling of a category (
Project Notes vs Projects, TV Shows vs Shows).
- Discover the value enum for a property (what actually fills
status? watched, backlog, wishlist, ...).
- Spot drift and typos (one note with
status: watche next to 50 notes with watched).
- Decide whether a property is load-bearing at all (zero non-empty values = nobody uses it).
After you know the universe, write the query.
Query Syntax
Comparison Operators
ovq 'status = "active"'
ovq 'priority > 2'
ovq 'created >= 2024-01-01'
ovq 'done = true'
Operators: =, !=, >, <, >=, <=
Contains (Arrays and Substrings)
ovq 'tags contains "project"'
ovq 'categories contains "Meetings"'
ovq 'title contains "sync"'
Existence Checks
ovq 'due'
ovq '!due'
ovq 'due != null'
ovq 'due = null'
Boolean Logic
ovq 'status = "active" AND priority > 2'
ovq 'status = "done" OR status = "archived"'
ovq '(type = "note" OR type = "doc") AND published = true'
Value Types
- Strings:
"quoted"
- Numbers:
42, 3.14
- Booleans:
true, false
- Dates:
2024-01-15
- Null:
null
Common Patterns
Find by Category
ovq 'categories contains "Meetings"'
ovq 'categories contains "Projects"'
ovq 'categories contains "Project Notes"'
Find by Tag
ovq 'tags contains "work"'
ovq 'tags contains "urgent" AND status != "done"'
Find by Project
ovq 'project = "ProjectName"'
ovq 'project contains "Graph"'
Combined Queries
ovq 'categories contains "Meetings" AND project = "ProjectName"'
ovq 'status = "active" AND due'
ovq 'created >= 2024-01-01'
ovq 'categories contains "Meetings" AND !date'
Workflow
When the property names and value enums are already known:
- Use ovq to find matching files
- Read relevant files to get content
- Process or summarize as needed
When you do not know the schema yet (new vault, new property, drafting a note in a category you have not touched before):
- Discover first.
ovq --values <field> --count to see what values exist. For a category-keyed property, also run ovq 'categories contains "<Category>"' and read 1-2 of the returned files to see the real frontmatter shape.
- Then proceed with steps 1-3 above.
Skipping step 0 leads to inventing property names that no .base view actually filters on, which silently drops the note from the user's existing dashboards.
Matching Behavior
- Field names: case-insensitive (
Status matches status)
- String values: case-insensitive
- Obsidian links:
[[Link]] normalized to Link for comparison