Earlier this year, Stream launched Winds 2.0, the second iteration of our popular open-source and native application for macOS, Windows, and Linux, which provides an entirely new way to consume RSS feeds and Podcasts. If you haven’t used Winds, you can sign up at https://getstream.io/winds. Or, if you just want a visual, below is a screenshot of Winds:
With all of the content that we are compiling and querying, we needed an intuitive way to surface content for users to search for. That’s where Algolia comes into the picture. Algolia is an easy to use drop-in that allows developers like ourselves to create unique search and discovery experiences for our users. Best of all, Algolia is lightning fast, configurable from the backend dashboard or frontend code, and the results returned to the user are highly relevant.
In this post, we’ll do a quick technical deep dive into how we handled out installation and configuration of Winds. If you want to follow along, the code for Winds is an open-source project and available on GitHub.
Getting Started 😇
Algolia has perfected their onboarding process for developers by understanding that traditional developers want to try out a product before making a long-term commitment to a paid plan. With that in mind, they support the community by offering a free tier for the open-source option; the only requirement is that you show the Algolia logo in your search bar (as shown in the photo below).
If you’re interested in using Algolia, get started with their free open-source plan which offers 10,000 records and 100,000 operations. Simply fill out the form here and the Algolia team will get back to you – they are pretty quick to respond!
Note: An account is not required for this post. We’re just going over the Winds integration and will not be talking about general installation on a project. However, this post could be used as a guideline for installing Algolia in your own application.
The Algolia Integration 👨💻
Integrating with Algolia is extremely straightforward. Because we’re using Node.js, we installed the JavaScript client (https://www.npmjs.com/package/algoliasearch)(provided by Algolia) (algoliasearch). And, to make things even easier, we created a helper file that we could easily pass search results (as objects) to:
import algolia from 'algoliasearch';
import config from '../../config';
import util from 'util';
import logger from '../../utils/logger';
if (config.algolia.appId && config.algolia.writeKey && config.algolia.index) {
const client = algolia(config.algolia.appId, config.algolia.writeKey);
const index = client.initIndex(config.algolia.index);
module.exports = async data => {
if (!data.type) {
throw new Error('Missing data.type key and value.');
}
await util.promisify(index.addObject.bind(index))(data);
};
module.exports.indexMany = async data => {
await util.promisify(index.addObjects.bind(index))(data);
};
} else {
module.exports = async () => {
logger.info('Faking search indexing');
};
module.exports.indexMany = function() {
logger.info('Faking search indexing');
};
}
The first portion of the if statement is for production or development environments, as it checks for valid Algolia keys and secrets. The latter half is used for faking requests to Algolia for testing purposes.
The following is called inside of one of our controllers when we need to add a single object:
await util.promisify(index.addObject.bind(index))(data);
To add multiple objects, Algolia supports a bulk insert using the following code:
await util.promisify(index.addObjects.bind(index))(data);
After everything is added, they show up in our index (see below)!
Fetching Search Results from Algolia 🔎
On the application side, there is a wide variety of ways you can search and browse data from Algolia. One example might be the JavaScript client algoliasearch; you can also opt to use a higher-level, component-based abstraction, like InstantSearch.js. One thing to note, regardless of your method, is that you should always attempt to fetch results from the client side, in order to reduce latency.
In our React application, we fetch the results and populate a drop-down menu with the following code:
import Algolia from 'algoliasearch';
const client = Algolia(`APP_ID`, `SEARCH_API_SECRET`); // your api credentials
const index = client.initIndex(INDEX_NAME); // your index name (e.g. dev_winds)
index.search({ query: text, }, (err, results) => {
if (err) {
console.log(err);
return;
}
this.setState({
results: results.hits,
});
});
What’s beautiful about integrating with Algolia is that you can build an interface for searching and browsing your data in a multitude of ways. Because all of the data that you need in order to display search results is already inside of our Algolia records, there is very little complexity at rendering-time, so we can avoid methods that might significantly decrease the speed or efficiency of other tools.
Performance with Algolia 🏃
As was mentioned previously, we’re fetching results from the client side. This is to avoid any possible latency that would be encountered if we were to pipe the results through the API. Search responses from Algolia should be in the low millisecond range, regardless of the query. This is achieved due to the infrastructure sitting behind Algolia. It’s a rather fascinating read, and if you’re like me, you’ll want to read this article which covers a speed test on Algolia vs Elasticsearch.
Here’s a screenshot showing just how fast Algolia’s search functionality is (in milliseconds) on a dataset with 70,000+ records:
Final Thoughts 🤔
Regardless of your search needs, we at Stream highly recommend using Algolia. It’s fast, easy to configure and built by passionate developers like you. In the end, I can confidently say that we had a lot of fun playing with and discussing all of the possibilities that would allow us to provide the ultimate user experience in search.
For more information on Winds, stay tuned to future blog posts about tech, and Winds, and follow me on Twitter at @nickparsons. If you think I missed something, feel free to drop a line in the comments below. Happy searching! 🎉
Top comments (0)