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. #1 by Edison Nica on July 28, 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. #2 by jeffro on July 28, 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. #3 by pratik on January 2, 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. #4 by pratik on January 3, 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. #5 by jeffro on January 3, 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. #6 by pratik on January 10, 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. #7 by jeffro on January 10, 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. #8 by pratik on January 11, 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. #9 by jeffro on January 11, 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.

  10. #10 by jObick on December 13, 2012 - 7:48 pm

    Hello @jeffro im wondering if it is possible to make a search in a certain site? e.g. ‘www.mysite.com’

    i mean can i make search in my site and the result can only be found in my site?

    Thanks.. 😀

  11. #11 by jeffro on January 27, 2013 - 7:45 pm

    Hi @jObick, if you look at #2 in the code above you will see I add the ‘inurl’ parameter to the search. By adding this to the search you effectively restrict the the search results to a specific domain, just what you want.

    So to use your example, you would put:
    searcher.setQueryAddition("inurl:mysite.com");

    Hope that helps

  12. #12 by fi on May 8, 2013 - 8:34 pm

    Hello, Sir..
    I’ve been trying to implement your codes, but would you mind to send/upload the how the full code look like?
    I’m being confused on how and where to put the code in my file.

    Thanks.. 🙂

  13. #13 by jeffro on May 11, 2013 - 9:06 am

    @fi All the code you need is on the page already. Just put the js at the top and the HTML in the body of the page. I apologize if this is rude but if following the instructions on the page is too complicated this might not be the right place to find help. There is a bit more to implementing this code that just using the code that is on this page, you have to set up the Google CSE backend as well.

    This was just meant to help others that might be looking for alternate ways to implement the CSE frontend code. If you would still like to see the whole page that this code is implemented on send me an email and I will send it to you. (my email is on the ‘More About Me’ page)

  14. #14 by Lennie Gudis on May 12, 2013 - 3:38 pm

    Google’s rise to success was in large part due to a patented algorithm called PageRank that helps rank web pages that match a given search string. When Google was a Stanford research project, it was nicknamed BackRub because the technology checks backlinks to determine a site’s importance.;:

    Our website
    http://homeimprovementstuffs.com

  15. #15 by Félix Redondo Casado on May 28, 2013 - 11:52 am

    Hi Jeff. I have a blog called Arqueocinema.com

    I need improve my CSE to search only in titles.

    ¿Is this posible?

  16. #16 by jeffro on May 28, 2013 - 4:37 pm

    I don’t think that a Google CSE will be your best bet here. You can’t really ‘target’ what Google indexes in a site. The search is thorough, so if a user searches for ‘Narnia’ they are definitely going to get the movie Narnia plus anything associated with it, or that even mentions it.

    If you need a high level of customizability you might check out some of the open source search engines available. I recommend either Sphider or Yioop!. Yioop is really powerful and the most Google-like search engine you are going to find. Sphider is old but easy to implement and maintain. That is if you are using PHP..

  17. #17 by aleksey on June 9, 2013 - 5:45 pm

    hello, can you please modify this code so it adds intitle: infront of search querys ? i tryed but couldnt make it work..

    Loading

    google.load(‘search’, ‘1’, {language : ‘auto’});

    google.setOnLoadCallback(function()
    {
    var customSearchControl = new google.search.CustomSearchControl(‘006590860120993065683:WMX2097392520’);
    customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
    customSearchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);

    customSearchControl.draw(‘cse’);
    }, true);

  18. #18 by jeffro on June 9, 2013 - 5:58 pm

    @aleksey Not 100% sure here, but you might try using the .setQueryAddition(term) function then add your search term again like such

    .setQueryAddition(“intitle:my my query”);

    This would basically be the same as submitting the query “my query intitle:my query” but it should get the results you are looking for.

  19. #19 by Gary Smith (@interfaceplanet) on December 26, 2013 - 1:53 am

    Thanks @jeffro, I have successfully installed your code in my site. Keep writing on these topic.
    Thanks a lot again

  20. #20 by Gary Smith (@interfaceplanet) on December 26, 2013 - 1:57 am

    Gary Smith (@interfaceplanet) :
    Thanks @jeffro, I have successfully installed your code in my site. Your code can be easily setup with .Net coding.
    I have bookmark you for your latest post, Keep writing on.
    Thanks a lot again

  21. #21 by Adam Smith on March 30, 2014 - 11:06 pm

    Oh Thanks a lot Jeff. Your articles are always appreciate by visitors. and this is really gonna help me.

  22. #22 by Ash on April 3, 2015 - 1:03 pm

    Hi Jeff,
    I am trying to number the search results in my custom search page. Could you please help me with that?

  23. #23 by Bill on January 14, 2016 - 6:42 pm

    Hi Jeff,
    Thanks for the wonderful tutorial. I’m wondering what should be the value for CustomSearchControl’s constructor. You have passed XXXXXXXXXX. What it should be ?

  24. #24 by jeffro on January 15, 2016 - 6:43 pm

    Hi Bill! You’d replace that with your Google custom search ID. You get that when you register for your custom search engine. However, checking the docs, much of this code has been deprecated.

(will not be published)