When thinking about search, I always think server side first. Maybe because I’ve been a server side developer most of my life. Or maybe because I’m just old and I still don’t grasp the idea of how many things can be delegated to the browser.
Anyhow, building this product which creates a web app for fitness coaches, when I had to add a search feature, my first reaction was to add an ElasticSearch server (through AWS), and index every item on change using DynamoDB’s streams. It worked perfectly.
But then I thought: how am I going to make this work offline? If the aim is to build a web app, my end goal for it is to work fully offline. And there’s also the costs issue, that I don’t want to have fixed monthly expenses. And that led to the question: do I really need to do this server side?
I started googling about JS search engines that worked in the browser. My requirement was not only that it needs to search, but that it should also provide fussy search right out of the box. And be easy to implement.
Looking at different libraries I finally chose fuse.js. It offered fussy search by default, it seemed easy to use, had enough configuration options and seems to still be maintained.
Since I’m using redux, the idea was simple. Every time an item gets added to the local state, it will be indexed, and every time an item gets removed, just remove it from the index.
Does it fit the product?
Having the right library was only the technical part of the decision. The other part was of a strategic nature: in order to do client side search, it meant that data needs to be on the client. And this is when thinking about the product and which technologies fit it most comes to play. In my case, since I want to end up having everything offline, I will end up downloading everything to the client at some point or another. Also since I’m using DynamoDB, I already have an index that actually is a subset of the information that I use for listing elements, and is this subset that is enough for searches (an item’s name and some keywords).
Because of the nature of this web app, I thought of how may items in the end an app may contain. Let’s say it may have 20 plans, 200 workouts, 500 exercises. Each item data, consisting only of the name and some keywords, would be in average at 50 characters/bytes (something like “push up” or “12 weeks program for beginners” plus keywords like “advanced”, “beginner” and so on). This would be like 35k of bytes. The size of an image. Let’s make it 100k. Still something that can be downloaded nowadays easily. And this would be for a full loaded app, which most aren’t.
Making the transition
And that’s how I decided to remove the ES instance and move to a client side search, downloading everything at the web app startup. Since it’s not a website that needs to be ready for the user right away, even a couple of seconds loading time is fine, and it improves the whole experience since after the first load the user can just search and list and switch between screens much faster. Still the full item gets downloaded on-demand when accessing the item’s view page.
The transition was painless, it took my around 4 hours and it’s working really well. The app feels even more like an app than before, I made sure not having fixed or escalating costs, learned something new and made my whole architecture simpler.
I still need to add some caching strategy to the data, so not every time an app is accessed the data is downloaded. Once every X hours or even once a day would be enough, but that’s for some other time.