When I was at the MVP Global Summit back in March we saw an early preview of a SearchDataSource. This looked really cool, but something I would probably not use.
Oh, how quickly things change!
I've been doing a couple of sites lately that use MondoSearch - Internet and Intranet. It's a great product and very powerful, but it takes a little while to figure out the best way to use it. There are a few different api's you can choose from (cgi, ActiveX, .Net Provider and .Net Web Service) and each of these provides different features and supports different scenarios. After a bit of experimentation I found that the web service offered the simplest solution and best features for my particular problem.
Anyway, I was very interested to see if the SearchProvider in the May release of ASP.Net Futures would make things simpler. So I created a MondoSearch provider and this is what I found.
Creating the provider
This is extremely easy, especially if you already have some code to talk to MondoSearch. My provider looks like this:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Preview.Search;
using System.Data;
using System.Collections.Specialized;
using System.Configuration.Provider;
namespace Jonesie.Search
{
/// <summary>
/// An ASP.Net search provider for MondoSearch
/// </summary>
public class MondoSearchProvider : SearchProviderBase
{
private string _licenseKey;
private string _url;
private string _mql;
private string _lang;
private bool _preview;
/// <summary>
/// Initialise the search from web.config settings
/// </summary>
/// <param name="name"></param>
/// <param name="config"></param>
/// <remarks>
/// The web.config should contain values for:
/// licenseKey - License key
/// url - Web Service URL
/// mql - MQL options
/// language - language to search on
/// preview - Use preview database
/// </remarks>
public override void Initialize(string name, NameValueCollection config)
{
// Verify that config isn't null
if (config == null)
throw new ArgumentNullException("config");
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(name))
name = "MondoSearchProvider";
// Add a default "description" attribute to config if the
// attribute doesn't exist or is empty
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description",
"MondoSearch Provider");
}
// Call the base class's Initialize method
base.Initialize(name, config);
// Initialize licensekey
_licenseKey = config["licenseKey"];
if (string.IsNullOrEmpty(_licenseKey))
_licenseKey = "";
config.Remove("licenseKey");
// Initialize url
_url = config["url"];
if (string.IsNullOrEmpty(_url))
_url = "";
config.Remove("url");
// Initialize mql
_mql = config["mql"];
if (string.IsNullOrEmpty(_mql))
_mql = "";
config.Remove("mql");
// Initialize language
_lang = config["language"];
if (string.IsNullOrEmpty(_lang))
_lang = "";
config.Remove("language");
// Initialize mql
string pv = config["preview"];
if (string.IsNullOrEmpty(pv))
{
_preview = false;
}
else
{
_preview = Convert.ToBoolean(pv);
}
config.Remove("preview");
// Throw an exception if unrecognized attributes remain
if (config.Count > 0)
{
string attr = config.GetKey(0);
if (!String.IsNullOrEmpty(attr))
throw new ProviderException
("Unrecognized attribute: " + attr);
}
}
/// <summary>
/// Perform a search against the mondo web service
/// </summary>
/// <param name="searchQuery"></param>
/// <returns></returns>
public override SearchResult[] Search(SearchQuery searchQuery)
{
List<SearchResult> results = new List<SearchResult>();
DataSet msds;
MondoSearch.SearchService ms = new MondoSearch.SearchService();
ms.Url = _url;
msds = ms.Search(_licenseKey, _lang, _preview, searchQuery.Query, _mql);
// loop through the pages and add them to the results
foreach (DataRow dr in msds.Tables["pages"].Rows)
{
SearchResult sr = new SearchResult();
sr.Title = (string)dr["title"];
sr.Description = (string)dr["description"];
sr.Url = (string)dr["linkdisplay"];
results.Add(sr);
}
return results.ToArray();
}
}
}
The web.config section for this is:
<microsoft.web.preview>
<search enabled="true">
<providers>
<add name="MondoSearchProvider" type="Jonesie.Search.MondoSearchProvider, Jonesie.MondoSearchProvider"
licenseKey="xxxxxxxxxxxxxxxxxxxxxxxxx"
url="http://mysearchsite.com/SearchService/SearchService.asmx"
mql=""
language="EN"
preview="false"/>
</providers>
</search>
</microsoft.web.preview>
Using the Provider
Using the new provider couldn't be simpler. Drop a SearchDataSource on the page and set the query, attach your repeater to it. Bind the repeater. Done. Here's mine:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style>
body {
font-family:verdana;
font-size:12px;
}
.result {
background-color:white;
}
.title {
font-weight:bold;
}
.description {
font-style: italic;
}
</style>
</head>
<body>
<h1>
MondoSearchProvider Test</h1>
<form id="form1" runat="server">
<asp:SearchDataSource ID="SearchDataSource1" runat="server">
<SelectParameters>
<asp:ControlParameter ControlID="queryText" Name="query" PropertyName="Text" Type="Object" />
</SelectParameters>
</asp:SearchDataSource>
<div>
Search For:
<asp:TextBox ID="queryText" runat="server" Width="250" Text="" />
<asp:Button ID="searchButton" runat="server" Text="Go!" OnClick="searchButton_Click" />
</div>
<asp:Repeater ID="rptResults" runat="server" Visible="false" DataSourceID="SearchDataSource1">
<HeaderTemplate>
<h3>
Search Results</h3>
</HeaderTemplate>
<ItemTemplate>
<div class="result">
<div class="title">
<a href='<%# Eval("Url") %>'>
<%# Eval("Title") %>
</a>
</div>
<div class="description">
<%# Eval("Description") %>
</div>
</div>
</ItemTemplate>
</asp:Repeater>
</form>
</body>
</html>
The code behind for the button is:
rptResults.DataBind();
rptResults.Visible = true;
Limitations & Successes
The main issue I see with the provider is the loss of some of the advanced features of MondoSearch. For example, security filtering, highlighting, multiple languages - there is nowhere in the SearchResult to store this extra information.
However, using this provider does make it extremely simple to add simple searching to your site.