05 Oct 2018

Building an instant search result page

Introduction

In this tutorial, you will use Algolia to create an instant search results page where the whole page (results, filters and pagination) is updated as you type, to provide end-users with a lightning fast search experience.

InstantSearch - A 5-Minute overview

Instant search 1

Dataset

[
  {
    "name": "3-Year Unlimited Cloud Storage Service Activation Card - Other",
    "description": "Enjoy 3 years of unlimited Cloud storage service with [...]",
    "brand": "Pogoplug",
    "categories": [
      "Best Buy Gift Cards",
      "Entertainment Gift Cards"
    ],
    "hierarchicalCategories": {
      "lvl0": "Best Buy Gift Cards",
      "lvl1": "Best Buy Gift Cards > Entertainment Gift Cards"
    },
    "type": "Online data backup",
    "price": 69,
    "price_range": "50 - 100",
    "image": "https://cdn-demo.algolia.com/bestbuy/1696302_sc.jpg",
    "url": "http://www.bestbuy.com/site/3-year-unlimited-cloud-storage-service-activation-card-other/1696302.p?id=1219066776306&skuId=1696302&cmp=RMX&ky=1uWSHMdQqBeVJB9cXgEke60s5EjfS6M1W",
    "free_shipping": true,
    "popularity": 10000,
    "rating": 2,
    "objectID": "1696302"
  },
  [...]
]

The sample dataset can be downloaded here here. Learn how to import data into Algolia here here.

Index configuration

This tutorial will teach you how to configure the dataset properly to use in a search UI.

Or, if you already understand index configuration, you can quickly configure your data using the following code:

<?php
// composer autoload
require __DIR__ . '/vendor/autoload.php';

// if you are not using composer
// require_once 'path/to/algoliasearch.php';

$client = new \AlgoliaSearch\Client('YourApplicationID', 'YourAdminAPIKey');

$index = $client->initIndex('your_index_name');
require 'rubygems'
require 'algoliasearch'

Algolia.init(application_id: 'YourApplicationID',
             api_key:        'YourAPIKey')
index = Algolia::Index.new('your_index_name')
// var algoliasearch = require('algoliasearch');
// var algoliasearch = require('algoliasearch/reactnative');
// var algoliasearch = require('algoliasearch/lite');
// import algoliasearch from 'algoliasearch';
//
// or just use algoliasearch if you are using a <script> tag
// if you are using AMD module loader, algoliasearch will not be defined in window,
// but in the AMD modules of the page

var client = algoliasearch('YourApplicationID', 'YourAPIKey');
var index = client.initIndex('your_index_name');
from algoliasearch import algoliasearch

client = algoliasearch.Client("YourApplicationID", 'YourAPIKey')
index = client.init_index('your_index_name')
let client = Client(appID: "YourApplicationID", apiKey: "YourAPIKey")
let index = client.index(withName: "your_index_name")
Client client = new Client("YourApplicationID", "YourAPIKey");
Index index = client.getIndex("your_index_name");
AlgoliaClient client = new AlgoliaClient("YourApplicationID", "YourAPIKey");
Index index = client.InitIndex("your_index_name");
 // Synchronous version
 APIClient client = new ApacheAPIClientBuilder("YourApplicationID", "YourAPIKey").build();

 // Asynchronous version
 AsyncAPIClient client = new AsyncHttpAPIClientBuilder("YourApplicationID", "YourAPIKey").build();

 // Google AppEngine
 APIClient client = new AppEngineAPIClientBuilder("YourApplicationID", "YourAPIKey").build();

 Index index = client.initIndex("index");
import "github.com/algolia/algoliasearch-client-go/algoliasearch"

func main() {
  client := algoliasearch.NewClient("YourApplicationID", "YourAPIKey")
  index := client.InitIndex("your_index_name")
}
// No initIndex
val client = new AlgoliaClient("YourApplicationID", "YourAPIKey")
<?php
$index->setSettings(array(
  "searchableAttributes" => [
    "brand",
    "name",
    "categories",
    "unordered(description)"
  ],
  "customRanking" => [
    "desc(popularity)"
  ]
));
index.set_settings(
  searchableAttributes: [
    'brand',
    'name',
    'categories',
    'unordered(description)'
  ],
  customRanking: [
    'desc(popularity)'
  ]
)
index.setSettings({
  searchableAttributes: [
    'brand',
    'name',
    'categories',
    'unordered(description)',
  ],
  customRanking: ['desc(popularity)'],
});

HTML

To begin with HTML, first create the necessary DOM elements in your HTML document to contain the search results.

<!doctype html>
<head>
  <meta charset="UTF-8">
  <title>InstantSearch Demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.3/dist/instantsearch.min.css">
  <!-- Always use `2.x` versions in production rather than `2` to mitigate any side effects on your website,
  Find the latest version on InstantSearch.js website: https://community.algolia.com/instantsearch.js/v2/guides/usage.html -->
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <header>
    <div>
       <input id="search-input" placeholder="Search for products">
       <!-- We use a specific placeholder in the input to guides users in their search. -->
    </div>
  </header>
  <main>
      <div id="hits"></div>
      <div id="pagination"></div>
  </main>
  <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@2.3/dist/instantsearch.min.js"></script>
  <script src="app.js"></script>
</body>

In this HTML page we included:

  • InstantSearch.js javascript and css
  • the app.js file where we’ll put all javascript
  • a search input
  • a container for search results
  • a container for pagination of search results

We recommend using jsDelivr only for prototyping, not for production applications. Whenever possible, you should host your assets yourself or use a premium CDN service. jsDelivr is a free service and isn’t operated by Algolia, so we won’t be able to provide support if it fails.

Out of the box, InstantSearch.js’s CSS file only contains the bare minimum rules to make it usable, but has no strong styling. To reproduce the tutorial theme, copy the content of the CSS file to your style.css.

Initialize InstantSearch.js

Before beginning, be sure you have the following information:

  • Your search credentials: the Application ID and Search only API Key found in the credentials page in your Algolia Dashboard
  • The name of the index you want to query
var search = instantsearch({
  // Replace with your own values
  appId: 'latency',
  apiKey: '6be0576ff61c053d5f9a3225e2a90f76', // search only API key, no ADMIN key
  indexName: 'instant_search',
  urlSync: true,
  searchParameters: {
    hitsPerPage: 10
  }
});

The urlSync option is now activated. This will keep the browser url in sync and allow users to copy paste urls corresponding to the current search state.

We also asked the API to send us maximum 10 results via the hitsPerPage parameter.

Binding the search input

In the HTML there’s an input to bind to the Algolia index. This means that when a query is made, InstantSearch.js will fetch results from Algolia.

To implement this, you’ll use the searchBox widget, just be certain to specify the search input id in the container option of the widget.

// Add this after the previous JavaScript code
search.addWidget(
  instantsearch.widgets.searchBox({
    container: '#search-input'
  })
);

Displaying results

// Add this after the previous JavaScript code
search.addWidget(
  instantsearch.widgets.hits({
    container: '#hits',
    templates: {
      item: document.getElementById('hit-template').innerHTML,
      empty: "We didn't find any results for the search <em>\"{{query}}\"</em>"
    }
  })
);

You can see here that up to 10 search results will be rendered inside the <div id="hits">.

Instantsearch will use default templates if none are specified. In this case we want to use custom templates. The template can be specified directly (as with the empty template) or can be fetched from the DOM (as with the item template)

<!-- Add this to your HTML document -->
<script type="text/html" id="hit-template">
  <div class="hit">
    <div class="hit-image">
      <img src="{{image}}" alt="{{name}}">
    </div>
    <div class="hit-content">
      <h3 class="hit-price">${{price}}</h3>
      <h2 class="hit-name">{{{_highlightResult.name.value}}}</h2>
      <p class="hit-description">{{{_highlightResult.description.value}}}</p>
    </div>
  </div>
</script>

A few things to note about this hit template:

  • The item template will be used for every hit, using the hit’s data as the rendering context.
  • The special attribute, _highlightResult, contains the same attributes with HTML highlights on the matched words.
  • By default, the API returns the highlighted part between <em> tags (like <em>smart</em>phone}) so that they can be easily styled.
  • You must disable escaping on highlighted values, which can be easily done using three curly-braces {{{html}}}.
  • Our template is wrapped in a <script type="text/html"> tag, so you can “store” some HTML code without rendering it on the page.

Pagination

To add pagination, all you need to do is add a pagination widget, and specify where to display it in the DOM.

// Add this after the other search.addWidget() calls
search.addWidget(
  instantsearch.widgets.pagination({
    container: '#pagination'
  })
);

Start your search UI

Now that all widgets have been added, let’s start the whole interface:

// Add this after all the search.addWidget() calls
search.start();

Going further

This is only the beginning of what can be done with InstantSearch.js. Remember that you can always go further with instant search through a variety of customizations and additional widgets.

Below is the documentation of the widget used in this tutorial

And here you can find a list of additional interesting widgets

What’s Next

Continue building your Algolia knowledge with these tutorials:

© Algolia - Privacy Policy