Simple Search Engine (JS, HTML & SQL)

Magnifer Glass

In this article will look at how to build a very simple but effective search engine. It can easily be added to any website. So let’s begin.

Search bar (HTML)

Here we have our HTML code for the search bar.

HTML

<ul class="search-master">
    <li>
        <form class="searchForm">
            <input type="text" class="searchInput" />
        </form>

        <ul class="resultBox"></ul>
    </li>
</ul>

We will add a link to jQuery in the head tag of your HTML page because we will need it.

HTML

<script scr="//ajax.aspnetcdn.com/ajax/jquery/jquery-3.2.1.min.js" 
		integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" 
		crossorigin="anonymous"></script>

JavaScript functions (and some optional in C#)

When the user types in the search bar the following JavaScript function is called in the .js file. The function sends an Ajax HTTP GET request to the server and gets all the results that match the terms in the title or description of the product.

JS

$(document).ready(function () {

  $('.searchInput').on('input', function () {
      $.ajax({
          url: "/api/circledmmsg/getproduct?terms=" + $(this).val(),
     
          type: 'GET',
          dataType: 'json',
          success: function (res) {

              if (res != null) {
                  $(".resultBox").empty();
                  console.log(res[0]);
                  if (res.length > 0) {
                      $(".resultBox").append('<li><a class="clear-box"
href="http://example.com/portal/#product?id=' 
+ res[0].id + '">' + res[0].title + '</a></li>');
                  };
  
                  if (res.length > 1) {
                    $(".resultBox").append('<li><a class="clear-box" 
href="http://example.com/portal/#product?id=' 
+ res[1].id + '">' + res[1].title + '</a></li>');
                };

                if (res.length > 2) {
                  $(".resultBox").append('<li><a class="clear-box"
href="http://example.com/portal/#product?id=' 
+ res[2].id + '">' + res[2].title + '</a></li>');
              };
  
              }
  
          }
      });
  });
  
  });

Every product in the returned list from the server has a url link pointing to the display page of the product. These links are added as autocomplete below the search bar. On the index page, the JavaScript function appends the ul element marked with class=”resultBox” with the returned urls and corresponding product titles.

I have limited the max number of results to be displayed in the ul element marked with class=”resultBox” to three. Of course we can set them to as much as we need by adjusting the code.

When the client hits submit on the search bar the following function is called in the .js file. Notice that firstly we must prevent the browser from calling the default form submit. We set our search form to preventDefault().

JS

$(document).ready(function () {
    $(".searchForm").on("submit", function (e) {
        e.preventDefault();
        window.location.href = "http://example.com/portal/#search?query=" + $(".searchInput").val();
    });
});

We use the value of the search bar input with id=”searchInput” to define our search terms for our GET request to the server.

I have a particular set up for this site. The plain HTML page uses window.location.href=”http://ath…” to redirect to a Angular single page application – SPA that uses its ActivatedRoute module to catch a product id or search term from the redirect url parameters. The SPA makes a call to the server and retrieves the product or products. I prefer this set up because it is simple, you can achieve a lot with less code in Angular and the result is very user-friendly. We just need to add plain HTML pages in front of the SPA to compensate for the poor SEO performance of Angular, learn more about this particular set up here.

On my server I use .NET Core 2+. This is an example of two of the functions called by the Angular SPA in a row. Here we are aiming to search for a “group object” – a product collection or a product category first. The great thing about this concept is that “group object” is a loose term and in our application it is user defined. Users can make their own group objects.

C#

 [Route("api/[Controller]/getgroup")]
 [HttpGet]
 public IActionResult GetGroup(string terms) {

   try {

     var segroup = _repository.SearchGroup(terms);

     return Ok(_mapper.Map < IEnumerable < GroupObject > ,
       IEnumerable < GroupObjectViewModel >> (segroup));

   } catch (Exception ex) {

     _logger.LogError($ "Failed to get my search: {ex}");
     return BadRequest("Failed to get results");

   }

 }

 [Route("api/[Controller]/getproduct")]
 [HttpGet]
 public IActionResult GetProduct(string terms) {

   try {

     var sepro = _repository.SearchProduct(terms);

     return Ok(_mapper.Map < IEnumerable < Product > ,
       IEnumerable < ProductViewModel >> (sepro));

   } catch (Exception ex) {

     _logger.LogError($ "Failed to get my search: {ex}");
     return BadRequest("Failed to get results");

   }

 }

If our first search function returns results, the second function getProduct() is not called by the Angular SPA. If the fist function returns a “group object”, it will have a description, title and cover picture among other properties. When the user clicks on the “group object” all the products (or categories) tagged with that “group object” show up. The idea here is to create a clear path for the customer to take and find the most relevant item. We try to achieve that by firstly returning collections of items or “group objects” that most accurately match the search terms. This could be especially useful when the users are not sure what they are looking for or they are typing generic terms. For example when a user types “blue ring” the server will return collections first – things like “Blue Ice Rings 2020 Collection”, “Rings in Ocean Blue Collection”, “Summer Rings Gallery” and etc. This makes more sense than returning a list of all the rings that include the word “blue” in their product page, here this is our fallback option in the case that the first function does not find a “group object”.

Querying the database tables

We use the following stored procedures to query our database for search results. I use a Microsoft SQL Server database.

So we will first query our group object table.

GROUP OBJECT TABLE COLUMNS

Id| Picture| Title| Type| Description|

SQL

CREATE PROCEDURE [dbo].[SearchProcedureGroup]
	@term varchar(100)
AS
	SELECT @term = '%' + RTRIM(@term) + '%';  
	SELECT * from [dbo].[GroupObjects] where Title LIKE @term 
OR Type LIKE @term

Every group object has a type which can be a gallery, collection, category or whatever we may imagine. The type feature gives us an extra level of categorization, it creates a sort of an inception effect. For example the user can view a page with a collection of galleries where each gallery opens another collection of galleries or items.

If the first function with the “group object” query returns nothing the second function is activated and the server will look for items in the product table.

PRODUCT TABLE COLUMNS

Id| Picture| Title| ReferenceNumber| Description| Size| DeliveryPolicy| InstagramTag| GroupObjectsTitle|

SQL

CREATE PROCEDURE [dbo].[SearchProcedureProduct]
	@term varchar(100)
AS
	SELECT @term = '%' + RTRIM(@term) + '%';  
	SELECT * from [dbo].[Products] where Title LIKE @term OR ReferenceNumber LIKE @term OR Description LIKE @term 
OR Size LIKE @term OR InstagramTag LIKE @term

Nowadays I think that it is a good idea to add an Instagram hashtag field to the items of our product table. Just because someone might try to search for your product by the hashtag they saw on social media. Also, product hashtags can give some context to your Instagram wall if you decide to add one to your project. More info on Instagram walls.

This is the final search procedure. If our initial search returned “group objects” and the user clicked on one of them the server will query our product table where the title of the ” group object” is referenced in the GroupObjectsTitle field of the product.

PRODUCT TABLE COLUMNS

Id| Picture| Title| ReferenceNumber| Description| Size| DeliveryPolicy| InstagramTag| GroupObjectsTitle|

SQL

CREATE PROCEDURE [dbo].[SearchProcedureProductByType]
	@term varchar(100)
AS
	SELECT @term = '%' + RTRIM(@term) + '%';  
	SELECT * from [dbo].[Products] where GroupObjectsTitle LIKE @term

This is a simple but powerful search engine that can be easily scaled up. It is defined by the businesses it is serving, it allows them to create clear search pathways for their customers to take.