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:
For those of you who just can't wait to see a working example, there is a rudimentary implementation here:
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.
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:
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:
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:
Although you could implement these requirements yourself, the prudent approach is probably to utilize an existing framework.
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:
Enjoy! As always I'll be happy to answer any questions.
Comments are now closed.
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)
Exist a limit number of queries? Regards.
--Emule 05:47, 16 January 2008 (MST)