Obsidian nested tag search caveats
I took a break from my vault for a while, but eventually came crawling back—I realized how much I missed it. And in updating my vault plugins and getting it set back up on devices I hadn’t yet installed it on, I ended up going back over some of my notes. In the process, I ran into a curious situation: What if I want to search for a tag, without returning nested ones?
This isn’t a new ask, and while I think this use-case goes against how tags are designed, Obsidian and Zettelkasten (if you adhere to it) are tools that should be customizable to the user. So, if you really want to do it, you should be able to.
Well then, how do you do it?
tag: operator and plain text search
The tag search operator tag: in Obsidian performs non-fuzzy, non-substring matches to complete tags, so tag:#hello/worl does not match the tag #hello/world. The operator will also automatically include all nested tags, so tag:#hello includes #hello/world.
If you want to exclude nested tags from such a search, tags are just text, so you could use text search to negate nested tags as they might appear in text:
tag:#hello -#hello/
But this has a major flaw: it will exclude notes that contain the non-tag text you excluded, so if the tag you’re excluding is, say, used as the anchor in a hyperlink, that note will be excluded too. So for the above example query, a note containing this content would not be returned in the search results:
#hello <-- found in the search
...
[hello](https://example.com/world#hello/) <-- causes exclusion
For those with generic tags, such as #todo or #work, this could be a real concern. But if you think unintentional matches will be rare, there should be no problem using this solution. Using emojis and other unique characters and phrases in your tags that would be rare to find in natural text or links will help as well.
Regular expressions (regex)
Some may suggest using regex, and skipping the tag: operator entirely, but that comes with its own difficulties:
- Regex is slow for large vaults
- Obsidian’s regex engine might be missing features depending on your platform
- Tags can be inserted in places in note text that confuse simple regexes
- Tags can be represented in frontmatter properties (
tags), meaning your regex should be able to also fully parse YAML values if you want to be particular
However, you will need regex searches if you want to match substrings of tags since there is no other native way, meaning you’re forced to deal with the above complications as they present themselves.
For example, if you want to search for all nested tags ending with usage, you might write something like this:
/(^|\s)#\S+\/usage(\/?$|[^\/\w-])/
This will match tags like #lotus-notes/usage and #kde-kontact/usage, but not #microsoft-office/usage/workarounds.
Or if you want to find tags containing the nested tag tasks in any position:
/(^|\s)#(|\S+\/)tasks(\/|$|[^\/\w-])/
This will match all of #car-repair/tasks, #home/tasks/done, and #tasks/urgent.
But these regexes aren’t perfect:
- To match the leftmost part of the nested tag, I used the inverted whitespace character class,
\S, which will match any character except ASCII whitespace and newline. This is quite broad, so text like#hello/world%/usagewould match the first example, despite only being made up of the tag#hello/world—the following/usageis plain text and not part of the tag, since the%separates them. - I used the word character class,
\w, to ensure the tag isn’t continued, and that we really are matching the end of the tag. In JavaScript-flavored Regular Expressions (JSRE), this is equivalent to[a-z0-9_], so the technically-valid tag#obsidian/usage→successwould also match the first example—it considers→to be the end of the tag.
There isn’t really a reasonable alternative to the above, since tags can contain what seems like any character except for a specific subset of punctuation. For example, all Japanese punctuation (e.g. the full stop “。”) do not end tags, while the French single guillemets (“‹›”) do, and the double guillemets (“«»”) don’t. 🤷
The only real solution is to dig into Obsidian and see how it works, or individually test characters… And in my individual character testing, this seems to be a decent list of punctuation that ends tags:
[^`~!@#$%‰^&*()=+{}[\];:'‘‚’"“„”‹›\\|.‥…,<>?\s]
Note the ] and \ are escaped.
Tags can be written different to how Obsidian sees them
Another issue with using regex or plain text searches is how you can write tags in your notes or, more realistically, how automated tools and plugins can add tags to your notes. This is an extreme example, but have a look at this:
