What Would Google Suggest?

From Jimbojw.com

Jump to: navigation, search
Tip: This is part of the Weekly Web Hack article series. If you like it, please subscribe to my blog. Thanks!

In this first edition of the Weekly Web Hack, I'll explain how Google Suggest operates under the hood, and how you can leverage its architecture to place search suggestions into your own page.

By the end of this article, you should be able to:

  1. Understand Google Suggest's request URL structure and response body,
  2. Roll your own autocomplete implementation utilizing Google's suggest data.

For those of you who just can't wait to see a working example, there is a rudimentary implementation here:

what-would-google-suggest.html

AJAX Request

When you start typing in Google Suggest - or, more accurately, when you finish typing a character - the page issues an XMLHttpRequest (XHR) to retrieve the data. To be precise, it issues a GET query to a URL of this form:

http://www.google.com/complete/search?hl=en&client=suggest&js=true&q={query}

Where {query} is the text entered thus far. So for example, if you had typed "hot", the browser would have issued an Ajax query for:

http://www.google.com/complete/search?hl=en&client=suggest&js=true&q=hot.

By following the above link, you can "replay" the search and look the data directly.

AJAX Response

Continuing the example above, we get the following On-Demand Javascript response from the server.

window.google.ac.sendRPCDone(frameElement, "hot", new Array("hotmail", "hotmail.com", "hotels", "hotels.com", "hotmail.co.uk", "hotwire", "hot topic", "hotmail.com.", "hotmail sign in", "hotel"), new Array("223,000,000 results", "78,600,000 results", "316,000,000 results", "7,340,000 results", "2,340,000 results", "19,800,000 results", "131,000,000 results", "80,800,000 results", "60,700,000 results", "448,000,000 results"), new Array(""));

From this, we can deduce that the google.ac.sendRPCDone method must have a signature somewhat like the following:

function sendRPCDone( frameElement, queryTerm, suggestions, resultCounts );

Where:

  • frameElement is probably a reference to a DOM node to be updated.
  • queryTerm is the string queried for (returned to aid in handling multiple, concurrent, asynchronous calls).
  • suggestions is the list of suggested terms for the query.
  • resultCounts is an Array of result counts for each suggested term.

Emulating Google's Architecture

Since the Google Suggest response is On-Demand Javascript (sometimes referred to as "JavaScript on Demand" or simply JoD) instead of JSON, XML or plain-text, it can be utilized in a cross-domain fashion. In fact, cross-domain usability is the primary benefit of JoD over for AJAX requests. Since Google Suggest uses an XHR to GET the data, and since it resides in the same domain as the originating request, the choice of JoD is somewhat mysterious. Nevertheless, we can use this design choice to our advantage by embedding the content in other, non-google originating pages.

In order to set up a favorable environment for executing the requests in an external page, we need to at least do the following:

  1. Create a window level variable called frameElement (its value is unimportant).
  2. Create a window level variable called google whose value should be an Object.
  3. Create a member variable in the google object called ac, which should also be an Object.
  4. Create a method in the ac Object called sendRPCDone() which has a method signature capable of handling any desired inputs.

A simple snippet of code to do the above might look something like this:

var frameElement;
var google = {
    ac: {
        sendRPCDone: function( frameElement, queryTerm, suggestions ) {
            /* Do something with the queryTerm and/or the suggestions Array */
        }
    }
};

By itself, this code isn't very useful, since it doesn't help with any of the following:

  • Determining when a key has been pressed
  • Submission Throttling (determining if an appropriate amount of time has passed since last keypress so as not to unduly overload the server)
  • Populating a suggest drop-down
  • Handling user interface tasks such as navigating through the populated list

Although you could implement these requirements yourself, the prudent approach is probably to utilize an existing framework.

Putting it to use

Finally, for demonstration purposes, consider this code which is built on the Scriptaculous JavaScript library:

GoogleSuggestAutocompleter = Class.create();
Object.extend(Object.extend(GoogleSuggestAutocompleter.prototype, Autocompleter.Local.prototype), {
    cache: {},
    getUpdatedChoices: function() {
        var tok = this.getToken().toLowerCase();
        if (this.cache[tok]) return this.sendRPCDone( null, tok, this.cache[tok] );
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'http://www.google.com/complete/search?hl=en&client=suggest&js=true&q=' + encodeURIComponent(tok);
        document.body.appendChild(script);    
    },
    sendRPCDone: function( meh, tok, list ) {
        if (!list) return this.updateChoices( '<ul></ul>' );
        if (!this.cache[tok]) this.cache[tok] = list;
        var values = list.collect( function(v) { 
            return '<b>'+v.slice(0, tok.length)+'</b>'+v.slice(tok.length);
        });
        this.updateChoices( '<ul><li>' + values.join('</li><li>') + '</li><ul>' );
    }
});
 
var frameElement, google = {};
 
Event.observe(window, 'load', function(event) {
 
    google.ac = new GoogleSuggestAutocompleter( "searchBox", "searchBoxSuggestions", null, { minChars: 1 } );
    
}, 'false');

Note: This example exhibits Browser-Side Caching in the form of a member variable called cache. Implementors are strongly recommended to perform some kind of caching to reduce load on Google's servers.

The above code fragment creates a new class called GoogleSuggestAutocompleter which extends the Autocompleter.Local class. At window-load time, a single instance is created and assigned to window.google.ac, so that the sendRPCDone() calls in the suggest results will have a proper target.

When the window.google.ac instance is created, it is told to monitor the <input> element with ID "searchBox" and insert updates into a <div> with ID "searchBoxSuggestions".

A simple proof-of-concept demo is available here:

what-would-google-suggest.html

Enjoy! As always I'll be happy to answer any questions.


Comments

Comments are now closed.

Emule said ...

Exist a limit number of queries? Regards.

--Emule 05:47, 16 January 2008 (MST)

Jimbojw said ...

Hi Emule,

I don't know of any limit off-hand, but there may be one that I just don't know about. Not sure :/

--Jimbojw 10:29, 16 January 2008 (MST)