< Back

Personalized Search at the Speed of Algolia

Search is more than a query and results – search is a conversation your users have with your product. When done right, you feel understood by the engine – and, in turn, the product – which reacts to your needs and adapts to what you’re saying as you’re saying it.

Perhaps one of the most magical moments is when the search engine speaks to you – an individual person, with search results that are catered to your personal tastes and expectations.

“Personalization, yes! But not at the cost of speed.”

One of the core focuses of Algolia is speed. Every time we’ve looked at how other search engines handle personalization, it always came at the cost of speed – we don’t think personalizing the search results makes sense if it makes the search slow. Who wants a slow, personal conversation?

We rolled up our sleeves earlier this year and got to work, and we’re proud today to announce two features that make personalization of search results a breeze, without compromising on the speed Algolia is known for. We call these two API features Optional Filters and Filter scoring – and here’s how it works.

Because the feature can place high demands on CPU performance, it is enabled by default only in our Enterprise plans. If you’re interested in trying it out on your plan, send us a note.

Getting started

Let’s take the example of searching through an index of movies. In this hypothetical website, users can add movies to their “Watch later” list – a familiar feature – and this information is added to the index, in the form of an attribute watch_later with an array of user IDs of every user that adds this movie.

A Sample JSON entry:

{
 movie_name: "The Karate Kid",
 genre: "Action"
 watch_later: ["user42", "user123", "user421", …]
 // …
}

When we search this index, we want movies that are added to a watch list to appear before others.

A regular search would look like:

index.search("karate").then(/* … */)

With personalization, it would work like this:

// `watch_later` needs to be set as a facet in attributesForFaceting
index.search(
 "karate",
 { optionalFilters: ["watch_later:user42"] }
).then(/* … */)

With only one added parameter, suddenly the results are personalized: the search retrieves all the movies related to “karate”, but if a user has added one of the movies related to “karate” to their watch list, this result will show up at the top of the results.

Going further: multiple levels of personalization

What if you want to have multiple layers of personalization? For example, let’s say you’ve analyzed that a user is a huge fan of Action movies, watches a lot of comedies, but is not restricted to these two genres. You’d like the results to first show Action movies, then Comedies, and then the rest of the results. Here’s how you’d do it:

// `genre` needs to be set as a facet in attributesForFaceting
index.search(
 "karate",
 { optionalFilters: ["genre:Action<score=2>", ["genre:Comedy<score=1>"] }
).then(/* … */)

…And you’ll get the results in the order that you expected! We added a new rule called filters in our ranking formula, which will rank the results according to the matching optionalFilters and filters scores.

We’ve been playing around with these features for a few months now, and a few of our customers already use them in production during the beta period, with amazing results.

The best part about these features is how versatile they are: there’s virtually nothing you can’t do when it comes to personalization of results:

  • On a professional social network, retrieving people that are part of your extended network first
  • When searching on a shoe store, displaying brands or categories of products the user has bought from before first
  • In a CRM search, displaying leads I’ve contacted in the past first, or the ones that are assigned to me

If you want to know more about these features, we wrote a guide on personalization. We can’t wait to see what you’ll build with them!