Lightning-Fast Search with MeiliSearch and Elixir in Record Time

In this story, you will learn how to implement lightning-fast search functionality in your Elixir Phoenix web application using MeiliSearch.
Let’s start!
What is MeiliSearch?
MeiliSearch is an open-source search engine built using Rust, designed to offer high performance and ease of use.
MeiliSearch has features such as typo tolerance, faceted search, and multi-language support.
I personally love MeiliSearch, it is insanely simple, very fast and simply fun to use.
MeiliSearch also has many SDKs and an easy-to-use REST API which lets us use it even easier.
What we are building in this story
In this story, you will learn how to use Elixir, Phoenix and MeiliSearch to build a blazing-fast movie search engine.
We will also be using the newly released version of Phoenix, Phoenix 1.7, which comes with many new improvements.
The end result will look like this.

You can find the entire project here.
Anyways, enough talking. Time to code!
Coding our project
Time to code, everyone!
I will be explaining all the steps so that you understand everything, and as I said, feel free to check out the GitHub repository if you are stuck at some point to see what the complete implementation looks like.
Creating a MeiliSearch instance using Docker
Creating a MeiliSearch instance using Docker is very simple. It takes only two commands to get started!
To create a MeiliSearch instance using Docker, you will need to run the following commands:
Pulling the image
docker pull getmeili/meilisearch:v1.0
Running the image
docker run -it --name meilisearch -p 7700:7700 \ -e MEILI_ENV=development \ -v $HOME/docker/volumes/meili_data:/meili_data \ getmeili/meilisearch:v1.0
Loading the movie dataset to MeiliSearch
Now that we have a MeiliSearch instance running, we just have to insert our movie dataset in it.
You can find the dataset here.
Download the dataset and then run the following command to insert it in MeiliSearch.
curl -X POST 'http://localhost:7700/indexes/movies/documents?primaryKey=id' \ -H 'Content-Type: application/json' \ --data-binary @data/movies.json
As you can see, we specify the primaryKey as id, since that's what we have in our dataset and the whole thing takes a single API request.
MeiliSearch’s REST API is asynchronous by default. This means that when you send a request to MeiliSearch, it will return a response immediately, even if the request is not yet complete.
We can check the status of the upload using this command.
curl -X GET 'http://localhost:7700/tasks/0'
This will give you a response like this.
{
"uid": 0,
"indexUid": "movies",
"status": "succeeded",
"type": "documentAdditionOrUpdate",
"canceledBy": null,
"details": {
"receivedDocuments": 31944,
"indexedDocuments": 31944
},
"error": null,
"duration": "PT17.265502149S",
"enqueuedAt": "2023-03-05T07:52:40.725678886Z",
"startedAt": "2023-03-05T07:52:40.728976213Z",
"finishedAt": "2023-03-05T07:52:57.994478362Z"
}Yup, looks good!
If you visit localhost:7700, you can use the MeiliSearch UI to further explore the data.
Phoenix web application
Alright, it is time to start coding the web application.
First of all, run the following command to install the latest version of Phoenix.
mix archive.install hex phx_new
After that, we can create a new Phoenix 1.7 project.
mix phx.new movies --no-ecto
We used the --no-ecto flag because we don’t need a database for this project.
Cleaning up unnecessary files
This step is pretty much optional and way too boring for me to write about it.
Basically, you can delete all the files related to page_html, since we will be using LiveView, and you can also clean up lib/movies_web/components/layouts/app.html.heex since we don’t really need a navigation bar for our application.
This is what my layout looks like.
<main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-2xl">
<.flash_group flash={@flash} />
<%= @inner_content %>
</div>
</main>You can see what the entire project looks like on GitHub.
Coding the web application
From now on, I won’t be explaining every step in detail since I already did that in an older story of mine. Feel free to check that story if you don’t already know Phoenix LiveView basics.
Alright, let’s go.
Creating our LiveView page
Let’s create a lib/movies_web/live folder, and two new files in it. index.ex and index.html.heex.
This is how the index.html.heex file should look like.
<.header>
<div class="flex space-x-4 self-center items-center">
<h1>
Movies
</h1>
<h1 class="text-sm text-green-500">
<%= @processing_time %>ms
</h1>
</div>
</.header>
<br />
<.simple_form for={@form} phx-change="search">
<.input name="search" value="" placeholder="Search a movie..." />
</.simple_form>
<div id="infinite-scroll" class="flex flex-col space-y-8 items-center" phx-hook="InfiniteScroll">
<.table id="results" rows={@results}>
<:col :let={result} label="ID"><%= result["id"] %></:col>
<:col :let={result} label="Title"><%= result["title"] %></:col>
<:col :let={result} label="Overview"><p class="w-64"><%= result["overview"] %></p></:col>
<:col :let={result} label="Image"><img class="h-full" src={result["poster"]} /></:col>
</.table>
</div>As you can see, we just have a simple page with a header, a search bar, and a table where we display our results.
Here you can also see the core components we got with Phoenix 1.7, providing us with the header, simple_form, input, table and col, which lets us build our application easier and faster.
And now the index.ex file. I will use GitHub Gists for Elixir files since Medium doesn’t have syntax highlighting for Elixir yet.





