<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Carolina</title>
    <description>The latest articles on Forem by Carolina (@carofg).</description>
    <link>https://forem.com/carofg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F876602%2F31eee6f6-c738-4a1f-bdde-ee190da440e3.jpg</url>
      <title>Forem: Carolina</title>
      <link>https://forem.com/carofg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/carofg"/>
    <language>en</language>
    <item>
      <title>How to deliver the best search results: inside a full text search engine</title>
      <dc:creator>Carolina</dc:creator>
      <pubDate>Tue, 04 Jul 2023 10:30:00 +0000</pubDate>
      <link>https://forem.com/meilisearch/how-to-deliver-the-best-search-results-inside-a-full-text-search-engine-1e17</link>
      <guid>https://forem.com/meilisearch/how-to-deliver-the-best-search-results-inside-a-full-text-search-engine-1e17</guid>
      <description>&lt;p&gt;Search bars are an integral part of browsing the web. Whether we are shopping for clothes, enjoying music, or planning a vacation, there is always a dedicated search engine at work to simplify our lives. Have you ever wondered how these search engines actually work? In this article, we will delve into the inner workings of one of them: Meilisearch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexing time: from data to documents
&lt;/h2&gt;

&lt;p&gt;Indexing is a fundamental process that encompasses the collection, parsing, and storage of data for subsequent retrieval. It plays a crucial role in enabling search engines to deliver fast and relevant results.&lt;/p&gt;

&lt;h3&gt;
  
  
  A performant storage engine
&lt;/h3&gt;

&lt;p&gt;Meilisearch stores data in the form of discrete records called "&lt;a href="https://docs.meilisearch.com/learn/core_concepts/documents.html%20embed%20%20"&gt;documents&lt;/a&gt;," which are grouped together into collections known as "&lt;a href="https://docs.meilisearch.com/learn/core_concepts/indexes.html"&gt;indexes&lt;/a&gt;." Under the hood, Meilisearch uses the LMDB (&lt;a href="http://www.lmdb.tech/doc/?ref=blog.meilisearch.com"&gt;Lightning Memory-Mapped Database&lt;/a&gt;) key-value store which has proven stability and extensive usage in database applications. As the name implies, key-value stores are storage systems that organize data as a &lt;strong&gt;collection of key-value pairs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--153UFnsH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/focua1xcl7ymx017h90m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--153UFnsH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/focua1xcl7ymx017h90m.png" alt="Illustration of a key-value store with 'engine' set to 'meilisearch' and 'language' set to 'rust'" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
Example of key-value entries



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Key-value stores offer flexibility, fast access times, and efficient performance, contributing to quick and responsive interactions for users. By permitting only a single writing process at a time, LMDB avoids synchronization-related issues. As a result, it enables unlimited concurrent readers to have fast access to up-to-date, consistent data.&lt;/p&gt;

&lt;p&gt;Before storing data in its key-value store, Meilisearch must preprocess it thoroughly. That’s where tokenization comes in.&lt;/p&gt;

&lt;h3&gt;
  
  
  From words to tokens
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tokenization&lt;/strong&gt; involves two main processes: segmentation and normalization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Segmentation&lt;/strong&gt; consists in splitting a sentence or phrase into smaller units called tokens.&lt;/p&gt;

&lt;p&gt;Meilisearch uses (and maintains) the open-source tokenizer named &lt;a href="https://github.com/meilisearch/charabia"&gt;Charabia&lt;/a&gt;. The tokenizer is responsible for retrieving all the words (or tokens) from document fields.&lt;/p&gt;

&lt;p&gt;👉 In practice, Meilisearch users can configure which fields should be &lt;a href="https://www.meilisearch.com/docs/learn/configuration/displayed_searchable_attributes#searchable-fields"&gt;searchable&lt;/a&gt;, allowing Charabia to know which fields should be tokenized.&lt;/p&gt;

&lt;p&gt;Then comes normalization. Words are normalized based on each language’s particularities. For a romance language like French, this process includes setting the word in lowercase letters and removing diacritical marks such as accents. For instance, a sentence like “Le café de Nicolas” is transformed into “le cafe de nicolas”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ltKGxPYF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvp1cf8wrn0z8fxsnhnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ltKGxPYF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvp1cf8wrn0z8fxsnhnk.png" alt="The sentence 'Le café de Nicolas,' with a capital 'L' and 'N,' and an accent on the 'é' of 'café,' is first split into words during the segmentation phase. Then, during normalization, each word is lowercased, and accents are removed." width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;
Example of the segmentation and normalization stages in the tokenization process



&lt;p&gt;&lt;br&gt;&lt;br&gt;
After segmenting and normalizing words, the engine needs to classify them. Using the appropriate data structure to organize tokens is crucial for performance. This will later allow swift retrieval of the most relevant search results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing tokens
&lt;/h3&gt;

&lt;p&gt;Modern full-text search engines like Meilisearch come with features such as prefix search, typo tolerance, and geo search. Each data structure has its own advantages and disadvantages. When designing the search engine, our team carefully selected the ones best suited to enable these features without compromising on speed.&lt;/p&gt;

&lt;p&gt;Throughout this section, we will explore which data structures powers Meilisearch.&lt;/p&gt;

&lt;h4&gt;
  
  
  Inverted index
&lt;/h4&gt;

&lt;p&gt;First and foremost, we need to talk about inverted indexes. Without a doubt, it’s the data structure that stands out by its importance. Meilisearch uses it to return documents fast upon search.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;inverted index&lt;/strong&gt; maps each word in a document to the set of documents where that term appears along with additional information like their position in the document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1X8p7_vP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55cirl1hus5e8j0mjsn3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1X8p7_vP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55cirl1hus5e8j0mjsn3.png" alt='Given the following documents: { “id”: 1 "title": "Hello world!" }, { “id”: 2 "title": "Hello Alice!" }. The inverted index would look like this: "alice" -&amp;gt; [2], "hello" -&amp;gt; [1,2], "world" -&amp;gt; [1]' width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
 Illustration of an inverted index generated from given documents, showcasing the mapping of keywords to document IDs 



&lt;p&gt;&lt;br&gt;&lt;br&gt;
By storing words once and associating them with the documents where they appear, inverted indexes leverage the redundancy of terms across documents. Thanks to this, Meilisearch does not need to browse all documents in order to find a given word, resulting in faster searches.&lt;/p&gt;

&lt;p&gt;Meilisearch creates approximately 20 inverted indexes per document index, making it one of the data structures with the highest number of instances. Indeed, to provide a search-as-you-type experience, the engine needs to precompute a lot at indexing time: word prefixes, documents containing filterable attributes, and more. For a better understanding of how inverted indexes work read more on &lt;a href="https://blog.meilisearch.com/best-practices-for-faster-indexing/"&gt;indexing best practices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;💡 You can find the complete list of Meilisearch inverted indexes on &lt;a href="https://github.com/meilisearch/meilisearch/blob/78e611f282d726a604cfbc231666265a37c4d46b/milli/src/index.rs#L103-L157"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Roaring bitmap
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Roaring bitmaps&lt;/strong&gt; are compressed data structures designed for efficiently representing and manipulating sets of integers.&lt;/p&gt;

&lt;p&gt;Meilisearch uses roaring bitmaps widely throughout its inverted indexes to represent document IDs. Roaring bitmaps offer a space-efficient way to store large sets of integers and perform set operations like &lt;a href="https://en.wikipedia.org/wiki/De_Morgan%27s_laws"&gt;union, intersection, and difference&lt;/a&gt;. These operations play a crucial role in refining search results by allowing the inclusion or exclusion of specific documents based on their relationship with others.&lt;/p&gt;

&lt;h4&gt;
  
  
  Finite-state transducer
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;finite-state transducer&lt;/strong&gt; (FST) is a structured data representation ideal for performing string-matching operations. It represents a sequence of states ordered from smallest to largest, with words arranged in lexicographic order.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;a href="https://en.wikipedia.org/wiki/Lexicographic_order"&gt;Lexicographic order&lt;/a&gt; is a method of arranging or sorting items, such as words or symbols, based on their alphabetical or numerical sequence.&lt;/p&gt;

&lt;p&gt;A finite-state transducer is also called a word dictionary because it contains all words present in an index. Meilisearch relies on the utilization of two FSTs: one for storing all words of the dataset, and one for storing the most recurrent prefixes.&lt;/p&gt;

&lt;p&gt;FSTs are useful because they support compression and lazy decompression techniques, optimizing memory usage and storage. Additionally, by using automata such as regular expressions, they can retrieve subsets of words that match specific rules or patterns. Moreover, this enables the retrieval of all words that begin with a specific prefix, empowering fast, comprehensive search capabilities.&lt;/p&gt;

&lt;p&gt;The FST, with its compact design, presents the added advantage of being a smaller data structure than the inverted index. As we will see later, this contributes to faster and more efficient reading operations. You can learn more about the topic in this &lt;a href="https://blog.burntsushi.net/transducers/"&gt;comprehensive article on finite-state reducers&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  R-tree
&lt;/h4&gt;

&lt;p&gt;R-trees are a type of &lt;a href="https://en.wikipedia.org/wiki/Tree_(data_structure)"&gt;tree&lt;/a&gt; used for indexing multi-dimensional or spatial information. Meilisearch leverages R-trees to power its geo search functionality.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;R-tree&lt;/strong&gt; associates geographical coordinates with the identifiers of the document to which it belongs. By organizing coordinates this way, it enables Meilisearch to perform spatial queries with remarkable efficiency. These queries allow users to find nearby points, points within a specific area, or points that intersect with other spatial objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search time: query processing
&lt;/h2&gt;

&lt;p&gt;Modern search experiences only require you to start typing to get results. To achieve this search-as-you-type experience, Meilisearch precomputes a list of the most frequent prefixes.&lt;/p&gt;

&lt;p&gt;To be tolerant to typos, Meilisearch uses finite-state transducers in conjunction with the &lt;a href="https://www.meilisearch.com/docs/learn/configuration/typo_tolerance#understanding-typo-calculations"&gt;Levenshtein algorithm&lt;/a&gt;. This algorithm calculates the Levenshtein distance which can be thought of as the cost of transforming a string into another. In other words, it quantifies the number of transformations required for a word to be converted into another word.&lt;/p&gt;

&lt;p&gt;In the context of the Levenshtein algorithm, possible transformations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  insertions, e.g. hat -&amp;gt; chat&lt;/li&gt;
&lt;li&gt;  deletions, e.g. tiger -&amp;gt; tier&lt;/li&gt;
&lt;li&gt;  substitutions, e.g. cat -&amp;gt; hat&lt;/li&gt;
&lt;li&gt;  transpositions or swaps, e.g. scared -&amp;gt; sacred&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FSTs generate all possible variations of a word within a specified edit distance, enabling the engine to calculate the Levenshtein distance and accurately detect typos by comparing user input against a dictionary of valid words.&lt;/p&gt;

&lt;p&gt;It becomes apparent that there are many factors to consider when processing a search request. Has the user finished typing? Are there typos in the query? Which documents should appear first in the search results?&lt;/p&gt;

&lt;p&gt;Let’s discuss how Meilisearch processes search queries and how it refines and sorts search results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query graph
&lt;/h3&gt;

&lt;p&gt;Each time it receives a search query, Meilisearch creates a query graph representing the query and its possible variations.&lt;/p&gt;

&lt;p&gt;To compute these variations, Meilisearch applies &lt;a href="https://www.meilisearch.com/docs/learn/advanced/concat#concatenated-queries"&gt;concatenation&lt;/a&gt; and &lt;a href="https://www.meilisearch.com/docs/learn/advanced/concat#split-queries"&gt;splitting&lt;/a&gt; algorithms to the query terms. The variations considered also include potential typos and synonyms–if the user set them. As an example, let’s examine the query: &lt;code&gt;the sun flower&lt;/code&gt;. Meilisearch will also search for the following queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;the sunflower&lt;/code&gt;: concatenation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;the sun flowed&lt;/code&gt;: substitution&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;the sun flowers&lt;/code&gt;: addition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider now a more complex query: &lt;code&gt;the sun flower is facing the su&lt;/code&gt;, the query graph should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D7qp7Xq---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9y95romn745ruga6a1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D7qp7Xq---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9y95romn745ruga6a1z.png" alt="Graph representing interpretations of the search query 'the sun flower is facing the su'. The engine precomputes for each query term word variations, like 'flowed' or 'flowers' and the associated Levenshtein cost" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;
Visual generated using our internal debugging tool, powered by &lt;a href="https://github.com/terrastruct/d2"&gt;D2&lt;/a&gt;, showcasing the execution process of search requests



&lt;p&gt;&lt;br&gt;&lt;br&gt;
As illustrated above, the graph represents different interpretations of the search query. The engine precomputes the word variations (and their cost) for each query term. Moreover, it detects whether the last query term is a prefix (not followed by a space), thus signaling the need to consult the prefix database.&lt;/p&gt;

&lt;p&gt;So how does the engine utilize a query graph?&lt;/p&gt;

&lt;p&gt;As we saw earlier, during the indexing process, Meilisearch identifies all documents that have filterable attributes, such as &lt;code&gt;genre&lt;/code&gt;. Subsequently, it generates a list of document IDs associated with each attribute value, like &lt;code&gt;comedy&lt;/code&gt; or &lt;code&gt;horror&lt;/code&gt;. First of all, when a filter is applied, Meilisearch narrows down the potential results to the document IDs that fulfill the filter criteria.&lt;/p&gt;

&lt;p&gt;Next, it uses the query words and the variations generated in the query graph and searches for matching words in the finite-state transducer (that is, our word dictionary). If the word is considered a prefix, it will also look it up in the prefix FST.&lt;/p&gt;

&lt;p&gt;When encountering words in a FST, it searches them within the inverted index—which contains a mapping of words to document IDs— to retrieve the corresponding document IDs.&lt;/p&gt;

&lt;p&gt;Finally, the engine performs an intersection to identify the documents that contain the words in the query graph and meet the filter criteria.&lt;/p&gt;

&lt;p&gt;Let's take an example to better understand query processing: suppose you have a dataset of songs. The search query is &lt;code&gt;John Lennon&lt;/code&gt;. The user wants to retrieve only songs released between 1957 and 1975.&lt;/p&gt;

&lt;p&gt;First, Meilisearch retrieves the document IDs of songs within that time frame. Then, after checking in the FST that the words in the query graph exist, Meilisearch retrieves the IDs of the documents that contain either &lt;code&gt;John&lt;/code&gt;, &lt;code&gt;Lennon&lt;/code&gt;, or both. It also retrieves the possible variations, but we are not including them in this example for the sake of simplicity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aMY3Ruhe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e9fw3gn9o5pbb2c38esn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aMY3Ruhe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e9fw3gn9o5pbb2c38esn.png" alt='Three Venn diagrams illustrating the process of retrieving songs released based on a user query (John Lennon) with a date filter (between 1057 and 1975). Diagram 1: Represents songs released between 1957 and 1975. Diagram 2: Represents songs with "John" and/or "Lennon" in the title. Diagram 3: Represents the intersection between the two previous diagrams.' width="744" height="874"&gt;&lt;/a&gt;&lt;/p&gt;
Diagrams illustrating the operations involved in retrieving documents based on a user query.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Finally, the intersection of the two sets of document IDs is taken (the purple area in the diagram.) This means only document IDs that appear in both sets are kept. In other words, Meilisearch retains the document IDs of songs released between 1957 and 1975 that contain either &lt;code&gt;John&lt;/code&gt;, &lt;code&gt;Lennon&lt;/code&gt;, or both.&lt;/p&gt;

&lt;p&gt;What happens when several documents match the search query, how does the engine decide which one to return first? Which one is more relevant? Let's explore the rules allowing Meilisearch to determine how to rank results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relevancy
&lt;/h3&gt;

&lt;p&gt;As we discussed earlier, the query graph includes possible variations of words. As a result, Meilisearch will also return documents with an artist, such as &lt;em&gt;John Lebon&lt;/em&gt;, as part of the search results.&lt;/p&gt;

&lt;p&gt;Fortunately, the query graph not only accounts for word variations but also assigns them the Levenshtein cost, which, among other factors, helps Meilisearch determine the relevancy of the search results.&lt;/p&gt;

&lt;p&gt;Meilisearch sorts documents in the search results using &lt;a href="https://dev.toJohn%20Lebon"&gt;bucket sort&lt;/a&gt;. This algorithm allows ranking documents based on a set of consecutive rules. By default, Meilisearch prioritizes rules as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Words: sorts documents based on the number of matched query terms, with documents containing all query terms ranked first
&lt;/li&gt;
&lt;li&gt;Typo: sorts documents based on the number of typos, with documents matching query terms with fewer typos ranked first&lt;/li&gt;
&lt;li&gt;Proximity: sorts documents based on the distance between matched query terms. Documents where query terms occur close together and in the same order as the query string are ranked first&lt;/li&gt;
&lt;li&gt;Attribute: sorts documents based on the ranking order of attributes. Documents containing query terms in more important attributes are ranked first. Documents with query words at the beginning of attributes are considered more relevant than those with query words at the end&lt;/li&gt;
&lt;li&gt;Sort: sorts documents based on the user-defined parameters set at query time, if active&lt;/li&gt;
&lt;li&gt;Exactness: sorts documents based on the similarity of matched words with the query words&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Meilisearch applies these rules sequentially, sorting results step by step. If two documents are tied after one rule, it uses the next rule to break the tie.&lt;/p&gt;

&lt;p&gt;👉 Note that these rules are fully customizable, meaning you can add, delete, and reorder them as needed. Read more in the &lt;a href="https://www.meilisearch.com/docs/learn/core_concepts/relevancy#ranking-rules"&gt;relevancy documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By default, Meilisearch returns up to 1000 documents per search, prioritizing delivery of the most relevant results rather than all matching results. In other words, Meilisearch prioritizes efficiency and relevance over exhaustive results to ensure an optimized search experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Search engines are complex systems that encompass multiple interconnected components, all working together to deliver a seamless search experience. While the inner workings may differ among search engines, we have explored the underlying mechanisms that power a modern search engine.&lt;/p&gt;

&lt;p&gt;Meilisearch is currently exploring the vast realm of &lt;strong&gt;&lt;a href="https://github.com/meilisearch/meilisearch/issues/3838?ref=blog.meilisearch.com"&gt;semantic search&lt;/a&gt;&lt;/strong&gt;. AI-powered search is reshaping the way we understand queries and documents, opening up new possibilities and use cases. If you’re eager to experience it firsthand, we invite you to try our &lt;a href="https://github.com/meilisearch/product/discussions/677"&gt;vector search experimental feature&lt;/a&gt;—your feedback would be greatly valuable!&lt;/p&gt;

&lt;p&gt;Meilisearch is an open-source search engine that not only provides state-of-the-art experiences for end users but also a simple and intuitive developer experience. Do you want to see Meilisearch in action? Try our &lt;a href="https://where2watch.meilisearch.com/"&gt;demo&lt;/a&gt;. You can also &lt;a href="https://www.meilisearch.com/docs/learn/getting_started/quick_start"&gt;run it locally&lt;/a&gt; or create an account on &lt;a href="https://cloud.meilisearch.com/"&gt;Meilisearch Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more things Meilisearch, you can join the community on &lt;a href="https://discord.gg/meilisearch/"&gt;Discord&lt;/a&gt; or subscribe to the &lt;a href="https://meilisearch.us2.list-manage.com/subscribe?u=27870f7b71c908a8b359599fb&amp;amp;id=79582d828e"&gt;newsletter&lt;/a&gt;. You can learn more about the product by checking out the &lt;a href="https://roadmap.meilisearch.com/"&gt;roadmap&lt;/a&gt; and participating in &lt;a href="https://github.com/meilisearch/product/discussions"&gt;product discussions&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>algorithms</category>
      <category>database</category>
    </item>
    <item>
      <title>How to integrate an extremely fast and relevant search into your Rails app using Meilisearch and React</title>
      <dc:creator>Carolina</dc:creator>
      <pubDate>Thu, 01 Sep 2022 12:12:34 +0000</pubDate>
      <link>https://forem.com/meilisearch/how-to-integrate-an-extremely-fast-and-relevant-search-into-your-rails-app-using-meilisearch-and-react-3g36</link>
      <guid>https://forem.com/meilisearch/how-to-integrate-an-extremely-fast-and-relevant-search-into-your-rails-app-using-meilisearch-and-react-3g36</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you'll learn how to integrate Meilisearch with your Rails application database and quickly create a front-end search bar with a search-as-you-type experience using React.&lt;/p&gt;

&lt;p&gt;We will create a very basic application; our main focus will be the search. Therefore we won't go into much detail about Rails or React.&lt;br&gt;
Prerequisites&lt;/p&gt;

&lt;p&gt;To follow this tutorial, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    &lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; &amp;gt;=16.10&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://yarnpkg.com/"&gt;yarn&lt;/a&gt; 1&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt; &amp;gt;= 2.7&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://guides.rubyonrails.org/getting_started.html#creating-a-new-rails-project-installing-rails"&gt;Ruby on Rails&lt;/a&gt; 7.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ideally, you are familiar with Ruby on Rails and have already created a simple RoR app. If that's not the case, you can still follow this tutorial, but as we stated in the introduction, explanations will focus on the search.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1. Installing Meilisearch
&lt;/h2&gt;

&lt;p&gt;There are &lt;a href="https://docs.meilisearch.com/learn/getting_started/installation.html#download-and-launch"&gt;several ways&lt;/a&gt; to install Meilisearch. The easiest is &lt;a href="https://curl.se/"&gt;cURL&lt;/a&gt;, a tool that allows you to make HTTP requests and transfer data from the command line.&lt;/p&gt;

&lt;p&gt;Open your terminal and paste the following lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Install Meilisearch
curl -L https://install.meilisearch.com | sh

# Launch Meilisearch
./meilisearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2. Creating and setting up your Rails app
&lt;/h2&gt;

&lt;p&gt;Now that you've got Meilisearch up and running, let's create our RoR app.‌‌ We will create a simple recipe app named &lt;code&gt;delicious_meals&lt;/code&gt;. Run the following command on the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new delicious_meals -j esbuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‌Let's generate our model &lt;code&gt;Recipe&lt;/code&gt;. It will have four attributes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;    &lt;code&gt;title&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;    &lt;code&gt;ingredients&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;    &lt;code&gt;directions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;    &lt;code&gt;diet&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go into the project folder and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/rails g model Recipe title:string ingredients:text directions:text diet:string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command also generates the migration file inside the &lt;code&gt;db/migrate&lt;/code&gt; directory. Let's add the &lt;code&gt;null: false&lt;/code&gt; option next to each column of the table so that no recipe is saved to the database if a field is empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateRecipes &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :recipes do |t|
      t.string :title, null: false
      t.text :ingredients, null: false
      t.text :directions, null: false
      t.string :diet, null: false

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;timestamps&lt;/code&gt; column method adds two extra fields to the table: &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can now create the database and run the migration above with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Creates the database 
bin/rails db:create 

# Runs the migration 
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you need to generate the controller with its &lt;code&gt;index&lt;/code&gt; action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/rails g controller Recipes index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will use the &lt;code&gt;index&lt;/code&gt; view to show our recipes and search through them with our search bar. We won't generate the rest of the CRUD actions, as it would exceed the purpose of this tutorial.&lt;/p&gt;

&lt;p&gt;Once the controller is created, modify the &lt;code&gt;config/routes.rb&lt;/code&gt; file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.routes.draw do
  # Maps requests to the root of the application to the index action of the 'Recipes controller'
  root "recipes#index"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the &lt;code&gt;root&lt;/code&gt; route is mapped to the &lt;code&gt;index&lt;/code&gt; action of &lt;code&gt;RecipesController&lt;/code&gt;. That way, the content of &lt;code&gt;app/views/recipes/index.html.erb&lt;/code&gt; will be rendered at the root of your application.&lt;/p&gt;

&lt;p&gt;You can check that everything is working as intended by starting the application with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser window and navigate to &lt;code&gt;http://127.0.0.1:3000&lt;/code&gt;. You should see your index view displaying a message such as:&lt;/p&gt;

&lt;h1&gt;Recipes#index&lt;/h1&gt;

&lt;p&gt;Find me in app/views/recipes/index.html.erb&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3. Adding Meilisearch to your app
&lt;/h2&gt;

&lt;p&gt;Now that we have the back-end basics of our application, let's connect it to our running Meilisearch instance using the &lt;code&gt;meilisearch-rails&lt;/code&gt; gem.&lt;/p&gt;

&lt;p&gt;Install it by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle add meilisearch-rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 When writing this tutorial, the latest version of the gem was 0.7.1. You can check the latest version in the &lt;a href="https://github.com/meilisearch/meilisearch-rails"&gt;meilisearch-rails GitHub repository&lt;/a&gt; or on &lt;a href="https://rubygems.meilisearch.com/?q=meilisearch-rails"&gt;Meilisearch finds rubygems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;code&gt;meilisearch.rb&lt;/code&gt; inside the &lt;code&gt;config/initializers/&lt;/code&gt; folder to setup your &lt;code&gt;MEILISEARCH_HOST&lt;/code&gt; and &lt;code&gt;MEILISEARCH_API_KEY&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch config/initializers/meilisearch.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have followed step 1 to the letter, your Meilisearch host should be &lt;code&gt;http://127.0.0.1:7700&lt;/code&gt;. Since we did not set any API key, we will comment out the line with the &lt;code&gt;meilisearch_api_key field&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MeiliSearch::Rails.configuration = {
    meilisearch_host: 'http://127.0.0.1:7700',
    # meilisearch_api_key: ''
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 You will need a master or a private key in production, you can learn more about it &lt;a href="https://docs.meilisearch.com/reference/features/authentication.html"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you did set a master key, you have to update your configuration accordingly before running Meilisearch (see step 1).&lt;/p&gt;

&lt;p&gt;Let's open our &lt;code&gt;app/models/recipe.rb&lt;/code&gt; file and add the following line inside the &lt;code&gt;Class&lt;/code&gt; declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;include MeiliSearch::Rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to add a meilisearch block. Note that the settings inside the Meilisearch block are not mandatory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Recipe &amp;lt; ApplicationRecord
    include MeiliSearch::Rails

    meilisearch do
        # all attributes will be sent to Meilisearch if block is left empty
        displayed_attributes [:id, :title, :ingredients, :directions, :diet]
        searchable_attributes [:title, :ingredients, :directions, :diet]
        filterable_attributes [:diet]
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each line of code:&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting displayed attributes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;displayed_attributes [:id, :title, :ingredients, :directions, :diet]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, Meilisearch displays all the attributes. Here, we are instructing Meilisearch to only display the specified attributes in the search response, this setting prevents Meilisearch from displaying the &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; fields.&lt;/p&gt;

&lt;p&gt;👉 You can learn more about &lt;code&gt;displayed attributes&lt;/code&gt; in our &lt;a href="https://docs.meilisearch.com/reference/features/field_properties.html#displayed-fields"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting searchable attributes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;searchable_attributes [:title, :ingredients, :directions, :diet]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above line of code, we are doing two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We first tell Meilisearch to only search among the specified attributes when performing a search query. So it won't try to find matches in the &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; fields.&lt;/li&gt;
&lt;li&gt;We are also specifying the order of importance of the attributes. We are telling Meilisearch that a document with a matching query word found in its &lt;code&gt;title&lt;/code&gt; is more relevant than a document with a matching query word found in &lt;code&gt;directions&lt;/code&gt;. The first document is more relevant and is returned first in the search results.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 Learn more about &lt;code&gt;searchable fields&lt;/code&gt; in our &lt;a href="https://docs.meilisearch.com/learn/configuration/displayed_searchable_attributes.html#searchable-fields"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting filterable attributes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filterable_attributes [:diet]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we are telling Meilisearch that we want to be able to &lt;strong&gt;refine our search results&lt;/strong&gt; based on the &lt;code&gt;diet&lt;/code&gt; type. That will allow us to search only for vegetarian recipes, for instance.&lt;/p&gt;

&lt;p&gt;👉 Visit our &lt;a href="https://docs.meilisearch.com/reference/features/filtering_and_faceted_search.html"&gt;documentation&lt;/a&gt; to know more about filtering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4. Seeding the database
&lt;/h2&gt;

&lt;p&gt;To test our application, we need some data in our database. The quickest way is to populate the database with dummy data using a gem called &lt;em&gt;&lt;a href="https://github.com/faker-ruby/faker"&gt;faker&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Add the following line to your &lt;code&gt;Gemfile&lt;/code&gt; inside the development group, save and run &lt;code&gt;bundle install&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'faker', :git =&amp;gt; 'https://github.com/faker-ruby/faker.git', :branch =&amp;gt; 'master' 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open the &lt;code&gt;./db/seeds.rb&lt;/code&gt; file and add the following code to populate your database with 1000 recipes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Deletes existing recipes, useful if you seed several times
Recipe.destroy_all

# Creates 1000 fake recipes
1000.times do
    Recipe.create!(
        title: "#{Faker::Food.dish} by #{Faker::Name.unique.name}",
        ingredients: "#{Faker::Food.ingredient}, #{Faker::Food.ingredient}, #{Faker::Food.ingredient}",
        directions: Faker::Food.description,
        diet: ['omnivore', 'pescetarian', 'vegetarian', 'vegan'].sample
    )
end 

# Displays the following message in the console once the seeding is done
puts 'Recipes created'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run &lt;code&gt;bin/rails db:seed&lt;/code&gt; in the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5. Testing search with the search preview
&lt;/h2&gt;

&lt;p&gt;Meilisearch delivers an out-of-the-box web interface to test it interactively. Open your browser and go to the Meilisearch HTTP address, which should be &lt;code&gt;http://127.0.0.1:7700&lt;/code&gt;, unless you specified it otherwise &lt;a href="https://docs.meilisearch.com/learn/configuration/instance_options.html#http-address-port-binding"&gt;at launch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;👉 Adding documents to an index is an asynchronous operation, don't worry if you don't see the 1000 documents right away. It might take some time for the updates to process. Learn more about asynchronous updates &lt;a href="https://docs.meilisearch.com/learn/advanced/asynchronous_updates.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure the &lt;code&gt;Recipe&lt;/code&gt; index is selected in the menu located at the top right, next to the search bar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SHxqBFTH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94bg2j6d1yvty9xkroag.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SHxqBFTH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94bg2j6d1yvty9xkroag.gif" alt="Meilisearch mini dashboard" width="880" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the data has been automatically added to our Meilisearch instance. The only visible and searchable attributes are the ones &lt;a href="https://blog.meilisearch.com/how-to-integrate-an-extremely-fast-and-relevant-search-into-your-rails-app-using-meilisearch-and-react/#setting-displayed-attributes"&gt;we specified in our model file&lt;/a&gt; inside the &lt;code&gt;meilisearch block&lt;/code&gt;. Please note that your search results may be different from those shown in the GIF, since faker randomly generates data.&lt;/p&gt;

&lt;p&gt;This is great for testing Meilisearch and some of its features, but it doesn't showcase the &lt;code&gt;filterable_attributes&lt;/code&gt; we specified in our block. We need a custom UI for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6. Adding React to the Rails app
&lt;/h2&gt;

&lt;p&gt;There are several ways of using ReactJS with Rails. We have chosen the most straightforward one: installing it as a JavaScript dependency in our Rails application.&lt;/p&gt;

&lt;p&gt;Run the following command to install &lt;strong&gt;ReactJS&lt;/strong&gt; and its &lt;strong&gt;react-dom&lt;/strong&gt; package for working with the DOM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create the folders and files for our React code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir app/javascript/recipes 
touch app/javascript/recipes/index.jsx 
touch app/javascript/recipes/App.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's open &lt;code&gt;app/javascript/recipes/index.jsx&lt;/code&gt; and add the required code to render our React elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
const root = createRoot(container); 
root.render(&amp;lt;App/&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;app/javascript/application.js&lt;/code&gt; and import the file we just created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "./recipes"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7. Integrating a front-end search bar
&lt;/h2&gt;

&lt;p&gt;To integrate a front-end search bar, you need to install two packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://github.com/algolia/react-instantsearch"&gt;React InstantSearch&lt;/a&gt;&lt;/strong&gt;: an open-source library that provides all the front-end tools you need to customize your search bar environment&lt;/li&gt;
&lt;li&gt;    &lt;strong&gt;&lt;a href="https://github.com/meilisearch/instant-meilisearch"&gt;Instant Meilisearch&lt;/a&gt;&lt;/strong&gt;: the Meilisearch client to establish the communication between your Meilisearch instance and the React InstantSearch library
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add react react-dom react-instantsearch-dom @meilisearch/instant-meilisearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now open your &lt;code&gt;app/javascript/recipes/App.jsx&lt;/code&gt; file and replace the existing code with the code from the &lt;code&gt;meilisearch-react&lt;/code&gt; &lt;a href="https://github.com/meilisearch/meilisearch-react/#getting-started"&gt;Getting Started&lt;/a&gt; guide. We only need to modify the &lt;code&gt;searchClient&lt;/code&gt; with our Meilisearch host and Meilisearch API key, as well as the &lt;code&gt;indexName&lt;/code&gt;. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react"
import { InstantSearch, Highlight, SearchBox, Hits } from 'react-instantsearch-dom';
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch';

const searchClient = instantMeiliSearch(
  "http://127.0.0.1:7700", // Your Meilisearch host
  "" // Your Meilisearch API key, if you have set one
);

const App = () =&amp;gt; (
  &amp;lt;InstantSearch
    indexName="Recipe" // Change your index name here
    searchClient={searchClient}
  &amp;gt;
    &amp;lt;SearchBox /&amp;gt;
    &amp;lt;Hits hitComponent={Hit} /&amp;gt;
  &amp;lt;/InstantSearch&amp;gt;
);

const Hit = ({ hit }) =&amp;gt; &amp;lt;Highlight attribute="title" hit={hit} /&amp;gt;

export default App

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, go to your &lt;code&gt;views&lt;/code&gt; folder and replace the content of the &lt;code&gt;app/views/recipes/index.html.erb&lt;/code&gt; with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="app"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run the &lt;code&gt;bin/dev&lt;/code&gt; command, open your browser and navigate to &lt;code&gt;http://127.0.0.1:3000&lt;/code&gt; and see the result:‌&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hX6u3e5I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1oivxbr4czw4v6yj5upf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hX6u3e5I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1oivxbr4czw4v6yj5upf.gif" alt="Typing salad in an unstyled search bar, the results are updated as you type" width="880" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, the search works, but it's not very pretty. Luckily, InstantSearch provides a &lt;a href="https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react/#loading-the-theme"&gt;CSS theme&lt;/a&gt; you can add by inserting the following link into the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; element of your &lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.4.5/themes/satellite-min.css" integrity="sha256-TehzF/2QvNKhGQrrNpoOb2Ck4iGZ1J/DI4pkd2oUsBc=" crossorigin="anonymous"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also customize the widgets or create your own if you want to. Check the &lt;a href="https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react/"&gt;React InstantSearch documentation&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;Let's check the rendering:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VvLNS3jx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnvmi7f1h2u3xnl5qpax.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VvLNS3jx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnvmi7f1h2u3xnl5qpax.gif" alt="Typing chicken in a styled search bar, the results are updated as you type" width="880" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not bad, right? But once again we lack the possibility of filtering the results by type of diet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8. Adding faceted search
&lt;/h2&gt;

&lt;p&gt;It's as simple as importing the &lt;code&gt;RefinementList&lt;/code&gt; widget in your &lt;code&gt;index.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { InstantSearch, Highlight, SearchBox, Hits, RefinementList } from 'react-instantsearch-dom';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and adding it inside our &lt;code&gt;InstantSearch&lt;/code&gt; widget, specifying the attribute we want to filter by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;RefinementList attribute="diet" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it more aesthetic and practical, let's create two &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements to divide our components. On the left, we'll find the filters, and the search bar and results on the right.&lt;/p&gt;

&lt;p&gt;You can also add a "Type of diet" heading along with the &lt;code&gt;ClearRefinements&lt;/code&gt; widget. It allows you to clear all the filters just by clicking on it, instead of having to uncheck them one by one.&lt;/p&gt;

&lt;p&gt;The file should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react"
import { InstantSearch, Highlight, SearchBox, Hits, RefinementList, ClearRefinements } from 'react-instantsearch-dom';
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch';

const searchClient = instantMeiliSearch(
  "http://127.0.0.1:7700",
  ""
);

const App = () =&amp;gt; (
  &amp;lt;InstantSearch
    indexName="Recipe" // Change your index name here
    searchClient={searchClient}
  &amp;gt;
    &amp;lt;div className="left-panel"&amp;gt;
      &amp;lt;ClearRefinements /&amp;gt;
      &amp;lt;h2&amp;gt;Type of diet&amp;lt;/h2&amp;gt;
      &amp;lt;RefinementList attribute="diet" /&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div className="right-panel"&amp;gt;
      &amp;lt;SearchBox /&amp;gt;
      &amp;lt;Hits hitComponent={Hit} /&amp;gt;
    &amp;lt;/div&amp;gt;

  &amp;lt;/InstantSearch&amp;gt;
);

const Hit = ({ hit }) =&amp;gt; &amp;lt;Highlight attribute="title" hit={hit} /&amp;gt;

export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this to work we need to add some CSS. Let's create a &lt;code&gt;app/assets/stylesheets/recipes.css&lt;/code&gt; file and add the following lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.right-panel {
    margin-left: 210px;
}

.left-panel {
    float: left;
    width: 200px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just to make it even prettier, let's add some padding and margin to the body and the search bar, and change the font:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* app/assets/stylesheets/recipes.css */

body { 
    font-family: sans-serif; 
    padding: 1em; 
}

.ais-SearchBox { 
    margin: 1em 0; 
}

.right-panel {
    margin-left: 210px;
}

.left-panel {
    float: left;
    width: 200px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ByE212Bk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lh69wqlmrgzd9pf1cf5m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ByE212Bk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lh69wqlmrgzd9pf1cf5m.gif" alt="Typing salad in an styled search bar with type of diet filters on the left, the results are updated as you type" width="880" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And tada! 🎉  You have a nice search bar with a search-as-you-type experience ready! 🥳&lt;/p&gt;

&lt;p&gt;⚠️ Because we used fake data to seed our database, the recipes' &lt;code&gt;title&lt;/code&gt;s, &lt;code&gt;ingredients&lt;/code&gt;, &lt;code&gt;directions&lt;/code&gt;, and &lt;code&gt;diet&lt;/code&gt; type are not necessarily consistent.&lt;br&gt;
Conclusion&lt;/p&gt;

&lt;p&gt;We have learned how to synchronize our Ruby on Rails database with Meilisearch and customize our search settings directly on our Rails app, allowing us to search through our data in milliseconds. To top it all off, we have also created a faceted search interface with a search-as-you-type experience using React.&lt;/p&gt;

&lt;p&gt;We have achieved all this seamlessly, thanks to &lt;a href="https://github.com/meilisearch/meilisearch-rails"&gt;Meilisearch Rails&lt;/a&gt; and &lt;a href="https://github.com/meilisearch/instant-meilisearch"&gt;Instant Meilisearch&lt;/a&gt;. Meilisearch has integrations for almost every popular language or framework. Take a look at the complete list in the &lt;a href="https://github.com/meilisearch/integration-guides/"&gt;Meilisearch integration guides&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions, please join us on &lt;a href="https://slack.meilisearch.com/"&gt;Slack&lt;/a&gt;; we are always happy to hear from you. For more information on Meilisearch, check out our &lt;a href="https://github.com/meilisearch/meilisearch/"&gt;Github repository&lt;/a&gt; and &lt;a href="https://docs.meilisearch.com/"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>meilisearch</category>
      <category>react</category>
      <category>rails</category>
    </item>
    <item>
      <title>How to integrate a relevant search into Strapi v4 using Meilisearch</title>
      <dc:creator>Carolina</dc:creator>
      <pubDate>Mon, 13 Jun 2022 16:42:25 +0000</pubDate>
      <link>https://forem.com/meilisearch/how-to-integrate-a-relevant-search-into-strapi-v4-using-meilisearch-189l</link>
      <guid>https://forem.com/meilisearch/how-to-integrate-a-relevant-search-into-strapi-v4-using-meilisearch-189l</guid>
      <description>&lt;p&gt;This tutorial will show you how to integrate Meilisearch with Strapi v4 to create a search-based web app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing our tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Strapi
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://strapi.io/"&gt;Strapi&lt;/a&gt; is a headless CMS. It provides a quick way to create a back-end where we can add and store data. The data is then available through a REST or GraphQL API without having to code anything or configure our database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meilisearch
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.meilisearch.com/learn/what_is_meilisearch/overview.html#what-is-meilisearch"&gt;Meilisearch&lt;/a&gt; is a fast, open-source search engine that's easy to use and deploy. The aim of Meilisearch is to create an out-of-the-box relevant search experience in very few steps, with no configuration needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To be able to follow this tutorial, you'll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An open &lt;a href="https://www.ionos.com/help/email/troubleshooting-mail-basicmail-business/access-the-command-prompt-or-terminal"&gt;terminal or command prompt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/download/"&gt;Node.js&lt;/a&gt; &amp;gt;= 12 &amp;lt;= 16&lt;/li&gt;
&lt;li&gt;NPM and NPX (v6 only) (installed with Node.js): package managers that help you access and use JS libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a directory for our app
&lt;/h2&gt;

&lt;p&gt;Let’s create a directory called &lt;code&gt;my-app&lt;/code&gt; where we will add our back and front-end parts of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a back-end using Strapi
&lt;/h2&gt;

&lt;p&gt;Our first step is to generate a back-end API using Strapi. Go to your open terminal window and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-strapi-app@latest back &lt;span class="nt"&gt;--quickstart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a Strapi app in a new directory called &lt;code&gt;back&lt;/code&gt; and opens the admin dashboard. Create an account or log in so that you have access to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---w4a46Yi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--11.27.18-1.png" class="article-body-image-wrapper"&gt;&lt;img alt="Strapi signup form" src="https://res.cloudinary.com/practicaldev/image/fetch/s---w4a46Yi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--11.27.18-1.png" width="880" height="1243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have created your account, you should be redirected to Strapi's admin dashboard. This is where we will configure our back-end API.&lt;/p&gt;

&lt;p&gt;Our next step is to create a new collection type. A collection is like a blueprint of your content—in this case, it'll be a collection of restaurants. We will create another collection called "Category" to organize our restaurants later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w9I9txAd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--11.34.17-2--1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w9I9txAd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--11.34.17-2--1.png" alt="Capture-d-e-cran-2022-04-08-a--11.34.17-2--1" width="880" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To follow along, complete the first 4 steps of "Part B: Build your content" from &lt;a href="https://docs.strapi.io/developer-docs/latest/getting-started/quick-start.html"&gt;Strapi's quick start guide&lt;/a&gt;. These will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating collection types&lt;/li&gt;
&lt;li&gt;creating new entries&lt;/li&gt;
&lt;li&gt;setting roles &amp;amp; permissions&lt;/li&gt;
&lt;li&gt;publishing the content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After finishing step 4 of Strapi's quick start guide, two new collections named &lt;code&gt;Restaurant&lt;/code&gt; and &lt;code&gt;Category&lt;/code&gt; should have appeared under &lt;code&gt;Content Manager&lt;/code&gt; &amp;gt; &lt;code&gt;Collection Types&lt;/code&gt;. If we click on &lt;code&gt;Restaurant&lt;/code&gt;, we can see that there is only one. Let's add more by clicking the &lt;code&gt;+ Create new entry&lt;/code&gt; button in the upper-right corner of the dashboard!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8smCQv4l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.27.41---copie-1--1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8smCQv4l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.27.41---copie-1--1.png" alt="Capture-d-e-cran-2022-04-08-a--18.27.41---copie-1--1" width="880" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll add three restaurants, one by one. For each restaurant, you need to press &lt;code&gt;Save&lt;/code&gt; and then &lt;code&gt;Publish&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;The Butter Biscotte.&lt;/code&gt; Description: &lt;code&gt;All about butter, nothing about health.&lt;/code&gt;
Let’s add the &lt;code&gt;French food&lt;/code&gt; category on the bottom right corner of the page 👇&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pYZVqc7R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.33.44-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pYZVqc7R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.33.44-2.png" alt="Capture-d-e-cran-2022-04-08-a--18.33.44-2" width="880" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Name: &lt;code&gt;The Slimy Snail&lt;/code&gt; Description: &lt;code&gt;Gastronomy is made of garlic and butter.&lt;/code&gt; Category: &lt;code&gt;French food&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name: &lt;code&gt;The Smell of Blue&lt;/code&gt; Description: &lt;code&gt;Blue Cheese is not expired, it is how you eat it. With a bit of butter and a lot of happiness.&lt;/code&gt; Category: &lt;code&gt;French food&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the three restaurants have been added, you should end up with the following page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fyBOVUC4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.42.35-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fyBOVUC4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.42.35-1.png" alt="Capture-d-e-cran-2022-04-08-a--18.42.35-1" width="880" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Strapi back-end is now up and running! Strapi automatically creates a REST API for our &lt;code&gt;Restaurants&lt;/code&gt; collection. Check out Strapi's documentation for all available &lt;a href="https://strapi.io/documentation/developer-docs/latest/developer-resources/content-api/content-api.html#api-endpoints"&gt;API endpoints&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, it's time to bring Meilisearch into the mix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch and start Meilisearch
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to &lt;a href="https://docs.meilisearch.com/learn/getting_started/installation.html#download-and-launch"&gt;download and run a Meilisearch instance&lt;/a&gt;, we will be using &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; here.&lt;/p&gt;

&lt;p&gt;Want to avoid a local installation? To quickly create a best-in-class search experience, we offer the convenience of &lt;a href="https://cloud.meilisearch.com/"&gt;Meilisearch Cloud&lt;/a&gt;, a hosted and fully‑managed version of Meilisearch. If you would like to try it, you can still &lt;a href="https://meilisearch.typeform.com/to/FtnzvZfh?typeform-source=www.meilisearch.com"&gt;register on the waiting list&lt;/a&gt; or wait for the public release planned a few weeks from now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open a second terminal window&lt;/strong&gt; and move to the &lt;code&gt;my-app&lt;/code&gt; directory. This window will be used to run Meilisearch. Execute the command below to download and install Meilisearch with Docker. If Docker doesn't work for you, consider trying one of the &lt;a href="https://docs.meilisearch.com/learn/getting_started/installation.html#download-and-launch"&gt;many other methods&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fetch the latest version of Meilisearch image from DockerHub&lt;/span&gt;
docker pull getmeili/meilisearch:latest

&lt;span class="c"&gt;# Launch Meilisearch&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 7700:7700 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data.ms:/data.ms &lt;span class="se"&gt;\&lt;/span&gt;
    getmeili/meilisearch:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a big chunk of text (including some ASCII art) confirming that Meilisearch is up and running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uUuEohIV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.44.16-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uUuEohIV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.44.16-1.png" alt="Capture-d-e-cran-2022-04-08-a--18.44.16-1" width="880" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find the line titled &lt;code&gt;Server listening on&lt;/code&gt;. This lets us know where Meilisearch is being served. Once you've found it, open this address in your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9pdic7TI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.47.49-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9pdic7TI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--18.47.49-1.png" alt="Capture-d-e-cran-2022-04-08-a--18.47.49-1" width="880" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the Meilisearch &lt;a href="https://docs.meilisearch.com/learn/what_is_meilisearch/search_preview.html"&gt;search preview&lt;/a&gt;. It will remain empty until we add some data to our Meilisearch instance.&lt;/p&gt;

&lt;p&gt;It’s time to connect Strapi and Meilisearch and start searching through our data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect Strapi and Meilisearch
&lt;/h2&gt;

&lt;p&gt;In order to add the Meilisearch plugin to Strapi, we need to quit our Strapi app. Go to the terminal window running Strapi (&lt;em&gt;make sure it's not the one running Meilisearch!&lt;/em&gt;) and push &lt;code&gt;Ctrl+C&lt;/code&gt; to kill the process.&lt;/p&gt;

&lt;p&gt;Once you've done that, install the plugin in the &lt;code&gt;back&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd back
npm install strapi-plugin-meilisearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the installation, we have to rebuild our Strapi app before starting it again in development mode, since it makes configuration easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run build
npm run develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, our Strapi app should be running once again on the default address: &lt;a href="http://localhost:1337/admin/"&gt;http://localhost:1337/admin/&lt;/a&gt;. Visiting it in your browser, you should see an admin sign-in page; enter the same credentials as before.&lt;/p&gt;

&lt;p&gt;Once connected, you should see the new &lt;code&gt;meilisearch&lt;/code&gt; plugin on the left side of the screen. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9oN7ID5V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--19.01.53-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9oN7ID5V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--19.01.53-1.png" alt="Capture-d-e-cran-2022-04-08-a--19.01.53-1" width="880" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add your Meilisearch credentials, click on the &lt;code&gt;Settings&lt;/code&gt; tab on the meilisearch plugin page so that Strapi knows where to send our data.&lt;/p&gt;

&lt;p&gt;For example, using the credentials from the &lt;strong&gt;Launch and start Meilisearch&lt;/strong&gt; section above, add the address serving Meilisearch in the &lt;code&gt;Meilisearch Host&lt;/code&gt; field, then click the &lt;code&gt;Save&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zIQljtP7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--19.06.13-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zIQljtP7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/Capture-d-e-cran-2022-04-08-a--19.06.13-1.png" alt="Capture-d-e-cran-2022-04-08-a--19.06.13-1" width="880" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that we leave &lt;code&gt;Meilisearch API Key&lt;/code&gt; blank because we launched Meilisearch without a &lt;a href="https://docs.meilisearch.com/reference/features/configuration.html#master-key"&gt;master key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now it's time to add your Strapi collection to Meilisearch. In the &lt;code&gt;Collections&lt;/code&gt; tab on the &lt;code&gt;meilisearch&lt;/code&gt; plugin page, you should see the &lt;code&gt;restaurant&lt;/code&gt; and &lt;code&gt;category&lt;/code&gt; content-types.&lt;/p&gt;

&lt;p&gt;By clicking on the checkbox next to &lt;code&gt;restaurant&lt;/code&gt;, the content-type is automatically indexed in Meilisearch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ZtbEdIi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/strapi-gif.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ZtbEdIi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/strapi-gif.gif" alt="strapi-gif" width="880" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the GIF above, the word “Hooked” appears when you click on the restaurant's checkbox in the &lt;code&gt;Collections&lt;/code&gt; tab. This means that each time you add, update or delete an entry in your restaurant content-types, Meilisearch is automatically updated!&lt;/p&gt;

&lt;p&gt;Once the indexing finishes, your restaurants are in Meilisearch. We can now go back to our Meilisearch host address: &lt;a href="http://127.0.0.1:7700"&gt;http://127.0.0.1:7700&lt;/a&gt;. After refreshing, you should observe that the page is no longer empty. Try typing “butter” in the search bar and see what happens 😉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SBWJlcJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/butter-gif.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SBWJlcJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.meilisearch.com/content/images/2022/04/butter-gif.gif" alt="butter-gif" width="880" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, your Strapi entries are sent to Meilisearch as is. You can modify the data before sending it to Meilisearch, for instance by removing a field. Check out all the customization options on the &lt;a href="https://github.com/meilisearch/strapi-plugin-meilisearch/#customisation"&gt;strapi-plugin-meilisearch&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Now that you have your Strapi collections in Meilisearch, you can &lt;a href="https://docs.meilisearch.com/reference/api/search.html"&gt;start searching&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Learn how to &lt;a href="https://blog.meilisearch.com/instant-meilisearch/"&gt;set up instant-meilisearch&lt;/a&gt; to integrate a search-as-you-type experience into your front end in no time at all!&lt;/p&gt;

&lt;p&gt;If you have any questions, please join us on &lt;a href="https://meilicommunity.slack.com/"&gt;Slack&lt;/a&gt;. If you like Meilisearch, &lt;a href="https://github.com/meilisearch/meilisearch"&gt;a star on GitHub&lt;/a&gt; means a lot.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>meilisearch</category>
      <category>strapi</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
