AI-powered search: From keywords to conversations
By now, everyone’s had the opportunity to experiment with AI tools like ChatGPT or Midjourney and ponder their inner ...
Director, Product Marketing
By now, everyone’s had the opportunity to experiment with AI tools like ChatGPT or Midjourney and ponder their inner ...
Director, Product Marketing
Search has been around for a while, to the point that it is now considered a standard requirement in many ...
Senior Machine Learning Engineer
With the advent of artificial intelligence (AI) technologies enabling services such as Alexa, Google search, and self-driving cars, the ...
VP Corporate Marketing
It’s no secret that B2B (business-to-business) transactions have largely migrated online. According to Gartner, by 2025, 80 ...
Sr. SEO Web Digital Marketing Manager
Twice a year, B2B Online brings together industry leaders to discuss the trends affecting the B2B ecommerce industry. At the ...
Director of Product Marketing & Strategy
This is Part 2 of a series that dives into the transformational journey made by digital merchandising to drive positive ...
Benoit Reulier &
Reshma Iyer
Get ready for the ride: online shopping is about to be completely upended by AI. Over the past few years ...
Director, User Experience & UI Platform
Remember life before online shopping? When you had to actually leave the house for a brick-and-mortar store to ...
Search and Discovery writer
If you imagine pushing a virtual shopping cart down the aisles of an online store, or browsing items in an ...
Sr. SEO Web Digital Marketing Manager
Remember the world before the convenience of online commerce? Before the pandemic, before the proliferation of ecommerce sites, when the ...
Search and Discovery writer
Artificial intelligence (AI) is no longer just the stuff of scary futuristic movies; it’s recently burst into the headlines ...
Search and Discovery writer
Imagine you are the CTO of a company that has just undergone a massive decade long digital transformation. You’ve ...
CTO @Algolia
Did you know that the tiny search bar at the top of many ecommerce sites can offer an outsized return ...
Director, Digital Marketing
Artificial intelligence (AI) has quickly moved from hot topic to everyday life. Now, ecommerce businesses are beginning to clearly see ...
VP of Product
We couldn’t be more excited to announce the availability of our breakthrough product, Algolia NeuralSearch. The world has stepped ...
Chief Executive Officer and Board Member at Algolia
The ecommerce industry has experienced steady and reliable growth over the last 20 years (albeit interrupted briefly by a global ...
CTO @Algolia
As an ecommerce professional, you know the importance of providing a five-star search experience on your site or in ...
Sr. SEO Web Digital Marketing Manager
Hashing. Yep, you read that right. Not hashtags. Not golden, crisp-on-the-outside, melty-on-the-inside hash browns ...
Search and Discovery writer
Oct 19th 2021 engineering
If you’re going to send an email to your customer, it better be relevant. Product recommendations are a great way to create engaging emails for your customers to invite them back to your site. Algolia Recommend builds personalized product recommendations by training AI models on customer site analytics. In this blog you’ll see how to build a back-end service that crafts tailored recommendation emails and sends them to your customers using Twilio SendGrid.
For this demo, we’ll be building an engagement email service for an e-commerce site. We’ll explore recommendation strategies that become more tailored as we layer in more features and analytics based on customer interactions with our site.
The four customer scenarios are as follows:
We’ll rely on the following technologies:
We’ll build the service in four steps:
We’ll build our service as an express app using node. You can clone the solution repository to follow along.
git clone https://github.com/algolia-samples/email-recommendations
Our service follows the typical structure of an express application written in Node.js. The server code lives in server/node
:
├── README.md ├── emails │ ├── 1 │ │ ├── README.md │ │ └── email.js │ ├── 2 │ │ ├── README.md │ │ └── email.js │ ├── 3 │ │ ├── README.md │ │ └── email.js │ └── 4 │ ├── README.md │ └── email.js ├── package-lock.json ├── package.json ├── sendgrid.js ├── server.js └── templates ├── base.html ├── partials │ ├── products_3_columns.html │ └── products_full_width.html ├── post_order.html ├── pre_order.html └── re-engagement.html
The server.js
file has our routing, API initialization, and page rendering. The templates/
directory is for our Cerberus email templates. The actual contents of our four engagement emails live in the emails/
directory. We manage sending emails in sendgrid.js
.
Before we can continue, we need to install our dependencies from our package.json
.
cd server/node npm install
Now we’re ready to start configuring the various APIs our service relies on. We copy the file .env.example
from the root of this project to the directory of the server we want to use and rename it to .env
. For example, to use the Node implementation:
cp .env.example server/node/.env
Our .env
file has some keys that need we need to assign:
# Algolia credentials # https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl ALGOLIA_APP_ID= ALGOLIA_API_KEY= ALGOLIA_INDEX_NAME= ALGOLIA_RECOMMENDATION_API_KEY= # SendGrid credentials SENDGRID_API_KEY= SENDGRID_FROM_EMAIL= # Envs
STATIC_DIR=../../client
Let’s start with our data and Algolia credentials.
To follow along, you’ll need an index under your existing Algolia account or you can sign up for a free account and set up an application. For the examples in this blog, we’ll be using an existing demo site.
This demo assumes you have a pre-existing index with enough event data to use Algolia Recommend. If you don’t have an index yet or don’t have enough events for recommendations, you can still use the strategies that don’t require trained machine learning models. Just follow these steps to create and populate an index.
Once we have our index we can set the ALGOLIA_INDEX_NAME
and ALGOLIA_APP_ID
environment variables in our .env
file. We’ll also set ALGOLIA_API_KEY
to our search-only API key. We can find this info on API Keys page of our Algolia dashboard.
ALGOLIA_APP_ID= ALGOLIA_API_KEY= ALGOLIA_INDEX_NAME=
While we’re on the API Key page, we should create another key to use for recommendations. From the API Keys page, we navigate over to the All API Keys tab and click New API Key, choose our index, and add recommendation
to our ACL. Now we can add this key to our .env
file.
ALGOLIA_RECOMMENDATION_API_KEY=
Once all the Algolia pieces are in place, let’s make sure our Recommend models are trained and ready. We click on the Recommend light bulb icon on the Algolia dashboard and look at the “Where is Recommend active?” section. If the models are not already trained, we’ll need to train the models.
It’s ok if we don’t have enough events to train our models. We can still use the templates that don’t need Algolia Recommend.
We use Twilio SendGrid as the email provider to send our rendered previews. You’ll need to sign up with SendGrid if you don’t have an account already, then create and store a SendGrid API key with permissions to send emails. You’ll also need to validate you email domain or single sender email before you’ll be able to send outbound mail.
Add your SENDGRID_API_KEY
and validated SENDGRID_FROM_EMAIL
in the .env
file:
SENDGRID_API_KEY= SENDGRID_FROM_EMAIL=
With all our APIs configured, we’re ready to dig into the server code starting with server.js
. Here we set up our express server and initialize all our API clients:
// Setup Express, markdown renderer and Algolia clients. const app = express(); const converter = new showdown.Converter(); const algoliaClient = algoliaearch(process.env.ALGOLIA_APP_ID, process.env.ALGOLIA_API_KEY) const recommendClient = algoliarecommend(process.env.ALGOLIA_APP_ID, process.env.ALGOLIA_API_KEY); const algoliaIndex = algoliaClient.initIndex(process.env.ALGOLIA_INDEX_NAME); // Setup email delivery sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
In this demo we generate and send emails interactively, so we want to serve up our client application.
// Home page app.get("/", (req, res) => { const indexPath = path.resolve(process.env.STATIC_DIR + "/index.html"); res.sendFile(indexPath); });
Each of our engagement scenarios has an email template and a README.md
describing the scenario. The client loads the four templates out of the emails/
directory and puts them in a list to display in the client application.
app.use(express.static(process.env.STATIC_DIR)); app.use(express.json()); const loadEmails = async () => { const EmailBaseDir = "./emails"; const emailDirs = await fs.promises.readdir(EmailBaseDir); const emails = []; for(const emailDir of emailDirs) { const emailDirPath = path.join(EmailBaseDir, emailDir); const explanationFile = await fs.promises.readFile(path.join(emailDirPath, "README.md"), "utf8"); const explanation = converter.makeHtml(explanationFile); const { email } = await import(`./${path.join(emailDirPath, "email.js")}`); emails.push({ ...email, explanation, }); }; return emails; };
After selecting a scenario from the drop down, the client renders the selected email using nunjucks, the appropriate Cerberus email template from the templates/
directory, and the underlying API calls needed to generate the recommendations for that scenario.
If we like the look of the rendered email, we can send it to ourselves using SendGrid. We type our email address into the Send this email field and click Send. Our service will pass the rendered email to the SendGrid API and mail it to us!
Now that we understand the general pattern, let’s dive into how we construct the recommendations for each of our four scenarios.
In this scenario, a customer browses a category on your site but doesn’t buy anything. We don’t know much about the customer or the specific item they are looking for. We use Algolia’s Faceting to recommend the best rated products from that category.
If the customer browses for men’s t-shirts, we call the Algolia Search API and request the first six items for this category using the index’s ranking criteria.
async (algoliaIndex, _) => { // The customer browsed the Men / Tee-shirt category const facetFilters = ["category:Men - T-Shirts"]; // Get the top-rated products from the category const { hits } = await algoliaIndex.search("", { facetFilters, hitsPerPage: 6 });
We use SendGrind to email these suggestions to the customer with links back to our site to continue browsing.
This is a great fallback strategy when we do not have enough information about the customer or the particular product they are looking for. It doesn’t require event data and relies only on category information within the index itself.
In this scenario, the customer has purchased a product from our site. We’ll suggest products other customers have often bought along with it. We’ll use Algolia Recommend to train a machine-learning algorithm based on event data of customer purchases.
With our models already trained, we’ll only need a few lines of code to get from the current product to a list of recommended products based on the purchase history of other customers on our site.
const orderContent = ["D07940-6931-990"]; // Get the similar items for this item. const { results: [ { hits }, ]} = await recommendClient.getRecommendations([{ indexName: process.env.ALGOLIA_INDEX_NAME, objectID: orderContent[0], model: "bought-together", maxRecommendations: 3, }]);
Again, we use SendGrind to email these suggestions to the customer with links back to our site to continue browsing.
This is similar to scenario two. The customer has just purchased a product from our site. This time we’ll use this information to suggest products related to the one they purchased. We’ll use the Algolia Recommend related-products
model this time.
As with the previous scenario, since our models are already trained, we’ll only need a few lines of code to get from the current product to a list of recommended products based on the purchase history of other customers on our site.
const orderContent = ["D07940-6931-990"]; // Get the related product for the bought product. const { results: [ { hits }, ]} = await recommendClient.getRecommendations([{ indexName: process.env.ALGOLIA_INDEX_NAME, objectID: orderContent[0], model: "related-products", maxRecommendations: 3, fallbackParameters: {facetFilters:["category:Women - Jeans"]} }]);
Notice here we include a fallback parameter. We can fill in our recommendation list with top items from this category if there aren’t enough recommendations from the model.
Now we can email the recommendations to the customer using SendGrind.
In this final scenario, a customer has visited the site before. As they navigated our site, we captured their click and conversion actions using Algolia Insights. Algolia takes these events and builds a Personalization profile for the customer. We will use this profile along with the Recommend API to find related products that match the customer’s affinities.
First, we use the unique token our site uses for this customer to retrieve their personalization profile from the Algolia Personalization endpoint. (Note that we use the Recommendation API credentials to retrieve this profile.)
async (algoliaIndex, recommendClient) => { // Get the customer's affinities profile // https://www.algolia.com/doc/rest-api/personalization/#get-a-usertoken-profile const userToken = 'user-1'; const res = await fetch( `https://recommendation.eu.algolia.com/1/profiles/personalization/${userToken}`, { headers: { "X-Algolia-API-Key": process.env.ALGOLIA_RECOMMENDATION_API_KEY, "X-Algolia-Application-Id": process.env.ALGOLIA_APP_ID } } ); const { scores } = await res.json();
The response is the customer’s profile in JSON format. It will look something like this:
{ "userToken": "user_1", "lastEventAt": "2019-07-12T10:03:37Z", "scores": { "category": { "Women - Jeans": 1, "Men - T-Shirts": 10 }, "location": { "US": 6 } } }
This profile includes a list of categories from our site ranked by affinity score. We’ll take the top-ranked categories and turn them into facets that the user can use to search for products.
// Transform the top customer affinities into a list of `facetFilters` let facetFilters = []; for (const key in scores) { const score = scores[key]; let values = []; for (const valueKey in score) { values.push([ valueKey, score[valueKey] ]); }; values.sort((kv1, kv2) => kv2[1] - kv1[1]); facetFilters.push(`${[key]}:${values[0][0]}`); };
Algolia Recommend provides recommendations based on individual products, not categories. We’ll retrieve the top products from each of the facets we know our customer likes, then send them to Algolia Recommend to find similar products.
// Get the top-rated products matching the top-scoring customer profile facets const { hits: [topHit,] } = await algoliaIndex.search("", { facetFilters, hitsPerPage: 6 }); // Get the related items matching the top-rated product const { results: [ { hits }, ]} = await recommendClient.getRecommendations([{ indexName: process.env.ALGOLIA_INDEX_NAME, objectID: topHit.objectID, model: "related-products", maxRecommendations: 3 }]);
Now we have engaging results by combining our knowledge of the customer’s habits with the buying behaviors of other customers on our site.
Once again, we add links back to our site and send the email out using SendGrid.
You can generate engaging customer recommendation emails based on knowledge of your products and your users’ behavior. Machine learning models like those in Algolia Recommend can bring even more relevance into your emails. Now you have the tools to go build a recommendation email service of your own!
Want to do more with recommendations? Take a look at our code blocks using Twilio or, more broadly, our code blocks using Algolia Recommend. Check out related solutions on our open source code exchange platform.
Powered by Algolia Recommend