TechWorkRamblings

by Mike Kalvas

Adding a random note link to my site

A quick write-up of a fun little feature

Today I walk through adding a random note selector to my site. I’ll talk about why I did it and what the implementation looks like, but most importantly share something that brought me a bit of programming joy.

#blog #tech #ramblings

If you’ve come to my site before, you may have noticed that there’s a new icon in the nav — the two dice. I recently decided to implement a random note route that will (true to what it says on the tin) take you to a randomly selected note/post.

Why do I want to add a random note link? Great question, I’m glad you asked!

It’s well known that spaced-repetition is a good way to memorize things. Typically, I’m more interested in actually constructing lasting knowledge1 than memorizing facts, but it's also obviously impossible to say that you’ve really constructed a piece of lasting knowledge without being able to remember it. My Zettelkasten tool of choice is Obsidian and I use the random note function in that regularly to read back through notes that I wrote. This approximates a spaced repetition (obviously without the structure and specific goal) that keeps the various branches of my knowledge graph relatively fresh in my mind. I find that if I do this regularly, I don't actually need to re-read even a small portion of all the notes to keep things loaded up in the "cache" of my mind.

Another goal of keeping notes in a Zettelkasten format is to surprise us with the organic structures and connections that form when we hang another piece of knowledge in the graph. One of the drawbacks of having a large, long-term Zettelkasten that I've gardened over a long period of time is that — unsurprisingly! — there's a lot in it. It can be hard to hit on these moments of surprise without cultivating an environment where they can easily happen.

The second goal then, of the new random note button, is to surface moments of surprise more readily. If I click through some random notes in my Zettelkasten while I have projects, thoughts, or ideas on the back-burner of my mind, I often find that there's something in the graph that sparks a new way of thinking about the problem at hand.

Since Obsidian already has a random note feature built in and I use it so frequently for the reasons outlined above, I thought it was only natural to build it into my site too.

Let's take a look at how simple it was to implement.

Code

First I added a route to my application at /notes/random which invokes the random_note handler below. You can see that we do two basic actions: get a random note from the database with Note::random() and do a Redirect::temporary to the note we found. The rest is error handling and content security policy nonce handling.

pub async fn random_note(Extension(nonce): Extension<Nonce>) -> impl IntoResponse {
    let result = Note::random();
    match result {
        Ok(note) => Redirect::temporary(&format!("/notes/{}", note.id)).into_response(),
        Err(err) => {
            if let Some(Error::QueryReturnedNoRows) = err.downcast_ref() {
                NotFoundTemplate { nonce }.into_response()
            } else {
                ServerErrorTemplate { nonce }.into_response()
            }
        }
    }
}

Then in the model, we pull a random note from the database with the convenient ORDER BY RANDOM() clause. Going into this I had forgotten that there was a RANDOM function available to us and thought I was going to have to do some manual randomizing, but luckily that wasn't the case.

pub fn random() -> anyhow::Result<Note> {
    let conn = connect()?;
    let tbl = Note::table_name();
    let result = conn
        .prepare(&format!("SELECT * FROM {tbl} ORDER BY RANDOM() LIMIT 1"))?
        .query_row([], |row| Note::try_from(row))?;
    Ok(result)
}

That's it! Super easy.

I hope you find something interesting or surprising by clicking it a couple times!


  1. Linking to this note made me realize that I’d also like to implement showing backlinks on notes here on my site. Maybe that will be the next blog post!