Bases Migration/Quick Start Guide

Base functions have been changed pretty heavily in Obsidian 1.9.2. This should allow for more streamlined formulas, and should be a bit more intuitive, but there have been a lot of breaking changes as a result. I’ve put together a small guide to help ease the transition. This can also serve as a quick start guide for people new to Bases.

Here are some common formulas I’ve seen and how to implement them in with the new syntax:

Filtering by tag

  • #tag in the body of a note or a Property:
    • New:
      • file.hasTag("cat")
      • file.hasTag("cat", "dog")
      • file.hasTag("cat", "dog","",...)
    • Old: taggedWith(file.file, "cat")
  • tag only in Properties
    • New: tags.contains("cat")
    • Old: contains(tags, "cat")
  • #tag inline, not in properties
    filters:
      and:
        - file.hasTag("cat")
        - if(tags.contains("cat"), false, true)

Links

  • General:
    • file.hasLink("path")
  • Notes that link to this note:
    • New file.hasLink(this)
    • Old: linksTo(file.file, this.file.name)
  • Property contains a link to this note:
    • New:
      • If your property is of type list: <property>.contains(this)
      • If your property is of type text: <property> == link(this)
    • Old: contains(<property>, concat("[[", this.file.name, "]]"))
    • Replace <property> with the name of your property, i.e. director.contains(this) , attendees.contains(this), etc.
      • If your property name has spaces or special characters, see note under Misc Notes
      • If you have properties where the value is not in a list element, and is instead on the same line as the property key, you may need to convert your property to a list first: list(<property>).contains(this)

Folders

  • Filtering by folder
    • New: file.inFolder("MyFolder")
    • Old: contains(file.path, "MyFolder")
  • List all notes in the same folder as this note:
    • New: file.inFolder(this.file.folder)
    • Old: contains(file.path, this.file.folder)

Created Today

  • Using ctime
    • New: file.ctime.date() == today()
    • Old: date(file.ctime) == date(now())
  • Using a custom property in “YYYY-MM-DD” format
    • New: datecreated == today() (datecreated being a date field)
    • Old: datecreated == date(now())

Modified Today

  • Using mtime
    • New: file.mtime.date() == today()
    • Old: date(file.mtime) == date(now())
  • Using a custom property in “YYYY-MM-DD” format
    • New: datemodified == today()
    • Old: datemodified == date(now())

Created in the past n days

  • Using ctime, with n = 7
    • New: file.ctime >= today() - "7d"
    • Old: date(file.ctime) >= date(dateModify(date(now), "-7d"))

Modified in the past n days

  • Using mtime, with n = 7
    • New: file.mtime >= today() - "7d"
    • Old: date(file.mtime) >= date(dateModify(date(now), "-7d"))

Query all in note with a specific property

  • All notes with a specific property which contains a value
    • myProperty or
    • note.myProperty
  • All notes that contain a property, but it doesn’t have a value:
    filters:
      and:
        - note.keys().contains("myProperty")
        - !myProperty || myProperty == null

Misc Notes

  • The not() function has been removed, we can simply use ! instead
    • New: !director.contains(link("Hayao Miyazaki"))
    • Old: not(contains(director, "[[Hayao Miyazaki]]"))
  • If your property contains spaces or characters such as dashes, you’ll need to encode the property name like so:
    • For My Property
      • note["My Property"]
    • For My-Property
      • note["My-Property"]
    • If you’re filling out a filter and select the property through the UI, this will be handled for you

New

There’s also some new functions that have been added. Here are some examples of how you can implement them.

link()

This lets you set the display text for a link, similar to link aliases. This lets us use the value of a property as a link: link(file, property)

Example:
I have notes on albums, the note names are in this format: “GOLLIWOG-by-billy-woods-2025”

I have the following properties:

title: GOLLIWOG
artists:
  - "[[billy woods]]"
year: 2025

I can create a Formula column with my formula set to link(file, title), and I’ll get a column that lists this note as “GOLLIWOG” instead of the full note title. I can hide my name column, and utilize this one instead to clean up my results.

Before:

After:

A more advanced use case for this function would be using it to create a link to a daily note using the note name:

  • Suppose I have a note: “2025-06-05 My Note”
  • I can add this formula in a column:
    • link(date(file.name.split(' ')[0]).format("YYYY-MM-DD"))
      • The exact implementation that will work for your vault will be dependent on your note name structure
  • That column will now have a link to the corresponding daily note for that date

This gives us a lot of flexibility with creating link to notes within our base results. We can take this particular example a step further by using the display text parameter:

  • link(date(file.name.split(' ')[0]).format("YYYY-MM-DD"), date(file.name.split(' ')[0]).format("dddd, MMM YYYY"))

Using this, you can change the date format that is displayed for the daily note link.

15 Likes

All my bases are broken. It seems !project.contains(link("MyProject")) does what you expectproject.contains(link("MyProject")) should do..

In other words, “does not contain” executes as “contains”.

In the filter did you accidentally select None of the following are true as a “type” of filter, by any chance ? :thinking:

Nope, it’s literally inversed. When I put ! (does not contain) it actually shows anything that DOES contain the given project.

What happens if you do !list(project.contains(link("MyProject")) ?

This works. Without the ! this time though. Is this supposed to be the way?

One thing I liked about the previous version of Bases is that I can just click buttons instead of writing queries to do “simple” things (like dataview).

If adding list() worked then your properties may have some inconsistencies. If it’s of type list, some of your notes might have

project: "MyProject" instead of

project:
    - "MyProject"

For the most part, doing this through the UI if your properties are well formatted would be as easy as selecting project contains and typing [[My Project]] into the value parameter. That’s all I can think might be causing you trouble.

All my “project” properties are of the text type:

project: "[[MyProject]]"

My initial “button click” query works fine, but the “contains” logic is just inversed.

In that case, you can do property is [[MyProject]]

Which becomes project == link("MyProject")

Same problem as other queries, to get the desired result (==) I need to use !=, so, to be clear:

project != link("MyProject") shows all files where project IS MyProject.

EDIT: The problem is:

My property value is actually an alias, which is pre-loaded in the selector.

So the query project == link("myVeryLongProject|ProjectA") (where the value is just selected from all populated options for the project property) does not work but project == link("myVeryLongProject") works. The actual content in source view is project: [[myVeryLongProject|ProjectA]] so it’s weird that this does not work as expected.

I was not able to make that work. My property is called “Espèce” so since there’s a special character I also tried note[“Espèce”].contains(this) but that did not work either.

This worked however:
Espèce == link(this)

2 Likes

I also use this expression, haven’t figured out the equivalent in the new syntax yet..

It’s listed just above