Full Text Search with IndexedDB

The problem: local chat, global expectations
It has been reported that while working on Borogove the author wanted full‑text search of locally stored chat history — the sort of search that finds every message containing all the words you typed, in any order. IndexedDB is the browser’s go‑to storage, but it’s low‑level and not built with full‑text search in mind. So what’s the simplest, performant way to get a solid search experience without shipping a server-side index? Sound familiar? Many web apps face the same itch: quick, local search that “just works.”
Start small: the table scan
The obvious first move is a table scan. If your dataset is small (under ~10k documents), scanning every message is surprisingly practical and keeps complexity down. The post walks through a compact example: a promisified cursor loop, tokenization that strips stopwords and non‑word characters, optional stemming, and a superset check to ensure a message contains all query terms. Simple. Straightforward. No ceremony. It may feel old‑school — like running grep on your chat — but it’s often the fastest way to ship something usable.
Scale up: multiEntry index
When messages scale into the hundreds of thousands or millions, a table scan won’t cut it. The author shows how to leverage IndexedDB’s ordered (B‑Tree) indexes with createIndex(..., { multiEntry: true }) on a per‑message terms array so each token gets its own index entry. This isn’t a full inverted‑index engine like Lucene or Elasticsearch, but it allegedly yields a big performance boost with modest added complexity: you must maintain a terms array (and think about stopwords and stemming) on write, but reads become far cheaper. Tradeoffs? Sure — storage cost, update complexity, and the limits of B‑Tree semantics — but for many browser apps this is a pragmatic middle ground.
Bottom line
If you’re building a web chat or any client‑side search feature, start with the simple table scan to prove the UX, then consider a multiEntry index once scale demands it. Practical, incremental engineering wins again — and you get local search without hauling a full search stack into the browser.
Sources: blog.jmp.chat, Lobsters
Comments