
Recommending relevant content to the user is essential to keep the user interested in the app. Although it is a common feature that we would like to have in our apps, building it is not straightforward. This changed as vector databases and Open AI emerged. Today, we can perform semantic searches that are highly aware of the context of the content with just a single query into our vector database. In this article, we will go over how you can create a Flutter movie-viewing app that recommends another movie based on what the user is viewing.
A quick disclaimer, this article provides an overview of what you can build with a vector database, so it will not go into every detail of the implementation. You can find the full code base of the app in this article here to find more details.
Why use a vector database for recommending content
In machine learning, a process of converting a piece of content into a vector representation, called embeddings, is often used, because it allows us to analyze the semantic content mathematically. Assuming we have an engine that can create embeddings that are well aware of the context of the data, we can look at the distance between each embedding to see if the two content are similar or not. Open AI provides a well-trained model for converting text content into an embedding, so using it allows us to create a high-quality recommendation engine.
There are numerous choices for vector databases, but we will use Supabase as our vector database in this article, because we want to also store non-embedding data, and we want to be able to query them easily from our Flutter application.
What we will build
We will be building a movie listing app. Think Netflix except the users will not be able to actually view the movie. The purpose of this app is to demonstrate how to surface related content to keep the users engaged.
Tools/ technologies used
- Flutter - Used to create the interface of the app
- Supabase - Used to store embeddings as well as other movie data in the database
- Open AI API - Used to convert movie data into embeddings
- TMDB API - A free API to get movie data
Creating the app
We first need to populate the database with some data about movies and its embeddings. For that, we will use the Supabase edge functions to call the TMDB API and the Open AI API to get the movie data and generate the embeddings. Once we have the data, we will store them in Supabase database, and query them from our Flutter application.
Step 1: Create the table
We will have one table for this project, and it is the films
table. films
table will store some basic information about each movie like title or release data, as well as embedding of each movie’s overview so that we can perform vector similarity search on each other.
Step 2: Get movie data
Getting movie data is relatively straightforward. TMDB API provides an easy-to-use movies endpoint for querying information about movies while providing a wide range of filters to narrow down the query results.
We need a backend to securely call the API, and for that, we will use Supabase Edge Functions. Steps 2 through 4 will be constructing this edge function code, and the full code sample can be found here.
The following code will give us the top 20 most popular movies in a given year.
Step 3: Generate embeddings
We can take the movie data from the previous step and generate embedding for each of them. Here, we are calling the Open AI Embeddings API to convert the overview
of each movie into embeddings. overview
contains the summary of each movie, and is a good source to create embedding representing each of the movies.
Step 4: Store the data in the Supabase database
Once we have the movie data as well as embedding data, we are left with the task of storing them. We can call the upsert()
function on the Supabase client to easily store the data.
Again, I omitted a lot of code here for simplicity, but you can find the full edge functions code of step 2 through step 4 here.
Step 5: Create a database function to query similar movies
In order to perform a vector similarity search using Supabase, we need to create a database function. This database function will take an embedding
and a film_id
as its argument. The embedding
argument will be the embedding to search through the database for similar movies, and the film_id will be used to filter out the same movie that is being queried.
Additionally, we will set an HSNW index on the embedding
column to run the queries efficiently even with large data sets.
Step 6: Create the Flutter interface
Now that we have the backend ready, all we need to do is create an interface to display and query the data from. Since the main focus of this article is to demonstrate similarity search using vectors, I will not go into all the details of the Flutter implementations, but you can find the full code base here.
Our app will have the following pages:
- HomePage: entry point of the app, and displays a list of movies
- DetailsPage: displays the details of a movie as well as its related movies
components/film_cell.dart
is a shared component to display a tappable cell for the home and details page. models/film.dart
contains the data model representing a single movie.
The two pages look like the following. The magic is happening at the bottom of the details page in the section labeled You might also like:
. We are performing a vector similarity search to get a list of similar movies to the selected one using the database function we implemented earlier.
The following is the code for the home page. It’s a simple ListView with a standard select
query from our films
table. Nothing special going on here.
In the details page, we are calling the get_related_film
database function created in step 5 to get the top 6 most related movies and display them.
And that is it. We now have a functioning similarity recommendation system powered by Open AI built into our Flutter app. The context used today was movies, but you can easily image that the same concept can be applied to other types of content as well.
Afterthoughts
In this article, we looked at how we could take a single movie, and recommend a list of movies that are similar to the selected movie. This works well, but we only have a single sample to get the similarity from. What if we want to recommend a list of movies to watch based on say the past 10 movies that a user watched? There are multiple ways you could go about solving problems like this, and I hope reading through this article got your intellectual curiosity going to solve problems like this.