Google Custom Search Engine code
This article will outline how I implemented a Google custom search engine (CSE). This is by no means a defacto-standard, and probably doesn’t even represent a best practices method. However, it is how I got it working and didn’t seem to introduce any grievous errors or poor performance. I will try to break down what I did in enough detail so you can better understand what everything does, again, not necessarily a best-of-breed, but its works swimmingly for me. Lets get started..
There are three main sections to the search on two different pages. For the sake of the example, the page with the search box will be index.html and the search results page will be search.html. These pages don’t necessarily have to be separate, in fact the search box will most likely appear on all your pages, including the search page. But either way, the search will be performed somewhere, and redirected to the search page and the result displayed.
First, the search box code. Pretty straight forward, just a basic HTML form with a text box, and submit button. There is hidden field ”d” in there too, I will revisit this later on. For now, just focus on the form action, textfield and submit. I am POSTing this form to the search.html page just because I don’t like query parameters in the url if they don’t need to be there. This is pretty straight forward.
<form action="search.html" method="post"> <input type="text" name="q" value="Search.." /> <input type="hidden" type="hidden" name="d" value="subdir" /> <input type="submit" value="Engage Google Searching!" /> </form>
Alright, now onto the good stuff. I am going to reference numbers that are throughout the following code block to help describe what I did any why. The numbered bullets refer to everything between that number and the subsequent number in the code. Also, and pretty important, I am using the jQuery library, so if you copy and paste the code and it doesn’t work make sure jQuery is included.
The API for this code is located here. It appears that Google has deprecated this API and recommended that we use the “new” custom search engine JSON/Atom API. Odd, since it isn’t even out of Labs yet. So what do we use? The deprecated API that might go away without notice, or the one might change without notice. Yeah.. thanks for making that an easy choice Google.
Starting off Don’t worry about the searchReturned var just yet, I will come back to that. I always encase block of code that deal with AJAX or JSON in try catch block so I can handle errors manually rather than just let something fail and break the whole page.
#1) Standard Google CSE search load, I copied this directly from the admin panel on google.com/cse. setOnLoadCallback is just Google’s way of trigging an event on document load, think $(document).ready() in jQuery world.
#2) setSearchStartingCallback is called when the user initiates the search, so this is where we set flags and additional parameters for the search. In this case, I set setQueryAddition to specify an ‘inurl’ param to the search. inurl basically tells Google that only return results that have the specified string in the URL. Very handy if you need to constrain the search to specific pages in your site. Rather than create new search engines in your CSE admin you can just set this flag. Remember the ‘d’ hidden field I had above? Yep,. this is where it can come into play. Say you have a series of support pages that all contain the string ‘/support/’ in the URL on your site and you want to include a search for just them along with having an overall search engine. Just set the ‘d’ input value to ‘/support/’ and use a mechanism of choice to dynamically write the ‘d’ param value into the javascript. (ie PHP <%echo $_POST['d'] %>
#3) I check for the gs-spelling class because thats the node spelling suggestions are presented in. With the way we are doing the search (with the inurl param) if someone spells something wrong Google will offer a suggestion like:
“Did you mean: blah blah inurl:/foo/“. I didn’t want people seeing the inurl part so I regex replace it out.
#4) I surround this block of code in addition to the previous block because the previous block will not catch an error in this function call. This block is responsible for placing the Next and Previous links before and after the numbered results. I use the try/catch because if there is no cursor attribute, if there are no results returned, accessing the estimateResultCount results in an error.
#5) I want the query to execute as soon as the page is loaded so I write the query param here (ie PHP <%echo $_POST['q'] %>
#6) Finally if there was an error I didn’t catch or if Google never returns a result within 10 seconds, we show an error message. Not sure what would happen if the search result took longer than 10 seconds, it most likely would show the error message then show the results. Someone would probably open a bug on that..
–Edit
Don’t be concerned with the strings inside the “<<>>” double angle brackets, they are meant to be replaced completely by your information, the angle brackets included. So if your query string is “I like dogs”. The query execute line would be: customSearchControl.execute(‘I like dogs’);
var searchReturned = false; try { // #1 google.load('search', '1', {language : 'en'}); google.setOnLoadCallback(function() { var customSearchControl = new google.search.CustomSearchControl('XXXXXXXXXX'); customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET); customSearchControl.setNoResultsString("Your search didn't return any results."); customSearchControl.setSearchStartingCallback(this, function(control, searcher, query) { // #2 searcher.setQueryAddition("inurl:<<searchPath>>"); searcher.setRestriction(google.search.Search.RESTRICT_EXTENDED_ARGS,{ "filter" : "0" }); }); customSearchControl.setSearchCompleteCallback(this, function(control, searcher){ $("#search_preload, .gs-no-results-result").hide(); $("#search_postload").show(); // #3 if($('.gs-spelling').length) { $('.gs-spelling a').html($('.gs-spelling a').html().replace(/inurl:.+/g,"")); } // #4 try { numResults=searcher.cursor.estimatedResultCount; $("#searchResultNum").text(numResults); var cpi = searcher.cursor.currentPageIndex; if (cpi < searcher.cursor.pages.length - 1) { $('#cse .gsc-cursor').append('<div class="gsc-cursor-page">Next</div>').click(function() { searcher.gotoPage(cpi + 1); }); } if (cpi > 0) { $($('#cse .gsc-cursor').prepend('<div class="gsc-cursor-page">Previous</div>').children()[0]).click(function() { searcher.gotoPage(cpi - 1); }); } } catch(e){} window.scrollTo(0,0); searchReturned=true; }); var options = new google.search.DrawOptions(); options.setInput($('#search-box-input')); options.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED); customSearchControl.draw('cse', options); // #5 customSearchControl.execute('<<query>>'); }, true); } catch(err) {} // #6 setTimeout(function(){ if(!searchReturned && $('#cse').length == 0) $("#search_preload").html("<h2 style='color:red;'>There was an error performing your search, please try again later.</h2>"); }, 10000);
The final part of this is the HTML on the same page as the above JS code. Nothing too out of the ordinary happening here.
<div id="search_preload"> <center>Loading search results...<br /> <img src="/images/ajax-loader.gif" /></center> </div> <div id="search_postload" style="display:none;"> <h2>Your search for "<%=query%>" returned <span id="searchResultNum"></span> results</h2> </div> <div id="cse" style="width:100%;"></div>
I found the Google API docs a bit sparse, and there weren’t many articles one implementing this. There you go, hopefully this helped you a little.



#1 by Edison Nica on July 28th, 2011 - 5:45 pm
Hi Jeff,
Check this link with a few examples too: http://googlecustomsearch.appspot.com/element/layoutdemo/index.html
Regards,
Edison
#2 by jeffro on July 28th, 2011 - 5:50 pm
Hey thanks @edison, good stuff in there. I will check it out and see if i can improve my code. Thanks again!
#3 by pratik on January 2nd, 2012 - 7:08 am
Hi Jeff,
I have been trying to add “allintitle:”+user_keyword dynamically when users types keyword and clicks on search. Its not concating and only giving all the results rather than giving keyword found in title only.
Also Edison Nica code for Results-only Layout is not displaying user search box. I think he has hardcoded the values for q=”
Is there a way to dynamically add allintitle with user keyword and then submit.
#4 by pratik on January 3rd, 2012 - 2:53 am
Hi Jeff,
What is the meaning of ‘<>’ u have used for customSearchControl.execute(‘<>’);
Also I have included the below mentioned jquey is it ok
Waiting for a quick and positive response from ur side.
Thanks in Advance.
#5 by jeffro on January 3rd, 2012 - 10:24 am
@pratik
For your first question, don’t try to concatenate inside the customSearchControl.execute() function. You have to set a searcher.setQueryAddition() like I did with the ‘inurl:’, so your line of code would be something like this:
searcher.setQueryAddition("allintitle:<<user_keyword>>");Second question, don’t worry about that, I simply meant for you to replace <<query>> with what-ever your search string is. The same applies to <<searchPath>>, that is meant to be replaced completely with your information.
Hope that helps..
#6 by pratik on January 10th, 2012 - 6:43 am
Hi Jeefro,
Thanks for replying.
1. For <> : I can’t get it. , should I replaceit the the textbox name i.e. q which google CSE Generates.
2. <> : Should I use the textbox variable name.
Thanks in Advance.
Pratik.
#7 by jeffro on January 10th, 2012 - 3:55 pm
No, just replace everything in the quotes with your search string.
customSearchControl.execute('This is what you are searching for');However that text gets in there, is up to you. I don’t know what language you are using to generate this page, PHP, Java, ASP.. what ever, you need to know how to put that string in there. I really can’t explain it more plainly.
#8 by pratik on January 11th, 2012 - 6:33 am
HI Jeff,
I am using PHP.
1. customSearchControl.execute(”); –Will this work.
2. searcher.setQueryAddition(“inurl:<>”); — What should be the searchPath ???
Thanks,
Pratik.
#9 by jeffro on January 11th, 2012 - 10:16 am
1. No, you have to put the search string in the execute function. If your search string comes from a HTTP post param named ‘query’, it would look like this:
customSearchControl.execute('< ?php $_POST['query']; ?>');2. If you aren’t sure what this line does, just take it out. Its not necessary.