Posted on 2025-09-04

Obsidian Bases Tips and Tricks

After a weekend tinkering with the new Bases feature of Obsidian, I want to share some cool formulas I came up with, that will hopefully power up your bases as much as they did mine.

I'm assuming you already know:

If you're unsure about any of this, you can watch Nick Milo's video to catch up.

Filters, Properties, Sorting and Limits

A quick refresh on how to access the advanced functionality.

Filters

Obsidian Base. Clicking on "Filter" on the right of the top menu bar has revealed the filter pop-up. The options to open/collapse the sections "All views" and "This view" is highlighted. The filter in the first section is in visual edit mode, but the button to "Advanced filter" is highlighted. The other section has this mode activated.

A base has two sections for filters, one titled All views, the second one This view. These are applied sequentially: first the "All views" filter selects a subset of your notes, then the "This view" filter prunes this selection further. You'll want to make use of this. If you plan on having one view for notes on books, one for all notes on books you read in 2025, and one for all fiction books you ever read, then you'll want the "All views" filter to be the lowest common denominator, in this case "all book notes." The other views additionally filter down to "reviewed in 2025" and "fiction," respectively.

You can define your filters either using some help from the UI by selecting fields and functions from drop-down menus. You can also switch to the "advanced" mode by clicking the little </> icon. All my tips will use this mode.

Properties and Sorting

The properties pop-up allows you to select properties to show in your view. But it also allows you to define custom formulas via the Add formula button at the bottom. My tips will also make use of this feature.

To sort on a property, you have two options. If the property is currently being displayed as a column, you can click on the column heading to cycle sorting directions. The other option that's always available is to use the popup hidden behind the Sort button in the top menu bar.

Limits

Same Base. Clicking on "87 results" on the left of the top menu bar has revealed the limit pop-up. It has options to enter a number to limit results. It also has export options to csv or the clipboard.

Finally, if you ever want to limit the number of results (or export the results), you can bring up the corresponding menu by clicking on the XX results button next to the view selections.

Formulas

Now that that's all out of the way, we can almost dive into some nifty formulas. Just two general hints before that:

If you are converting Dataview tables into bases, you can use this nifty converter by Boninall. Depending on the complexity of your input tables, it might struggle, but it can still be a good starting point.

Second, if you find yourself reusing the same chunk of formula a lot of times in a base, you should create a formula and then use that instead. This makes it clearer what the formula is doing, makes refactoring easier, and keeps the downstream formulas shorter. You can do this by referencing formula["formula name"]. For example, instead of using these two formulas:

// This might be used as a filter in one view:
list(sources).map(value.contains("Book")).contains(false)
// This might be used as a filter in another view:
list(sources).map(value.contains("Book")).filter(true).length > 5

You'll want to refactor them like this:

// Property "is-book-sources"
list(sources).map(value.contains("Book"))
// Filter in one view:
formula["is-book-sources"].contains(false)
// Filter in other view:
formula["is-book-sources"].filter(true).length > 5

Okay, so here are the formulas that do some heavy lifting in my own bases.

File Is a Daily Note

/ø \d{4}-\d{2}-\d{2}/.matches(file.name)

This formula uses regex to match the naming format of my daily notes to return true or false.

My daily note names are formatted like this: ø YYYY-MM-DD.md, e.g., ø 2025-12-31.md. You'll need to adjust the part between the slashes to match your own format. If you don't know how to use regex, you can ask any LLM: they're actually quite good with that. But I also recommend learning the basics yourself, as it is quite powerful, and even just knowing a bit will help you write powerful formulas.

list(sources).map(value.toString().startsWith("[[") && value.toString().endsWith("]]")).contains(false)

This checks all values of the sources property and returns false if any of its values are not Obsidian links (i.e., wrapped in double square brackets).

Wrapping a property in the list() function ensures we can run .map() on it even if it was empty or formatted as a single-value property.

[list(director), list(actors)].flat().filter(
 !value.toString().startsWith("[[") ||
 !value.toString().endsWith("]]")
)

This returns a list of all non-link values in the director and actors properties.

We create a big list out of all values and apply our check as a filter. If we wanted to use this formula to help us filter notes that only contain these errors, we could alter its last line like so:

[list(director), list(actors)].flat().filter(
  !value.toString().startsWith("[[") ||
  !value.toString().endsWith("]]")
).length > 0

Incomplete String to Date

if(note["release-date"].contains('-'),
    date(note["release-date"]),
    date(note["release-date"]+'-01-01')))

This converts the value of the property release-date to a date, whether the review date was just a year (YYYY) or a full date (YYYY-MM-DD).

The date() function only takes full dates formatted as YYYY-MM-DD as its input. Here we naïvely assume all dates without a dash to be YYYY and add the month and day January 1st to make them valid.

Wrongly Formatted String to Date

date(note["last-seen"].slice(6,10)+'-'+note["last-seen"].slice(3,5)+'-'+note["last-seen"].slice(0,2))

This converts dates in the last-seen property from German date formatting (DD.MM.YYYY) to a value of type date.

As explained above, we have to arrive at YYYY-MM-DD as an argument for date(). Here, we accomplish this by assuming all dates to at least be in the correct German format and then slice-ing the relevant parts out and reordering them.

Latest Date in List

list(note["review-dates"]).filter(value.contains('ø')).map(
    if(value.slice(2).contains('-'),
     date(value.slice(2)),
     date(value.slice(2)+'-01-01')))
.sort()[-1]

This returns the latest date in note["review-dates"], taking into account only notes formatted like my daily notes or yearly notes.

This combines a lot of what we saw above, but adds some new things:

  1. Wrapping the property in a list() makes it more robust.
  2. We filter down to only those values that contain my date-marker "ø."
  3. We use the Incomplete String to Date formula to turn the value into a date.
  4. We sort the dates using .sort().
  5. Finally, we access the last element (the latest date) using [-1].

Interlude: this

In case you didn't see Nick talking about it, let me tell you about this. This variable is a reference to the note you have currently selected,1 unlike note, which references the note the base is looking at.

For example, if you want to troll yourself, you can add this filter to your base:

this.file.ext != 'base'

This will make your base always appear empty when you are right on it. Only when you have a different (non-.base) file focused will the base show what the other filters permit.

Trolling yourself isn't the best use, so let's look at some others.

file.hasLink(this.file)

This emulates backlinks functionality, returning true if the other file is linking to this.

Don't dismiss this filter based on its simplicity. In combination with other filters, it becomes incredibly powerful. We'll come back to that.

this.file.hasLink(file)

This returns true if this note is linking to the other.

You can dismiss this one. It is quite a bit less powerful than the core outlinks plugin, as it only collects existing links.

Daily Notes Of Weekly Note

date(file.name.slice(2)).format("WW") == this.file.name.slice(8)

This returns true for all daily notes that share a week with this weekly note.

This is meant to be used in conjunction with File Is a Daily Note and assumes my formatting for daily and weekly notes. The first part, date(file.name.slice(2)).format("WW"), slices off my prefix and returns the calendar week the day belongs to. That's compared to the last two characters that remain after slicing off the starting 8 characters of this.file.name, which should be the active weekly note.

Last Three Daily Notes

date(file.name.slice(2)) < date(this.file.name.slice(2)) && 
date(file.name.slice(2)) + '3 day' >= date(this.file.name.slice(2))

This returns true for all daily notes that lie 3 or fewer days in the past relative to this daily note.

The first line checks whether the other note is in the past relative to this one. The second checks that when we add 3 days to that other note, it is the same or a future date relative to this one.

file.links.filter(value.startsWith(this.file.name+"#")).length > 0

This returns true for all notes that link to a section in this note.

In case you didn't know, you can link to a section by including a # in your link like so: [[Link#Section]]. This filter finds only those links.

file.links.contains(this.file.name+"#")

This returns true for all notes that link to this note with an additional # at the end.

An especially nifty one I came up with. As we've seen above, you can use the # to link to a specific section. But you can also include it without following up with a section heading: [[Link#]]. This breaks absolutely nothing. Obsidian treats it as if the # wasn't there. But the above filter cares. This allows you to mark certain links as special without having to put them into a property.

This last one is a doozy! It's a powerful way to find those notes that have lots of outlinks that don't get returned by those notes. It's quite complex, so let's take it in stages.

Create a property formula called inlinks:

file.backlinks.map(value.asFile()).unique().filter(
 !value.path.contains("Sources/") &&
 value.path != file.path)

It looks at all links coming into the file. It makes sure we're not including duplicates, not including ourselves, and not including anything from the Sources/ folder.

Next, create a property formula called outlinks:

file.links.map(value.asFile()).filter(
 value.isTruthy() &&
 !value.path.contains("Sources/") &&
 value.path != file.path
).unique()

It looks at all links out of the file. It makes sure we're not including duplicates, links to non-existent files, links to ourselves, and not including anything from the Sources/ folder.

Next, create a property called unrequited:

formula.outlinks.filter(!formula.inlinks.contains(value))

This results in a list of all outlinks that are not also inlinks. Set this property to be visible in your view.

Finally, create this filter:

formula.unrequited.length > 5

You might need to additionally filter out your daily notes or similar, but the resulting view should show you those files that have more than 5 links to existing other notes that are not linking back. Sure, not every link-relationship needs to be a two-way street, but you'll probably find some opportunity in there!

Conclusion

I hope you learned a thing or two about Bases and were able to steal a formula or two. If you did, send this post along to your Obsidian buddies to help them out as well. To understand the bases functions more deeply or discover some I haven't touched, I can't recommend the official documentation highly enough. It was a great help to me. If you come up with a cool formula, let me know. You can find ways to contact me below.

  1. If you have the base open in a different window, a different tab, and a sidebar, all of them will reference the currently active tab as this. When you are embedding a base (by including ![[Some.base]] in your note), this instead refers to the note that includes the embedding.

Thoughts? Reach out on Mastodon @Optional@dice.camp, message me via SimpleX, or shoot me an email.