Universal Search #1 Common Logic

This is the 1st article in a series that covers the steps to add searching to Ghost, Hexo or any general website using APIs provided by various services.

The goal of this series is to eventually create a javascript package that allows users to add searching to their websites by only copy-pasting code snippets and changing some configurations.

I name the series "universal search" because these techniques are meant to work on any website. In reality, some of them require additional plugins to be installed, some of them may only apply to a few platforms. What I am trying to do here is to provide as many options as possible so that users can find at least one solution that works.

Before reading

The latest version of Universal Search is available on Github at https://github.com/artchen/universal-search, feel free to clone and install.

This article discusses my design of the plugin. If you only want to use it without knowing everything behind the scene, you can go ahead to one the of the following tutorials to setup individual search services:

Basic Workflow

  • Register a 3rd-party searching service, get the credentials (API Key, ID, etc).
  • Send Ajax GET requests to some endpoint, get response in JSON format.
  • Parse the response if necessary.
  • Render the results on UI.

Common UI

Though I always advocate original and customized design, this time I am going to use the same common UI for some consistency. This interface is independent of whatever 3rd-party search service we choose.

https://gist.github.com/artchen/4a961c7ac9d6b9da188868c0f29a15d4.js

This markup will be compressed and embedded into the javascript. When the plugin is initialized, the markup will be inserted into <body>.

Common Logic

The javascript part of universal search should take care of 3 main aspects:

  • Query for search results.
  • Parse results.
  • Render UI.

We already decided that the UI should be the same across services. So the following UI interactions should be defined as common functions.

  • onSubmit(event): when the user submit the search form, open modal and query.
  • onNextPage(): when the user request the next page, fetch next page.
  • onPrevPage(): when the user request the previous page, fetch previous page.
  • close(): when the user close the modal, fade out the modal.
  • uiBeforeQuery(): hide the result container, show loading animation.
  • uiAfterQuery(): hide loading animation, scroll result container to top, fade in the results.
  • startLoading(): start the timer for loading bar animation.
  • stopLoading(): stop the timer for loading bar animation.

The query and parsing need to be defined on a per service basis because they apparently have different formats of API and response data. But we can centralize the following functions.

  • onQueryError(queryText, status): empty result container, generate error messages and render in error container.
  • buildResult(url, title, digest): generate html for one result.

Most importantly, there should be init() and destroy():

  • destroy(): unregister event handlers, clear markups.
  • init(): register event handlers.

All the functions listed above are common logics that goes into a super class called SearchService.

Here is the result:

https://gist.github.com/artchen/a24102f119cf67e385db7cfa39a9c812.js

These conclude the high level design and common part of universal search.

You can now proceed to one of the following articles to integrate the search service of your choice: