MongoDB search with autocomplete using EJS.

ยท

5 min read

MongoDB is one of the fast-growing databases out there and today we will implement autocomplete and fuzzy search along with a suggestion box. MongoDB has several types of searching methods using what they call operators and you can apply what is best for you. We will be using the autocomplete operator for now. one of the problems we will go through is loading the suggestion box while using EJS.

1. Jqueryui -jQuery UI is a curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library. Whether you're building highly interactive web applications or you just need to add a date picker to a form control, jQuery UI is a perfect choice. JqueryUI

2. EJS - What is the "E" for? "Embedded?" Could be. How about "Effective," "Elegant," or just "Easy"? EJS is a simple templating language that lets you generate HTML markup with plain JavaScript. No religiousness about how to organize things. No reinvention of iteration and control flow. It's just plain JavaScript.

Step 1

We will create a mongodb search index.

When creating a search index you will be asked to configure mapping if you were doing a more general text search you could move forward without configuring anything but when it comes to autocomplete you will need to configure which field you would like to apply the autocompletion to. You can see that I am applying the autocomplete to the movie schema title field. There are some specifications as well. So when it comes to tokenization you will have two options edgeGram and ngram. It is better that you read the documentation for it. I will attach a link below.

  "mappings": {
          "dynamic": false,
          "fields": {
            "title": [
              {
                "type": "autocomplete",
                "tokenization": "edgeGram",
                "minGrams": 3,
                 }
            ]
          }
     }
 }

Step 2

setting up our search aggregation function.

One easy way to come up with these functions would be to do it in the MongoDB aggregation before writing the code completely. In the $search we will be specifying what we are looking for and where we want to look for. In the project, we can tell MongoDB what to return to us and the limit is the number of documents we want to be returned from the search. In the fuzzy object, you will see something that might be confusing but doesnโ€™t worry it is just telling MongoDB to what extent it should look for the query. I wrote this code in a different file and exported it to my routes.

function aggSearch(Model, query) {
 return new Promise(function (resolve) {
         var agg = [
              {
                $search: {
                  autocomplete: {
                    path: "title",
                    query: query,
                    fuzzy: {
                      maxEdits: 1,
                      prefixLength: 1,
                      maxExpansions: 256,
                    },
                  },
                },
              },
              {
                $project: {
                  title: 1,
                  poster: 1,

                },
              },
              {
                $limit: 40,
              },
      ];
      Model.aggregate(agg).then(function (res) {
        resolve(res);
      });
     });
 }

Step 3

Now we will tackle one of the challenging parts. Sending data to the server as the user types. This function will get what the user is typing and send it back to the URL specified. And it will request data in return. If you donโ€™t want to make your own front end you can use jquery-UI to display the data returned for the user. #query is the id of the input element where the user types to search. this code should be written in the client-side javascript.


<link rel="stylesheet"href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"/>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script >

$(function () {         
   $("#query").autocomplete({
      source: function (req, res) {
              $.ajax({
                url: "autocomplete/",
                dataType: "json",
                type: "GET",
                data: req,
                success: function (data) {
                  res(data.result);
                },
                error: function (err) {
                  console.log(err.status);
                        },
              });
         },

      select: function (event, ui) {
              if (ui.item) {
                $("#query").text(ui.item.label);
              }
         },
     });
});

This is great it means most things are ready now. we will just need to make the routes where we will recive and send back data๐Ÿ‘๐Ÿ‘

Step 4

so now that we have prepared the frontend let's work on the server-side where we will set up our autocomplete route. Here we will get what the user is typing and pass it on to our aggregation search function. The function will return the title and poster But we just want the title for suggestion so we will map the result to get the title after getting an array of the titles I will slice it to make it look more compact then send the data back to the frontend.

app.get("/autocomplete/", async (req, res) => {
     var q = req.query["term"];
     const result = await searchFun.aggSearch(movies, q);
     const movie = result.map((item) => {
           return item.title;
     });
         moves = movie.slice(1, 8);
         let data = {
           result: moves,
            };
         res.json(data);
});

Step 5

If you are working on production-level projects you will need to mix together some text search and autocomplete to give you the best results but for now, we will use the aggregation function we implemented for our auto-complete. We did earlier to add fuzzy search to it so we will also see how that turns out. Because we are using EJS we will be rendering the results on a second page. This is the post route where we return the movies.

Type the following code

- 

 app.post("/search", async (req, res) => {
         const q = req.body.query;
          const result = await searchFun.aggSearch(movies, q);
         let pageData = {
           qur: q,
           moved: result,
         };
         res.render("searchp", pageData);
    });

we have successfully implemented our autocomplete and fuzzy search.๐ŸŽˆ๐ŸŽ‰

Key points