Microsoft's search engine Bing, is a getting more popular each day. Microsoft has released a REST API for Bing and we are going to use it to create a custom search engine. Before we go on coding, we need an API key from Bing. Normally you should be able to get one from http://www.bing.com/developers/appids.aspx but at the time this post was written, Bing's site had some issues with most of it's features. The issues should be fixed by the time you read this post though.
To get an API key, you have to click on "Create an AppId" link. If you are already logged in, then you will be presented with a screen requiring you to fill in some info about your application. The information needed is:
Application name
Description
Company name
Country/region
Email address
When you have successfully completed the preceding steps, the Your AppIDs page appears, displaying your new AppID as well as any others you have created previously.
The Challenge
We want to create a search engine that will query the Bing API and will be able to display normal search results and a more link with a drop down menu with more features. The "more" drop down menu will support features like:
Video Search
Translation Service
Spelling Service
Phonebook Service
Answers Service
Images Search
The results should be displayed using AJAX.
Sketch it first
As always, we should first get a paper and a pencil and start drawing. Your favorite image editing program should do the job too. Here is how our application will look like:
Image may be NSFW. Clik here to view.
The Tools
We will be using PHP for the communication between our application with Bing's API. Results and the pager will also be formated serverside. For the dropdown menu, we will be using YUI's button utility. This utility will allow us to transform a select element to a nice skinable dropdown menu.
The Markup
Here is the HTML code needed for our application:
[code lang="html"]<html>
<title>GooBingoo!</title>
<head>
<!-- Individual YUI CSS files -->
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/menu/assets/skins/sam/menu.css">
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/button/assets/skins/sam/button.css">
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/paginator/assets/skins/sam/paginator.css">
<!-- Individual YUI JS files -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/container/container_core-min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/menu/menu-min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/element/element-min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/button/button-min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/paginator/paginator-min.js"></script>
<!-- our style -->
<link rel="stylesheet" type="text/css" href="css/styles.css">
<script type="text/javascript" src="js/scripts.js"></script>
</head>
<body>
<div id="wraper">
<div id="logo"><img src="css/log.png" /></div>
<div id="searchcontrols">
<label id="searchoptions-container">
<input type="text" id="searchbox" />
<select id="searchoptions" name="searchoptions">
<option value="searchoptions-1" selected>Web</option>
<option value="searchoptions-2">Images</option>
<option value="searchoptions-3">Video</option>
<option value="searchoptions-4">Answers</option>
<option value="searchoptions-5">Spell</option>
<option value="searchoptions-6">Translate</option>
<option value="searchoptions-7">Phonebook</option>
</select>
</label>
</div>
<div id="results">
</div>
</div>
</body>
</html>[/code]
Our options are marked in a select element and each option holds a value of SELECT_ID-NUMBER. This is for the button menu utility to understand the markup of our dropdown menu.
The JavaScript
We create the dropdown menu with this code:
[code lang="javascript"]YAHOO.util.Event.onDOMReady(function () {
var Button = YAHOO.widget.Button;
var onMenuRender = function (type, args, button) {
button.set("selectedMenuItem", this.getItem(0));
};
var onSelectedMenuItemChange = function (event) {
var oMenuItem = event.newValue;
this.set("label", ("<em class=\"yui-button-label\">" +
oMenuItem.cfg.getProperty("text") + "</em>"));
switch(oMenuItem.cfg.getProperty("text")){
case "Web":
search('web');
break;
case "Images":
search('image');
break;
case "Video":
search('video');
break;
case "Phonebook":
search('phonebook');
break;
case "Answers":
search('answers');
break;
case "Translate":
search('translate');
break;
case "Spell":
search('spell');
break;
}
};
var oMenuButton1 = new Button({
id: "menubutton-1",
name: "menubutton-1",
label: "<em class=\"yui-button-label\">Select An option</em>",
type: "menu",
menu: "searchoptions",
container: "searchoptions-container"
});
/* Register a "selectedMenuItemChange" event handler that will sync the
Button's "label" attribute to the MenuItem that was clicked.*/
oMenuButton1.on("selectedMenuItemChange", onSelectedMenuItemChange);
});[/code]
Now lets break down the code to explain what we are doing. First we need to create the dropdown menu with our search engine options:
[code lang="javascript"]
//We wait until DOM is ready to be scripted
YAHOO.util.Event.onDOMReady(function () {
//and we create a button
var Button = YAHOO.widget.Button;
//then we need to set the first option of the select element as selected
var onMenuRender = function (type, args, button) {
button.set("selectedMenuItem", this.getItem(0));
};
//When the menu selection changed..we update the value shown
var onSelectedMenuItemChange = function (event) {
var oMenuItem = event.newValue;
this.set("label", ("<em class=\"yui-button-label\">" + oMenuItem.cfg.getProperty("text") + "</em>"));
//Here we go through all options to see which was chosen.
switch(oMenuItem.cfg.getProperty("text")){
case "Web":
search('web');
break;
case "Images":
search('image');
break;
case "Video":
search('video');
break;
case "Phonebook":
search('phonebook');
break;
case "Answers":
search('answers');
break;
case "Translate":
search('translate');
break;
case "Spell":
search('spell');
break;
}
};
var oMenuButton1 = new Button({
id: "menubutton-1",
name: "menubutton-1",
label: "<em class=\"yui-button-label\">Select An option</em>",//We add another option here to display some info for the user
type: "menu", //The type of the button
menu: "searchoptions", //The id of the select element
container: "searchoptions-container" //The id of the container element
});
/* Register a "selectedMenuItemChange" event handler that will sync the
Button's "label" attribute to the MenuItem that was clicked.*/
oMenuButton1.on("selectedMenuItemChange", onSelectedMenuItemChange);
});[/code]
We could skip the switch by just doing: search(oMenuItem.cfg.getProperty("text")) but this way would limit us to the search method only.
The getXMLHTTP method is a simple way to get an XML object and don't depend on browser version or type. So, this method will return an XML object on Firefox,Opera and IE:
[code lang="javascript"]function getXMLHTTP(){
var A;
var msxmlhttp = new Array(
'Msxml2.XMLHTTP.5.0',
'Msxml2.XMLHTTP.4.0',
'Msxml2.XMLHTTP.3.0',
'Msxml2.XMLHTTP',
'Microsoft.XMLHTTP');
for (var i = 0; i < msxmlhttp.length; i++) {
try {
A = new ActiveXObject(msxmlhttp[i]);
} catch (e) {
A = null;
}
}
if(!A && typeof XMLHttpRequest != "undefined")
A = new XMLHttpRequest();
if (!A)
alert('Your browser does not support ajax!');
return A;
}[/code]
The next method is search, which does the actual requests and displays the results on the page:
[code lang="javascript"]var search = function(type,start,end){
var xmlObject = getXMLHTTP();
var url = "bing.php";
url += "?type=" + type;
url += "&q="+document.getElementById('searchbox').value;
if(start && end){
url += "&end=" + end;
url += "&start=" + start;
}
url += "&sid=" + Math.random();
xmlObject.onreadystatechange = function(){
if(xmlObject.readyState == 4){
if(xmlObject.responseText){
document.getElementById('results').innerHTML = xmlObject.responseText;
}else{
document.getElementById('results').innerHTML = "<div id='pager'><img src='css/loading.gif' /></div>";
}
}else{
document.getElementById('results').innerHTML = "<div id='pager'><img src='css/loading.gif' /></div>";
}
}
xmlObject.open("GET",url,true);
xmlObject.send(null);
}
[/code]
This is where we will learn how to use the Bing API. The best thing about this API is that you can use 3 protocols to get the results. The protocols are:
JSON
XML
SOAP
So, you can actually use any way that you are familiar with. In our case, we will be using the JSON protocol. PHP allows us to work with JSON encoded data very easily. For example a typical call to the Bing API with JSON looks like this:
[code lang="php"]
$request ='http://api.search.live.net/json.aspx?Appid=API_KEY&sources=SOURCE&SOURCE.Count=NUMBER_OF_RESULTS&SOURCE.Offset=OFFSET&query=QUERY';
$response = file_get_contents($request);
$jsonobj = json_decode($response);
[/code]
Please note:
API_KEY - The API key you got from Bing's developer center.
SOURCE - What you will be searching for. There is a great number of sources to use. These are:
Ad - Use the Ad SourceType to incorporate advertisements into your application.
Image - The Image SourceType returns a list of images relevant to the query term. Image results contain properties about the image media file such as width, height, and file size, along with the URL for the page that contains the image.
InstantAnswer - The InstantAnswer SourceType enables you to get single, authoritative answers to questions such as: What is 789*12?
MobileWeb - The MobileWeb SourceType returns mobile Web search results - primarily Extensible Hypertext Markup Language (XHTML) or Wireless Markup Language (WML) pages relevant to the queried term.
News - Search news.
Phonebook - The Phonebook SourceType enables you to view details about a business for which you are searching as if they were a phonebook entry.
RelatedSearch - Use the RelatedSearch SourceType to view searches that provide information in which you might be interested, based on your current search.
Spell - When a query is sent to the Spell SourceType, alternative spellings for the query based on a number of criteria are proposed.
Translation - The Translation SourceType translates a term or a small block of text (three sentences or less) from one language to another.
Video - The Video SourceType returns a list of videos relevant to the query term.
Web - The Web SourceType returns pages relevant to the queried term.
NUMBER_OF_RESULTS - The number of the search results to be returned.
OFFSET - The number of results from where the number of results will start counting.
We will use some of these types in our manage() function:
[code lang="php"]function manage($type){
if($_GET['end']){
$end= $_GET['end'];
}else{
$end=10;
}
if($_GET['start']){
$start= $_GET['start'];
}else{
$start=0;
}
//Switch case on the type variable
switch($type){
case "web":
//if we search for web :
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=web&Web.Count='.$end.'&Web.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "spell":
//Spell check
$request ='http://api.search.live.net/json.aspx?AppId=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&Query='.urlencode( $_GET["q"]).'&Sources=Spell&Version=2.0&Market=en-us&Options=EnableHighlighting';
break;
case "image":
//Search for images
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=Image&Version=2.0&Image.Count='.$end.'&Image.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "video":
//Video search
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=Video&Version=2.0&Video.Count='.$end.'&Video.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "phonebook":
//phonebook service
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=Phonebook&Version=2.0&Market=en-us&UILanguage=en&Latitude=47.603450&Longitude=-122.329696&Radius=100.0&Phonebook.FileType=YP&Options=EnableHighlighting&Phonebook.Count='.$end.'&Phonebook.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "translate":
//translate service. This one will translate the term to Greek
$request = 'http://api.search.live.net/json.aspx?AppId=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&Query=' . urlencode( $_GET["q"]).'&Sources=Translation&Version=2.2&Market=en-us&Options=EnableHighlighting&Translation.SourceLanguage=en&Translation.TargetLanguage=el';
break;
case "answers":
//Answers service.
$request = "http://api.search.live.net/json.aspx?AppId=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&Query=".urlencode( $_GET["q"])."&Sources=InstantAnswer&Version=2.0&Market=en-us";
break;
}
//we get the contents of the file
$response = file_get_contents($request);
//and we decode the json data
$jsonobj = json_decode($response);
//Return the decoded object
return $jsonobj;
}[/code]
The next thing to do is to check if there is a request:
[code lang="php"]if($_GET['q']){
$obj = manage($_GET['type']);
}[/code]
and then we display the results using a switch case on the type:
[code lang="php"]switch($_GET['type']){
case "web":
?>
<ul>
<?php
foreach($obj->SearchResponse->Web->Results as $value){
?>
<li><a href="<?php echo $value->Url; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><?php echo $value->Title; ?></a><br /><div><?php echo strip_tags($value->Description); ?></div><div><?php echo $value->DisplayUrl; ?></div></li>
<?php
}
?>
</ul>
</div>
<?php
break;
case "image":
?>
<div>
<?php
foreach($obj->SearchResponse->Image->Results as $value){
?>
<div><a href="<?php echo $value->MediaUrl; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><img src="<?php echo $value->Thumbnail->Url;?>" alt="<?php echo strip_tags($value->Title); ?>"></a></div>
<?php
}
?>
</ul>
<?php
break;
case "video":
?>
<div>
<?php
foreach($obj->SearchResponse->Video->Results as $value){
?>
<div><a href="<?php echo $value->PlayUrl; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><img src="<?php echo $value->StaticThumbnail->Url;?>" alt="<?php echo strip_tags($value->Title); ?>"></a></div>
<?php
}
?>
</ul>
<?php
break;
case "phonebook":
?>
<ul>
<?php
foreach($obj->SearchResponse->Phonebook->Results as $value){
?>
<li><a href="<?php echo $value->Url; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><?php echo $value->Title; ?></a>
<br /><div><?php echo strip_tags($value->Business); ?></div>
<div>Phone: <?php echo $value->PhoneNumber; ?></div>
<div>Address: <?php echo $value->Address; ?></div>
<div>City: <?php echo $value->City; ?></div>
</li>
<?php
}
?>
</ul>
<?php
break;
case "translate":
echo "<h2>Translated to Greek:</h2><div class='translated'>".$obj->SearchResponse->Translation->Results[0]->TranslatedTerm."</div>";
break;
case "spell":
echo "<h2>Spelled like this:</h2><div class='spelled'>".$obj->SearchResponse->Spell->Results[0]->Value."</div>";
break;
case "answers":
echo "<h2>Your Answer is:</h2><div class='answered'>".$obj->SearchResponse->InstantAnswer->Results[0]->InstantAnswerSpecificData->Encarta->Value."</div>";
break;
}
?>
<!-- We add a simple pager -->
<div id="pager">
<div>
<span>
<?php if($_GET['start']>0){
?>
<a href="javascript:search('<?php echo $_GET['type'];?>','<?php echo $start-10;?>','<?php echo $end;?>');">Less</a>
<?php
}
?>
</span>
<span>
<a href="javascript:search('<?php echo $_GET['type'];?>','<?php echo $start+10;?>','<?php echo $end;?>');">More</a>
</span>
</div>[/code]
The last lines will display a simple pager.
The Full PHP code
This is the code we used for our example:
[code lang="php"]<?php
function manage($type){
if($_GET['end']){
$end= $_GET['end'];
}else{
$end=10;
}
if($_GET['start']){
$start= $_GET['start'];
}else{
$start=0;
}
switch($type){
case "web":
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=web&Web.Count='.$end.'&Web.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "spell":
$request ='http://api.search.live.net/json.aspx?AppId=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&Query='.urlencode( $_GET["q"]).'&Sources=Spell&Version=2.0&Market=en-us&Options=EnableHighlighting';
break;
case "image":
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=Image&Version=2.0&Image.Count='.$end.'&Image.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "video":
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=Video&Version=2.0&Video.Count='.$end.'&Video.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "phonebook":
$request ='http://api.search.live.net/json.aspx?Appid=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&sources=Phonebook&Version=2.0&Market=en-us&UILanguage=en&Latitude=47.603450&Longitude=-122.329696&Radius=100.0&Phonebook.FileType=YP&Options=EnableHighlighting&Phonebook.Count='.$end.'&Phonebook.Offset='.$start.'&query=' . urlencode( $_GET["q"]);
break;
case "translate":
$request = 'http://api.search.live.net/json.aspx?AppId=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&Query=' . urlencode( $_GET["q"]).'&Sources=Translation&Version=2.2&Market=en-us&Options=EnableHighlighting&Translation.SourceLanguage=en&Translation.TargetLanguage=el';
break;
case "answers":
$request = "http://api.search.live.net/json.aspx?AppId=28CDBBD47CD8F911BE7EA5C3CB748FE6EFA951FC&Query=".urlencode( $_GET["q"])."&Sources=InstantAnswer&Version=2.0&Market=en-us";
break;
}
$response = file_get_contents($request);
$jsonobj = json_decode($response);
return $jsonobj;
}
if($_GET['q']){
$obj = manage($_GET['type']);
}
if($_GET['end']){
$end= $_GET['end'];
}else{
$end=10;
}
if($_GET['start']){
$start= $_GET['start'];
}else{
$start=0;
}
switch($_GET['type']){
case "web":
?>
<ul>
<?php
foreach($obj->SearchResponse->Web->Results as $value){
?>
<li><a href="<?php echo $value->Url; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><?php echo $value->Title; ?></a><br /><div><?php echo strip_tags($value->Description); ?></div><div><?php echo $value->DisplayUrl; ?></div></li>
<?php
}
?>
</ul>
</div>
<?php
break;
case "image":
?>
<div>
<?php
foreach($obj->SearchResponse->Image->Results as $value){
?>
<div><a href="<?php echo $value->MediaUrl; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><img src="<?php echo $value->Thumbnail->Url;?>" alt="<?php echo strip_tags($value->Title); ?>"></a></div>
<?php
}
?>
</ul>
<?php
break;
case "video":
?>
<div>
<?php
foreach($obj->SearchResponse->Video->Results as $value){
?>
<div><a href="<?php echo $value->PlayUrl; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><img src="<?php echo $value->StaticThumbnail->Url;?>" alt="<?php echo strip_tags($value->Title); ?>"></a></div>
<?php
}
?>
</ul>
<?php
break;
case "phonebook":
?>
<ul>
<?php
foreach($obj->SearchResponse->Phonebook->Results as $value){
?>
<li><a href="<?php echo $value->Url; ?>" target="_blank" title="<?php echo strip_tags($value->Title); ?>"><?php echo $value->Title; ?></a>
<br /><div><?php echo strip_tags($value->Business); ?></div>
<div>Phone: <?php echo $value->PhoneNumber; ?></div>
<div>Address: <?php echo $value->Address; ?></div>
<div>City: <?php echo $value->City; ?></div>
</li>
<?php
}
?>
</ul>
<?php
break;
case "translate":
echo "<h2>Translated to Greek:</h2><div class='translated'>".$obj->SearchResponse->Translation->Results[0]->TranslatedTerm."</div>";
break;
case "spell":
echo "<h2>Spelled like this:</h2><div class='spelled'>".$obj->SearchResponse->Spell->Results[0]->Value."</div>";
break;
case "answers":
echo "<h2>Your Answer is:</h2><div class='answered'>".$obj->SearchResponse->InstantAnswer->Results[0]->InstantAnswerSpecificData->Encarta->Value."</div>";
break;
}
?>
<div id="pager">
<div>
<span>
<?php if($_GET['start']>0){
?>
<a href="javascript:search('<?php echo $_GET['type'];?>','<?php echo $start-10;?>','<?php echo $end;?>');">Less</a>
<?php
}
?>
</span>
<span>
<a href="javascript:search('<?php echo $_GET['type'];?>','<?php echo $start+10;?>','<?php echo $end;?>');">More</a>
</span>
</div>[/code]
The API key displayed above is a fictional key. You should get one from Bing in order to use the API. Get one now! It is free.
Here is a live example of the application described above. We called it GooBingoo!, but Bingoo! might have been a better name for it ;)