Post contents
As part of our partnership with Orama, this blog post was sponsored and financially compensated for by Orama. This doesn't invalidate our learnings from this post, but if you'd like to read more, you can check our disclosure of our partnership with Orama to learn more.
On October 20th, 2023 we launched a major redesign. Not only did this redesign massively facelift all of the pages we had built previously:
Unicorn Utterances was our old name, which you can learn more about here.
But during this process we decided to flesh out some functionality we had previously. For example, while our old site had the concept of a "collection", they weren't exposed anywhere on our site; not on the homepage, not via search, etc.
In the new site, we have this banner of collections on the homepage and they show up in our searches (more on that later):
One of the features we embarked on improving was search.
Improving search UX
On the previous site, you could only make a search on the homepage. It was clunky, didn't have many of the features people wanted (like tags filtering), and wasn't scalable.
Yup, that was the entire search experience.
To solve this, we worked hard on a new search experience with a dedicated search page powered by Preact, React Aria, and TanStack Query. It featured:
- A brand new design
- Ability to sort by date
- Collections in search
- Filters on tags and authors
- Mobile view
And more.
Scalability Concerns
Earlier, we mentioned that the old search page wasn't scalable.
The reason it wasn't scalable was because we were using a JavaScript package fully on the client-side via Lunr.js. This meant that as we added articles to the site, the more JavaScript we had to download on each client-load to index all of the articles. We even did this if you didn't use search yet.
To solve this, we both:
- Migrated to a slightly heavier package with better search results (in our experience): Fuse.js
- Moved the searching to the server via a simple Vercel serverless function
This alliviated our scalability concerns, since we were only needing to ship the required information down to the search page based on the request.
We did this through a faily trivial pipeline:
- Store all of our content in markdown in our Git repository
- During the build of the static parts of the Playful Programming site, build a
searchIndex.json
file that acts as a database of our articles - Consume the
searchIndex.json
file via afs.readFile
method in our serverless function - Deploy the static file and serverless functions via Vercel
This worked well for an initial implementation, but to keep things simple for an MVP, we initially avoided:
- Sever-side pagination
- Server-side filtering
- Counting article results on the server
We knew we could fix this in a follow-up, but this would've required more work.
In addition, we weren't entirely happy with the search results. For example, searching "AST"
showed unrelated articles instead of Corbin's article of "How Computers Speak: Assembly to AST"
Similarly, any searches that include a more conversational tone, like "articles that explain how effects work in React"
would either return unrelated items or nothing at all:
Fixing Search Results
To solve these issues, we reached out to Orama. Corbin had worked with them previously with his work on the TanStack docs site and knew that they could solve the challenges we were facing.
While Orama has an incredibly powerful built-in UI:
We didn't want to give up on the custom UI we'd built.
Luckily, Orama provides a great JavaScript SDK that we could utilize for our needs: @oramacloud/client
.
To use it, we exposed our database of articles via a remote JSON that's deployed via CI/CD:
We point Orama at this JSON endpoint:
Orama will regularly check this remote JSON endpoint of ours to make sure that it's the most up-to-date data as needed.
Then, we initialize the Orama client like so:
const postClient = new OramaClient({ endpoint: ORAMA_POSTS_ENDPOINT, api_key: ORAMA_POSTS_API_KEY});
And can call this client with a simple search term, complete with pagination and more:
postClient.search( { term: "articles that explain how effects work in React", limit: 6, offset: 6 * pageIndex, },);
Once this was done in our codebase, our search results were immediately improved:
Why Orama?
Like any other decision-making tree, our decision to go with Orama extended beyond the developer experience wins we showcased before.
First, for Orama is perfect for open-source projects like ours. Not only do you get unlimited search queries on any plan; Orama is free for open-source and community projects, forever.
Don't take it from us! We asked Michele Riva, CTO of Orama, and this is what he had to say about community usage of their product:
@orama.com is free and unlimited for all open-source projects and non-profit communities, forever.
If you need full-text, vector, hybrid search, and unlimited GenAI sessions + analytics for free on your project, feel free to reach out!
Secondly, Orama handles generating embeddings for your project. This means that we didn't have to be machine learning (ML) experts to get up-and running. We only needed to provide our data and it handled everything else for us.
Third, Orama's vector searches allow us to maintain context through our search. This means that if someone searches something like "Green dress", it won't show search results for "Green salad dressing", but instead only show results for "Dressing in a green gown".
Finally, Orama not only provides vector search, but it manages "hybrid search features". This means that it's able to mix-n-match methods of vector search and conventional text search to provide the best results; regardless of the length of text.
After all, if I know that I want to search "React Side Effect"
, looking for a specific article, I don't want a vector database to override my request with something less relevant.
Takeaways
Ultimately, we're very happy with our decision to go with Orama for our semantic search experience. They provided incredible support, a good product, and our users are happier as a result.
Story Time
True story, after our first call with Orama, they had a proof-of-concept up and running against our API in 5 minutes
What do you think about our search page? We'd love to hear from you!
Let us know in our Discord; we hope to see you soon!