498 lines
13 KiB
Markdown
498 lines
13 KiB
Markdown
---
|
|
name: obsidian-bases
|
|
description: Create and edit Obsidian Bases (.base files) with views, filters, formulas, and summaries. Use when working with .base files, creating database-like views of notes, or when the user mentions Bases, table views, card views, filters, or formulas in Obsidian.
|
|
---
|
|
|
|
# Obsidian Bases Skill
|
|
|
|
## Workflow
|
|
|
|
1. **Create the file**: Create a `.base` file in the vault with valid YAML content
|
|
2. **Define scope**: Add `filters` to select which notes appear (by tag, folder, property, or date)
|
|
3. **Add formulas** (optional): Define computed properties in the `formulas` section
|
|
4. **Configure views**: Add one or more views (`table`, `cards`, `list`, or `map`) with `order` specifying which properties to display
|
|
5. **Validate**: Verify the file is valid YAML with no syntax errors. Check that all referenced properties and formulas exist. Common issues: unquoted strings containing special YAML characters, mismatched quotes in formula expressions, referencing `formula.X` without defining `X` in `formulas`
|
|
6. **Test in Obsidian**: Open the `.base` file in Obsidian to confirm the view renders correctly. If it shows a YAML error, check quoting rules below
|
|
|
|
## Schema
|
|
|
|
Base files use the `.base` extension and contain valid YAML.
|
|
|
|
```yaml
|
|
# Global filters apply to ALL views in the base
|
|
filters:
|
|
# Can be a single filter string
|
|
# OR a recursive filter object with and/or/not
|
|
and: []
|
|
or: []
|
|
not: []
|
|
|
|
# Define formula properties that can be used across all views
|
|
formulas:
|
|
formula_name: 'expression'
|
|
|
|
# Configure display names and settings for properties
|
|
properties:
|
|
property_name:
|
|
displayName: "Display Name"
|
|
formula.formula_name:
|
|
displayName: "Formula Display Name"
|
|
file.ext:
|
|
displayName: "Extension"
|
|
|
|
# Define custom summary formulas
|
|
summaries:
|
|
custom_summary_name: 'values.mean().round(3)'
|
|
|
|
# Define one or more views
|
|
views:
|
|
- type: table | cards | list | map
|
|
name: "View Name"
|
|
limit: 10 # Optional: limit results
|
|
groupBy: # Optional: group results
|
|
property: property_name
|
|
direction: ASC | DESC
|
|
filters: # View-specific filters
|
|
and: []
|
|
order: # Properties to display in order
|
|
- file.name
|
|
- property_name
|
|
- formula.formula_name
|
|
summaries: # Map properties to summary formulas
|
|
property_name: Average
|
|
```
|
|
|
|
## Filter Syntax
|
|
|
|
Filters narrow down results. They can be applied globally or per-view.
|
|
|
|
### Filter Structure
|
|
|
|
```yaml
|
|
# Single filter
|
|
filters: 'status == "done"'
|
|
|
|
# AND - all conditions must be true
|
|
filters:
|
|
and:
|
|
- 'status == "done"'
|
|
- 'priority > 3'
|
|
|
|
# OR - any condition can be true
|
|
filters:
|
|
or:
|
|
- 'file.hasTag("book")'
|
|
- 'file.hasTag("article")'
|
|
|
|
# NOT - exclude matching items
|
|
filters:
|
|
not:
|
|
- 'file.hasTag("archived")'
|
|
|
|
# Nested filters
|
|
filters:
|
|
or:
|
|
- file.hasTag("tag")
|
|
- and:
|
|
- file.hasTag("book")
|
|
- file.hasLink("Textbook")
|
|
- not:
|
|
- file.hasTag("book")
|
|
- file.inFolder("Required Reading")
|
|
```
|
|
|
|
### Filter Operators
|
|
|
|
| Operator | Description |
|
|
|----------|-------------|
|
|
| `==` | equals |
|
|
| `!=` | not equal |
|
|
| `>` | greater than |
|
|
| `<` | less than |
|
|
| `>=` | greater than or equal |
|
|
| `<=` | less than or equal |
|
|
| `&&` | logical and |
|
|
| `\|\|` | logical or |
|
|
| <code>!</code> | logical not |
|
|
|
|
## Properties
|
|
|
|
### Three Types of Properties
|
|
|
|
1. **Note properties** - From frontmatter: `note.author` or just `author`
|
|
2. **File properties** - File metadata: `file.name`, `file.mtime`, etc.
|
|
3. **Formula properties** - Computed values: `formula.my_formula`
|
|
|
|
### File Properties Reference
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `file.name` | String | File name |
|
|
| `file.basename` | String | File name without extension |
|
|
| `file.path` | String | Full path to file |
|
|
| `file.folder` | String | Parent folder path |
|
|
| `file.ext` | String | File extension |
|
|
| `file.size` | Number | File size in bytes |
|
|
| `file.ctime` | Date | Created time |
|
|
| `file.mtime` | Date | Modified time |
|
|
| `file.tags` | List | All tags in file |
|
|
| `file.links` | List | Internal links in file |
|
|
| `file.backlinks` | List | Files linking to this file |
|
|
| `file.embeds` | List | Embeds in the note |
|
|
| `file.properties` | Object | All frontmatter properties |
|
|
|
|
### The `this` Keyword
|
|
|
|
- In main content area: refers to the base file itself
|
|
- When embedded: refers to the embedding file
|
|
- In sidebar: refers to the active file in main content
|
|
|
|
## Formula Syntax
|
|
|
|
Formulas compute values from properties. Defined in the `formulas` section.
|
|
|
|
```yaml
|
|
formulas:
|
|
# Simple arithmetic
|
|
total: "price * quantity"
|
|
|
|
# Conditional logic
|
|
status_icon: 'if(done, "✅", "⏳")'
|
|
|
|
# String formatting
|
|
formatted_price: 'if(price, price.toFixed(2) + " dollars")'
|
|
|
|
# Date formatting
|
|
created: 'file.ctime.format("YYYY-MM-DD")'
|
|
|
|
# Calculate days since created (use .days for Duration)
|
|
days_old: '(now() - file.ctime).days'
|
|
|
|
# Calculate days until due date
|
|
days_until_due: 'if(due_date, (date(due_date) - today()).days, "")'
|
|
```
|
|
|
|
## Key Functions
|
|
|
|
Most commonly used functions. For the complete reference of all types (Date, String, Number, List, File, Link, Object, RegExp), see [FUNCTIONS_REFERENCE.md](references/FUNCTIONS_REFERENCE.md).
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `date()` | `date(string): date` | Parse string to date (`YYYY-MM-DD HH:mm:ss`) |
|
|
| `now()` | `now(): date` | Current date and time |
|
|
| `today()` | `today(): date` | Current date (time = 00:00:00) |
|
|
| `if()` | `if(condition, trueResult, falseResult?)` | Conditional |
|
|
| `duration()` | `duration(string): duration` | Parse duration string |
|
|
| `file()` | `file(path): file` | Get file object |
|
|
| `link()` | `link(path, display?): Link` | Create a link |
|
|
|
|
### Duration Type
|
|
|
|
When subtracting two dates, the result is a **Duration** type (not a number).
|
|
|
|
**Duration Fields:** `duration.days`, `duration.hours`, `duration.minutes`, `duration.seconds`, `duration.milliseconds`
|
|
|
|
**IMPORTANT:** Duration does NOT support `.round()`, `.floor()`, `.ceil()` directly. Access a numeric field first (like `.days`), then apply number functions.
|
|
|
|
```yaml
|
|
# CORRECT: Calculate days between dates
|
|
"(date(due_date) - today()).days" # Returns number of days
|
|
"(now() - file.ctime).days" # Days since created
|
|
"(date(due_date) - today()).days.round(0)" # Rounded days
|
|
|
|
# WRONG - will cause error:
|
|
# "((date(due) - today()) / 86400000).round(0)" # Duration doesn't support division then round
|
|
```
|
|
|
|
### Date Arithmetic
|
|
|
|
```yaml
|
|
# Duration units: y/year/years, M/month/months, d/day/days,
|
|
# w/week/weeks, h/hour/hours, m/minute/minutes, s/second/seconds
|
|
"now() + \"1 day\"" # Tomorrow
|
|
"today() + \"7d\"" # A week from today
|
|
"now() - file.ctime" # Returns Duration
|
|
"(now() - file.ctime).days" # Get days as number
|
|
```
|
|
|
|
## View Types
|
|
|
|
### Table View
|
|
|
|
```yaml
|
|
views:
|
|
- type: table
|
|
name: "My Table"
|
|
order:
|
|
- file.name
|
|
- status
|
|
- due_date
|
|
summaries:
|
|
price: Sum
|
|
count: Average
|
|
```
|
|
|
|
### Cards View
|
|
|
|
```yaml
|
|
views:
|
|
- type: cards
|
|
name: "Gallery"
|
|
order:
|
|
- file.name
|
|
- cover_image
|
|
- description
|
|
```
|
|
|
|
### List View
|
|
|
|
```yaml
|
|
views:
|
|
- type: list
|
|
name: "Simple List"
|
|
order:
|
|
- file.name
|
|
- status
|
|
```
|
|
|
|
### Map View
|
|
|
|
Requires latitude/longitude properties and the Maps community plugin.
|
|
|
|
```yaml
|
|
views:
|
|
- type: map
|
|
name: "Locations"
|
|
# Map-specific settings for lat/lng properties
|
|
```
|
|
|
|
## Default Summary Formulas
|
|
|
|
| Name | Input Type | Description |
|
|
|------|------------|-------------|
|
|
| `Average` | Number | Mathematical mean |
|
|
| `Min` | Number | Smallest number |
|
|
| `Max` | Number | Largest number |
|
|
| `Sum` | Number | Sum of all numbers |
|
|
| `Range` | Number | Max - Min |
|
|
| `Median` | Number | Mathematical median |
|
|
| `Stddev` | Number | Standard deviation |
|
|
| `Earliest` | Date | Earliest date |
|
|
| `Latest` | Date | Latest date |
|
|
| `Range` | Date | Latest - Earliest |
|
|
| `Checked` | Boolean | Count of true values |
|
|
| `Unchecked` | Boolean | Count of false values |
|
|
| `Empty` | Any | Count of empty values |
|
|
| `Filled` | Any | Count of non-empty values |
|
|
| `Unique` | Any | Count of unique values |
|
|
|
|
## Complete Examples
|
|
|
|
### Task Tracker Base
|
|
|
|
```yaml
|
|
filters:
|
|
and:
|
|
- file.hasTag("task")
|
|
- 'file.ext == "md"'
|
|
|
|
formulas:
|
|
days_until_due: 'if(due, (date(due) - today()).days, "")'
|
|
is_overdue: 'if(due, date(due) < today() && status != "done", false)'
|
|
priority_label: 'if(priority == 1, "🔴 High", if(priority == 2, "🟡 Medium", "🟢 Low"))'
|
|
|
|
properties:
|
|
status:
|
|
displayName: Status
|
|
formula.days_until_due:
|
|
displayName: "Days Until Due"
|
|
formula.priority_label:
|
|
displayName: Priority
|
|
|
|
views:
|
|
- type: table
|
|
name: "Active Tasks"
|
|
filters:
|
|
and:
|
|
- 'status != "done"'
|
|
order:
|
|
- file.name
|
|
- status
|
|
- formula.priority_label
|
|
- due
|
|
- formula.days_until_due
|
|
groupBy:
|
|
property: status
|
|
direction: ASC
|
|
summaries:
|
|
formula.days_until_due: Average
|
|
|
|
- type: table
|
|
name: "Completed"
|
|
filters:
|
|
and:
|
|
- 'status == "done"'
|
|
order:
|
|
- file.name
|
|
- completed_date
|
|
```
|
|
|
|
### Reading List Base
|
|
|
|
```yaml
|
|
filters:
|
|
or:
|
|
- file.hasTag("book")
|
|
- file.hasTag("article")
|
|
|
|
formulas:
|
|
reading_time: 'if(pages, (pages * 2).toString() + " min", "")'
|
|
status_icon: 'if(status == "reading", "📖", if(status == "done", "✅", "📚"))'
|
|
year_read: 'if(finished_date, date(finished_date).year, "")'
|
|
|
|
properties:
|
|
author:
|
|
displayName: Author
|
|
formula.status_icon:
|
|
displayName: ""
|
|
formula.reading_time:
|
|
displayName: "Est. Time"
|
|
|
|
views:
|
|
- type: cards
|
|
name: "Library"
|
|
order:
|
|
- cover
|
|
- file.name
|
|
- author
|
|
- formula.status_icon
|
|
filters:
|
|
not:
|
|
- 'status == "dropped"'
|
|
|
|
- type: table
|
|
name: "Reading List"
|
|
filters:
|
|
and:
|
|
- 'status == "to-read"'
|
|
order:
|
|
- file.name
|
|
- author
|
|
- pages
|
|
- formula.reading_time
|
|
```
|
|
|
|
### Daily Notes Index
|
|
|
|
```yaml
|
|
filters:
|
|
and:
|
|
- file.inFolder("Daily Notes")
|
|
- '/^\d{4}-\d{2}-\d{2}$/.matches(file.basename)'
|
|
|
|
formulas:
|
|
word_estimate: '(file.size / 5).round(0)'
|
|
day_of_week: 'date(file.basename).format("dddd")'
|
|
|
|
properties:
|
|
formula.day_of_week:
|
|
displayName: "Day"
|
|
formula.word_estimate:
|
|
displayName: "~Words"
|
|
|
|
views:
|
|
- type: table
|
|
name: "Recent Notes"
|
|
limit: 30
|
|
order:
|
|
- file.name
|
|
- formula.day_of_week
|
|
- formula.word_estimate
|
|
- file.mtime
|
|
```
|
|
|
|
## Embedding Bases
|
|
|
|
Embed in Markdown files:
|
|
|
|
```markdown
|
|
![[MyBase.base]]
|
|
|
|
<!-- Specific view -->
|
|
![[MyBase.base#View Name]]
|
|
```
|
|
|
|
## YAML Quoting Rules
|
|
|
|
- Use single quotes for formulas containing double quotes: `'if(done, "Yes", "No")'`
|
|
- Use double quotes for simple strings: `"My View Name"`
|
|
- Escape nested quotes properly in complex expressions
|
|
|
|
## Troubleshooting
|
|
|
|
### YAML Syntax Errors
|
|
|
|
**Unquoted special characters**: Strings containing `:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` `` must be quoted.
|
|
|
|
```yaml
|
|
# WRONG - colon in unquoted string
|
|
displayName: Status: Active
|
|
|
|
# CORRECT
|
|
displayName: "Status: Active"
|
|
```
|
|
|
|
**Mismatched quotes in formulas**: When a formula contains double quotes, wrap the entire formula in single quotes.
|
|
|
|
```yaml
|
|
# WRONG - double quotes inside double quotes
|
|
formulas:
|
|
label: "if(done, "Yes", "No")"
|
|
|
|
# CORRECT - single quotes wrapping double quotes
|
|
formulas:
|
|
label: 'if(done, "Yes", "No")'
|
|
```
|
|
|
|
### Common Formula Errors
|
|
|
|
**Duration math without field access**: Subtracting dates returns a Duration, not a number. Always access `.days`, `.hours`, etc.
|
|
|
|
```yaml
|
|
# WRONG - Duration is not a number
|
|
"(now() - file.ctime).round(0)"
|
|
|
|
# CORRECT - access .days first, then round
|
|
"(now() - file.ctime).days.round(0)"
|
|
```
|
|
|
|
**Missing null checks**: Properties may not exist on all notes. Use `if()` to guard.
|
|
|
|
```yaml
|
|
# WRONG - crashes if due_date is empty
|
|
"(date(due_date) - today()).days"
|
|
|
|
# CORRECT - guard with if()
|
|
'if(due_date, (date(due_date) - today()).days, "")'
|
|
```
|
|
|
|
**Referencing undefined formulas**: Ensure every `formula.X` in `order` or `properties` has a matching entry in `formulas`.
|
|
|
|
```yaml
|
|
# This will fail silently if 'total' is not defined in formulas
|
|
order:
|
|
- formula.total
|
|
|
|
# Fix: define it
|
|
formulas:
|
|
total: "price * quantity"
|
|
```
|
|
|
|
## References
|
|
|
|
- [Bases Syntax](https://help.obsidian.md/bases/syntax)
|
|
- [Functions](https://help.obsidian.md/bases/functions)
|
|
- [Views](https://help.obsidian.md/bases/views)
|
|
- [Formulas](https://help.obsidian.md/formulas)
|
|
- [Complete Functions Reference](references/FUNCTIONS_REFERENCE.md)
|