Following up on Part 1 of this series on adding Google Custom Search to your DotNetNuke skin: Here in Part 2 we'll examine a few of the many options for customizing Google searches as well as ways to influence the ranking of results. As part of the customization we'll learn how to initiate multiple searches at once (and see why you'd want to do that). And just as in Part 1, all of this functionality is delivered for -free- from the Google Search AJAX library...
Parts 1, 2 and 3
In Part 1, we saw how to remove DNN Search out of your skin and replace it with the (free) Google AJAX search. Here, in Part 2, we will examine a few of the many options for customizing Google searches as well as ways to influence the ranking of results. Finally, in Part 3, we'll see how to tie it all in with a Google web account (totally free) so you can define and manage custom engine criteria online and get user search info reported to you via Google Analytics (also totally free).
Recall the Nitty-Gritty
In the final step (2D) outlined in Part 1, we added a JavaScript function "OnLoad()" that instantiated several objects (defined in the Google library file), set options on those objects and then added a page OnLoad event to call the "OnLoad()" function. This step comprised the bulk of the changes made in Part 1 and I told you to just add it all and ignore the "nitty-gritty" details until Part 2. Well, here we are in Part 2 and it's time to get nitty about the gritty.
Recall the function that we added:
function OnLoad()
{
var searchControl = new google.search.SearchControl();
var drawOptions = new google.search.DrawOptions();
drawOptions.setInput(document.getElementById("gss_input"));
var options = new google.search.SearcherOptions();
options.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
options.setRoot(document.getElementById("ContentPane"));
// options.setNoResultsString("No results found."); <-- This option is broken as of 11/10/2008
searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
var siteSearch = new google.search.WebSearch();
siteSearch.setSiteRestriction("YOUR WEBSITE URL GOES HERE");
searchControl.addSearcher(siteSearch, options);
searchControl.draw(null, drawOptions);
}
In the spirit of keeping this closer to a blog entry rather than a textbook, I'll cover only the most interesting points and gloss over the rest.
First up, we create a "Search Control". You have to have it and you only need one. We'll cover a couple of this object's options in a bit.
Next is "DrawOptions". You should know that you can use DrawOptions to have Google auto-insert its own text input field and labels. If you do you'll have little or no control over input field sizing, colors, etc. I prefer to use my own input box so that's what our example does. For our example, then, the only thing we need from "DrawOptions" is to define a link to our input box. We do this via "DrawOptions.setInput()".
Moving on we come to "SearcherOptions". This object tells Google where to deliver our results and how to display them. The next few lines define some of these options:
- setExpandMode: display search results in a collapsed DIV or an expanded DIV (default)
- setRoot: defines what page element/container to insert search results into
- setNoResultsString: sets what message is displayed when there are no search results
NOTE: As of 11/10/2008, the 'setNoResultsString' option causes an error when no search results are returned. I have commented it out of our example, above, until Google fixes the issue in their AJAX library.
Now we jump back to the "Search Control" object. Our example is setting two options:
- setLinktarget: defines whether search links open in the same window or a new window
- setResultSetSize: defines how many results are returned per result page; Currently you may define result sizes of 1, 4 or 8 though you cannot specify the size directly by an integer value. Instead you must use Google's predefined labels. For our example we used "LARGE_RESULTSET" in order to view 8 results per result page.
Finally we get to "WebSearch". This is where things get interesting. Google provides different types of searches including web, blog, book, image, video and more. WebSearch references the standard Google search and is probably what you'll use the most. Even more interesting, though, is the fact that one "Search Control" object can have multiple searches defined for it. That means that your single search input box could generate results for multiple categories at the same time: Web, Images, Blogs, etc. We're going to do exactly this in our next example.
In our Part 1 example, the only "WebSearch" option we used was "setSiterestriction". As you've probably guessed, this option directs Google to restrain its search to the domain specified. This is the option that transforms a standard Google web search to a Google site search.
The last two lines of our function linked our SearcherOptions with our WebSearch and then initiated the "Draw" method to link it all up to our text input. If we had wanted Google to insert it's own input box we would have specified (as the first parameter) a page container element to insert the control into instead of passing a value of "null".
Search Example #2: Multiple Searches at Once
Now that we have an idea of what the core Google search objects do, let's expand our Part 1 example to perform two searches at once. Why two searches? Because we're going to search for different things. Physically different things, that is: one search will return matching web pages from our site and the other search will return matching files stored in our DNN instance's "portals" directory. This is exactly what the example Google AJAX Search does on this page (see the Custom Site Search box in the upper-right corner of the page).
The code below is our same example as before with added functionality. (Unchanged code is in gray to make it easy to see where we're adding new code.)
function OnLoad()
{
var searchControl = new google.search.SearchControl();
var drawOptions = new google.search.DrawOptions();
drawOptions.setInput(document.getElementById("gss_input"));
var options = new google.search.SearcherOptions();
options.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
options.setRoot(document.getElementById("ContentPane"));
options.setNoResultsString("No results found.");
searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
searchControl.setResultSetSize(google.search.Search.SMALL_RESULTSET);
// Set up a site web page search. We're only including "aspx" pages from our site.
// We're specifically excluding "LinkClick" page links (links with "fileticket").
var siteSearch = new google.search.WebSearch();
siteSearch.setSiteRestriction("YOUR WEBSITE URL GOES HERE");
siteSearch.setQueryAddition("+filetype:aspx -fileticket");
siteSearch.setUserDefinedLabel("WEB PAGE search results from YOURSITE");
searchControl.addSearcher(siteSearch, options);
// Set up a site file search. To keep things simple we're only including files
// directly linked from our site's PORTALS directory, not files linked via DNN
// "LinkClick" links.
var fileSearch = new google.search.WebSearch();
fileSearch.setSiteRestriction("YOUR WEBSITE URL GOES HERE");
fileSearch.setSiteRestriction("YOUR WEBSITE URL/portals/YOUR PORTAL ID");
fileSearch.setQueryAddition("-filetype:aspx");
fileSearch.setUserDefinedLabel("FILE search results from YOURSITE");
searchControl.addSearcher(fileSearch, options);
searchControl.draw(null, drawOptions);
}
Our updated code now provides two sets of search results, one following the other. Each search is carried out independantly of the other so you'll often notice one set of results appearing before or after the other set. To make sure we're not returning too many results to comfortably present on our webpage we're also limiting the number of results to 4 per search. We do this via the "setResultSetSize" with a value of "SMALL_RESULTSET". Both sets of searches have independant AJAX-driven pagination so you can advance each set of results independantly of the other without a page referesh.
The first set of results is the site web page results. Note the "setUserDefinedLabel" option we've added. This lets us give the user an indication of the type of result -- very handy when returning mutiple result sets. For our web page results, we've employed the "setQueryAddition" option in order to sneakily add our own search input text to the user's search term(s). You can use this feature to influence user searches. For example, if you want users' search results to skew towards things having to do with bananas, you could use "setQueryAddition" to add "bananas" to their search terms without the users knowing it. This can be extremely useful if you want to give weight to search results featuring a particular topic ("+DNN", "+DOTNETNUKE") or locale ("+EUROPE", "+ITALY").
For our example, we're adding two "setQueryAddition" search directives within the same string:
- "+filetype:aspx": This directs Google to only include results with a filetype of "aspx".
- "-fileticket": This directs Google to exclude any results that include "fileticket". This is optional but for DNN sites it helps to exclude certain page links from our results (we only want actual pages, not links).
The second set of results is the site file results. Although Google provides specialized search objects for Blogs, Books, etc., Google does not (yet) have a search object specifically geared for files. To overcome this, we use the standard Web search object as before and once again turn to the "setQueryAddition" option combined with the "setSiteRestriction" option to restrict our search results. This time around we're ignoring "aspx" files (web pages) and are also restricting our results to files directly linked to our site's PORTALS subdirectory. That is, files stored locally on our DNN server in our portal's file area.
Note that by configuring the fileSearch's "setSiteRestriction" to include the "PORTALS" directory in the path we are excluding results that include DNN's "FlieClick" method -- that's the method used by DNN to allow the counting of user clicks on individual file links. Of course, you could allow the DNN "FileClick" links but keep in mind that a "FileClick" link can link to anything: a local file, remote file, local page, remote page, etc. "FileClick" links are not guaranteed to link only to files. To insure that our File Search results only contain links to acutal files, our example restricts results to files directly linked from the "PORTALS" directory.
Other Display Options
Our two sets of results were shown together in order because we only specified a single page container as the target. We could have specified separate page container targets for the two searches. This would allow us to display the two sets of results side-by-side or even within different tabs of a tabstrip object. See one of Google's examples (including Local, Blog and Web search results): http://www.google.com/uds/samples/apidocs/somewhere-else.html
Going Beyond Our Example
You should find the Goolge AJAX Search documentation to be quite useful in moving beyond the examples we've looked at so far: http://code.google.com/apis/ajaxsearch/index.html
Coming Part 3
In "Part 3" we'll tie our examples in with a (free) Google web account so we can employ online management of our custom search engines as well as access reports on what our users are searching for. Our Google account will also let us define custom search criteria that is hosted at Google (instead of defined inline as we've been doing) so end users will not be able to pick apart our search settings by viewing the page source code.