24 June 2009

Implementing a Bing Search Using jQuery

Microsoft Bing

Microsoft Bing

Microsoft launched a successor to it’s Live search engine in June this year and included as part of that roll-out is a very comprehensive API.

Bing certainly raised a few eyebrows by virtue of its name alone (it is allegedly supposed to denote the “sound of found“), but the real point is that it is a worthy contender for Google.  This isn’t to say that I think anything will knock the incumbent of his perch anytime soon, but Bing offers a solid UI and provides a lot of added value that I’ve come to expect from a Microsoft product.

Microsoft has also launched a new site, Discover Bing, that goes into all the details of how Bing works and the decision process behind the creation of it, for those really struggling to understand what the point of all this is.

Given that I’m not entirely happy with search at The Office and seeing a pretty solid API for hooking into Bing’s search engine, my curiosity was piqued and I decided to have a crack at building off it.  Since the API offers a number of formats for returned search results, specifically SOAP, XML or JSON, I figured a good platform to use as part of the development would be jQuery.

Getting Started

The documentation available on Microsoft’s Bing API site details how to get yourself started on the development road.  In short, you need to:

  1. Go to http://bing.com/developers
  2. Sign in with your Windows Live account (or create one first, if necessary)
  3. Fill out the form to “Create an App ID” (a value that enables the API to validate that a request is from a registered Bing application developer)

You will then be issued with a App ID that will form part of your search request.  Now let’s have a go at putting it to use.

Building a Search Function

I’m going to create a new ASP.Net Web Application (you can use whatever you’re comfortable with – MVC, PHP or even plain ‘ol HTML) just because it’s what it’s convenient for me.  I’ll create a new default.aspx page and throw together some HTML to hold the various bits and pieces I’m going to be working with.  Specifically, I want:

  • A search box that I’ll be entering my search terms into
  • A button to fire the search event
  • A region on the page to hold my search results
  • A region on the page to show my hit aggregates (i.e. number of hits, hits currently displayed, etc)
  • A region to act as my result navigation

Having put all this together (or downloaded the source for this demo), I can get stuck into the really meaty stuff: the jQuery code.

The Plumbing

Let’s start by defining some of the core values that will be passed in the query string:

 var AppId = "AppId=<YOUR_APP_ID>";  // Replace this string with the AppId you received from the Bing Developer Center.
 var Query = "Query="
 var Sources = "Sources=Web";
 var Version = "Version=2.0";  // API Version that we want to use.  Unlikely you'll want to change this.
 var Market = "Market=en-nz"; // I'm a Kiwi.  You may want to change your localisation.
 var Options = "Options=EnableHighlighting"; // Highlight our search term text in results
 var WebCount = 10; // How many results to show per page
 var WebOffset = 0; // What page are we on

The key variables here are Query and Sources.  Query is the argument that passes in your search terms (e.g. query=pet+rabbits) and Sources defines whether you want to search for web results, images, news articles, etc.  For the purposes of this demo, I’m sticking to web but you can chooose from any number of source types.

Right.  Let’s make the AJAX call using jQuery:

    function Search() {
        var searchTerms = $('#txtQuery').val().replace(" ", "+");
        var arr = [AppId, Query + searchTerms, Sources, Version, Market, Options, "Web.Count=" + WebCount, "Web.Offset=" + WebOffset, "JsonType=callback", "JsonCallback=?"];
        var requestStr = "http://api.search.live.net/json.aspx?" + arr.join("&");

        $.ajax({
            type: "GET",
            url: requestStr,
            dataType: "jsonp",
            success: function(msg) {
                SearchCompleted(msg);
            },
            error: function(msg) {
                alert("Something hasn't worked\n" + msg.d);
            }
        });
    }

This is a pretty garden-variety AJAX call, except there are a couple of things to point out.

Our variable “searchTerms” is used to take the content of our search text box and replace any spaces with + signs, since the search request (i.e. the URL, in effect) cannot have actual space characters.  Similarly, the requestStr variable concatenates all the individual array elements that hold all our search query string information.

The other important point to note is the dataType value: “jsonp”.  We are making a call from our own environment (my laptop for example) to a third party site and as such need to work around Firefox’s native aversion to cross-site http requests (for the record, I’m not a huge fan of JSONP – it makes me feel icky).

Once our call to Bing is complete, we call the SearchCompleted function, passing in the JSON response as an argument.  This in turn checks that the response does not contain errors and displays the results, as shown by the following code:

    function SearchCompleted(response) {

        var errors = response.SearchResponse.Errors;
        if (errors != null) {
            // There are errors in the response. Display error details.
            DisplayErrors(errors);
        }
        else {
            // There were no errors in the response. Display the Web results.
            DisplayResults(response);
        }
    }

…and:

function DisplayResults(response) {
 $("#result-list").html("");                                 // Clear our existing results
 $("#result-navigation li").filter(".nav-page").remove();    // Remove our navigation
 $("#result-aggregates").children().remove();                // Remove our hit information

 var results = response.SearchResponse.Web.Results;          // Define our web results in a more succinct way

 // Let the user know what they searched for and what the search yielded
 $('#result-aggregates').prepend("<p>Web results for " + response.SearchResponse.Query.SearchTerms + "</p>");
 $('#result-aggregates').prepend("<p id=\"result-count\">Displaying " + StartOffset(results)
 + " to " + EndOffset(results)
 + " of " + parseInt(response.SearchResponse.Web.Total) + "</p>");

 // Create the list of results
 var link = [];                                  // We'll create each link in this array
 var regexBegin = new RegExp("\uE000", "g");     // Look for the starting bold character in the search response
 var regexEnd = new RegExp("\uE001", "g");       // Look for the ending bold character in the search response
 for (var i = 0; i < results.length; ++i) {
 // Step through our list of results and build our list items
 link[i] = "<li><a href=\"" + results[i].Url + "\" title=\"" + results[i].Title + "\">"
 + results[i].Title + "</a>"
 + "<p>" + results[i].Description + "<p>"
 + "<p class=\"result-url\">" + results[i].Url + "</p></li>";

 // Replace our unprintable bold characters with HTML
 link[i] = link[i].replace(regexBegin, "<strong>").replace(regexEnd, "</strong>");
 }
 $("#result-list").html(link.join(''));          // Concatenate our list and add it to our page

 // Set up our result page navigation
 CreateNavigation(response.SearchResponse.Web.Total, results.length);
 }

Apologies for the poor formatting here, by the way.  Let’s step through this code, beginning at the start of the DisplayResults function.

The first three lines clear out any existing results that might have been rendered on our page already.  The variable declaration for “results” laces response.SearchResponse.Web.Results in a more succinct way since we’ll be referring to that value a bit.

The next two lines reiterate our search terms back to the user and tell the user how many results the search yielded.  From here, we set about creating the list of individual results and links back to the pages.

First we create an array variable to hold each result (“var link = []“).  We also define two regular expressions to look for the unprintable formatting codes that denote the start and end of the found search terms.  These would normally be shown in bold, so we’ll replace them with <strong></strong> elements.

Stepping through each result in our collection, we create a new hyperlink whose href property is the result URL, the title (not required, but good practice) and text set to the result (destination page’s) Title.  Following that, we include the result Description in a new paragraph and finally show the actual URL in another paragraph again.  All this is wrapped up in a list item (<li></li>).  Finally, we replace our formatting codes that denote the start and end of the search terms.

Once we’ve iterated through our list of results, we concatenate them and display them on the page.

Now that we have all our results, we want to show the navigation for them.

    function CreateNavigation(totalHits, pageSize) {
        var totalPages = (totalHits / pageSize > 10) ? 10 : parseInt(totalHits / pageSize);
        var nav = [];
        for (var i = 0; i < totalPages; i++) {
            nav[i] = "<li class="nav-page">" + (i + 1) + "</li>";
        }
        $("#result-navigation li:first").after(nav.join(''));

        // Create a listener for the page navigation click event
        $("#result-navigation li.nav-page").click(function() {
            WebOffset = parseInt($(this).html()) - 1;
            Search();
        });

        // Show the navigation!
        $("#result-navigation").show();
    }

By and large, this is pretty simple.  We work out how many pages we have (to a maximum of 10) for display, create an array of list items whose text is the page number, append that to our navigation list and then bind the click event for those new list items.  Now, when we click on one of the list items, our WebOffset value gets updated to the value of the page (you’ll notice we’re subtracting 1 from the value – this is because WebOffset uses a 0-based index) and we call the Search() function again.

There it is.  Our first Bing application.  This is a good candidate for a plugin, although I’m unlikely to find the time.  But if you decide to wrap it up into something a bit more sexy or practical, drop me a line – I’m keen to see different applications.

Download the source for this demo.
Check a working version of this article here.

Tags: , , , , ,

7 Responses to “Implementing a Bing Search Using jQuery”

  1. David says:

    Ahh! Too bad I’m having problems running the live demo it probably won’t take long for me to separate it from ASP stuff but wanted to see a live demo to see how it went.

    Cheers!

  2. Phil says:

    Eek!
    Good point – forgot to post that up as well.

    You can find a working demo at jquerybing.demos.officeacuity.com.

  3. David says:

    Whoa, it’s a really nice demonstration! really like it :)
    Sadly my previous mention of seperating the the html/js I haven’t been able to haha been cleaning the house *sighs*

    cheers :)

  4. Phil says:

    Strictly speaking, the only file you really have to use (apart from the jQuery library itself, of course) is the function.js file.

    That file contains references to the HTML elements, but you can change all those references around as you please to fit your own page – be it PHP, HTML or whatever.

  5. [...] Implementing a Bing Search Using jQuery Source [...]

  6. Cory Mathews says:

    Excellent Post! I found some other implementations but all in php. This is great thanks!

Leave a Reply

Real Time Web Analytics