Skip to main content

My music database in Obsidian

Obsidian is usually pitched as a note-taking app. With the Dataview plugin it turns into something more interesting: a small relational database made entirely of markdown files. I've been using it to track every live gig and festival I've been to, and it's now one of my favourite things in the vault.

How it started

The first version wasn't a database at all. It was a single evolving note: a bullet list of artists as wikilinks, loosely grouped by year, with inline notes when I had something to say. It worked for a while. It broke down the moment I started seeing artists more than once. A single [[Levellers]] bullet couldn't tell me whether I'd seen them at Beautiful Days 2023, Beautiful Days 2024, or as a support act at a standalone gig. I started adding clarifying notes in brackets: "(support for X)", "(second time)", "(headliner this year)". The note became harder to scan and impossible to query.

The turning point was realising I didn't have one axis of data. I had three: artists, events, and festival series. Once I modelled each as its own note, the rest was easy.

I designed the schema myself but handed the conversion work to Claude Code. It parsed the old bullet list, pulled out the clarifying notes in brackets, and generated an artist note and event note for each entry. A job that would have taken me a full evening of copy-paste was done in minutes, and I could spend the time on the Dataview queries instead.

The shape of the vault

Everything lives under a single Gigs/ folder, split into three kinds of note:

Gigs/
  Overview.md
  Artists/
    Levellers.md
    3 Daft Monkeys.md
    ...
  Events/
    Festivals/
      Beautiful Days 2024.md
      WOMAD 2023.md
    Gigs/
      Some Standalone Gig.md
  Festivals/
    Beautiful Days.md
    WOMAD.md
    ...

Three note types, each with a type field in the frontmatter: artist, event, and festival_series. Everything else is built on top of those.

Artist notes

An artist note is minimal. Frontmatter with optional rating and a "would see again" flag, then a Dataview block that pulls in every event I've seen them at:

---
type: artist
rating:
would_see_again:
---
# Levellers

## Seen live

```dataview
TABLE year as "Year", location as "Location"
FROM "Gigs/Events/Festivals" OR "Gigs/Events/Gigs"
WHERE contains(artists, this.file.link)
SORT year DESC
```

The contains(artists, this.file.link) is the magic bit. Every event note lists artists as wikilinks, and Dataview resolves them, so each artist note shows every time I saw them without me maintaining anything.

Event notes

An event note is where the data actually lives. Frontmatter links the event to its festival series and to every artist seen:

---
type: event
year: 2024
date:
location:
festival: "[[Beautiful Days]]"
artists:
  - "[[Dub Pistols]]"
  - "[[Levellers]]"
  - "[[New Model Army]]"
  - "[[3 Daft Monkeys]]"
  - "[[The Damned]]"
---

Because artists are wikilinks, Obsidian creates the reverse connection for free. Visit any artist note and you see every event they appeared at. Visit any festival series and you see every year I've been. It's a three-way join done by a file system.

Festival series notes

A festival series is a parent note for the recurring festival. It does almost nothing on its own. It's there so event notes have something to link to, and so the overview can count those links.

The overview page

Overview.md is the page I actually open. It's a dashboard of Dataview queries:

  • Most seen artists: flatten all event artists, group by artist, sort by count.
  • Recent performances: events sorted by date.
  • Want to see again: artists where would_see_again = true, sorted by rating.
  • Events per year: group by year, count rows.
  • Venues and locations: group by location, count rows.
  • Timeline per year: one section per year with the events from that year.

A sample query, for most seen artists:

```dataview
TABLE WITHOUT ID
  artist as "Artist",
  length(rows) as "Times Seen"
FROM "Gigs/Events/Festivals" OR "Gigs/Events/Gigs"
FLATTEN artists as artist
GROUP BY artist
SORT length(rows) DESC, artist ASC
LIMIT 20
```

FLATTEN turns a list field into one row per item, which is what a group-by query over a many-to-many relationship needs.

Why I like this

  1. It's just markdown. No lock-in. If I ever move off Obsidian the data is all there in plain files.
  2. The schema is optional. I can add a new field to one event note and Dataview will pick it up. No migrations, no table alters.
  3. The writing and the data are the same thing. I can add a paragraph of notes about a gig right next to the structured metadata. Traditional databases force those apart.
  4. Reverse links are free. Every wikilink becomes a backlink automatically. That's the bit that makes the whole thing feel relational without me doing any relational work.

What I would change

If I were starting over I'd be more disciplined about the date field on events. Some have a full date, some only a year, and my sort queries have to use choice() to paper over it. Not a problem, but worth fixing at the source.