Replacing complicated hashmaps with SQLite

I found myself in a situation of requiring multiple lookup keys for the same hashmap, along with other relational complications for a Go CLI application. Unexpectedly, it all becomes easier in SQLite.

In a recent project, I was working on a command line application with tricky data manipulation scenario. Imagine if you were moving members from mailing lists (e.g. Google Groups) to group chat service (e.g. Slack), where the goal is for members of the mailing list will find a new home in group chat channels.

It is perfectly sensible for engineers with hacker spirit to say “script this out and run it for each of the groups” and I agree — surely it’s a thing that a few HTTP calls and hashmaps can solve?

Turns out, this situation comes with some curve balls:

Now that doesn’t look so simple anymore, does it? As I was thinking about it, there is a lingering thought at the back of my head: “If I could use WHERE on any field and JOIN several entities, the new memberships could be solved in a single SQL query.”

And then it hit me — maybe I really could after all. So I did exactly that.

Much of what I had originally implemented is now much simpler, without having to worry about passing around states in every function. Initial implementation has my function signatures looking like this:

// MatchUsers performs best effort matching of users, returning:
// - map[string]User of email with their User counterpart
// - []string of emails without User counterpart
// - error, if any
func MatchUsers(
  ctx context.Context,
  emailsToMatch []string,
  existingUsers map[string]User,
) (map[string]User, []string, error)

Pretty much all functions require the states to be passed around that creates noise and bug-prone (e.g. what if a function unintentionally modified the hashmap?).

With SQLite as state management, the same method now look like this:

// MatchUsers performs best effort matching of users by comparing emails.
// Users without any match will have `slack_user_id` as NULL.
func MatchUsers(ctx context.Context, conn *sql.DB) error

And thus when I want to lookup the list of users which did not find any matches, it ends up being an intuitive flow:

SELECT * FROM mailing_list_users WHERE slack_user_id IS NULL;

If you find yourself passing around hashmap, reindexing the map, writing too many conditions to settle state management, and thought that it would be trivial in SQL: consider using ephemeral SQLite instance!

Miscellaneous interesting bits from the project:

Thanks for reading. More like this?

#sqlite #sql #golang

© 2023-2025 Wilson Husin

This site was last updated 2025-05-17T20:42:58-0700.

:wq