Ultra Fast Search API using Meilisearch 🦀

Karanvknarayanan
Intelliconnect Engineering
4 min readNov 18, 2021

Search is an essential tool in a lot of fields e-commerce, movie catalog, and many more. Search is an essential tool in a lot of fields e-commerce, movie catalogue, and many more. Meilisearch An open-source, blazingly fast, and hyper-relevant search-engine that will improve your search experience.

Advantages for developers:

  • Scalable
  • Maintainable
  • Customizable

Advantages for developers:

  • Super Fast (~< 30ms)
  • Relevant
  • Typo Tolerant

Realtime Results:

Designed to come up in less than 50 ms. It allows users to find the result and stop if the result is reached which allows for maximum efficiency.

Not everyone is correct:

Offers a typo-tolerant and natural query language search experience.

One can mean anything:

It allows users to find results with synonyms which makes the search experience pleasant.

Language support:

Currently, Meilisearch supports English, Latin-based languages and kanji languages, and many more coming soon. Developers are currently developing support for Arabic.

Wide SDK support:

Currently, Meilisearch supports the following SDK:

  • Dart
  • Golang
  • Java
  • JavaScript
  • PHP
  • Python
  • Ruby
  • Rust
  • Swift
  • .NET

Get Started

Installation:

Library Demo

Setup a new Cargo Project:

Connecting with MeiliSearch

use meilisearch_sdk::{
client::*, document::*, indexes::Index, progress::Progress, search::Selectors,
};
let client = Client::new("http://localhost:7700", "masterKey");

Creating an index:

let index = client.get_or_create(index).await.unwrap();

This part fetches the index if exists or create a new one

Inserting documents:

To insert a document one has to implement Documentfrom meilisearch and Serializeand Deserialize to insert a document let us see an example for creating a movie catalog

Preparing the Struct

use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs::File, io::prelude::*};
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Movie {
id: String,
title: String,
poster: String,
overview: String,
release_date: i64,
genres: Option<Vec<String>>,
}
// Seting up the document struct
impl Document for Movie {
type UIDType = String;
fn get_uid(&self) -> &Self::UIDType {
&self.id
}
}

For any struct in order to insert into meilisearch one has to implement a document to identify the id or the unique identifier property

Insert Documents

async fn add_documents(index: Index, documents: &Vec<Movie>, primary_key: &str) {
let res = index
.add_or_update(&documents, Some(primary_key))
.await
.unwrap();
}

This snippet returns a Progress object

Processing { 
content: EnqueuedUpdateResult
{ update_id: 0,
update_type: DocumentsPartial {
number: None
},
enqueued_at: "2021-11-16T03:59:11.064267100Z"
}
}

Searching an Index

The above query will filter based on the provided genre and faucets based on the genre and sort based on the release_date in descending order and if multiple genres were passed in the genre filtering then the faucet distribution shows how many matches in each category

Filtering

Filters can be used to limit the results available to a specific user or to create faceted search interfaces. Faceted search interfaces are especially effective at assisting users in sorting through a large number of results across many broad categories.

To set a filterable field:

Highlighting

if a search query is matching with a document they can be highlighted using the following snippet in the search query ( They add a em tag around the matching query words)

Restricted searching

Sometimes we don’t want the entire document to be retrieved every time we search we can restrict it by using the following snippet

Only the mentioned field is searchable rest are static non-searchable data this is useful for an index that has lot of attributes for each document

Restricting returning attributes

Sometimes we don’t want the entire document to be retrieved every time we search we can restrict it by using the following snippet

Sortable Attributes

For some attributes such as release_date attribute sorting is a desirable feature the following snippet will facilitate the sorting

Ranking rules

Ranking rules are built-in rules that enable you to tailor the relevance of your search results. They are stored in an array and are applied in the order in which they appear.

To set a custom ranking rule one can use the following code snippet

Stop Words

The sorting algorithm will ignore the stop words in your search query during the search.

To set a custom stop word use the following code snippet

Synonyms

If multiple words have an equivalent meaning in your dataset, you can create a list of synonyms. This will make your search results more relevant.

To set custom synonym use the following code snippet

Deleting an Index

With all of these let’s build a custom HTTP API

Setup

Cargo Setup

/cargo.toml

/src/main.rs

To set the filterable genre

Search function with optional filter

Conclusion

Rust for Search Engines brings a balance between performance and developer safety. Developers rely on search engines such as Elastic search (which has a bigger ecosystem and rich features). However, for quick and fast search implementation - solutions like Meilisearch are faster and easy to use.

Refer code at https://github.com/intelliconnect/meilisearch-rust

--

--