ASP.Net MVC2 and Full-Text Searching

Implementing searching on your site has never been easier. There are many tools out there such as Lucene.Net which is amazing, but if the scale of the project is not too large, it’s probably much easier to enable Full-Text Search on the tables and query results out of the catalog.

I am using ASP.Net MVC2, which provides much flexibility and is amazing fun to work with. Things can be set up to your taste as long as you follow the conventions laid out. Here’s how I have it set up.

Model
I used the following model to retrieve the search text and store the results once they were queried and parsed from the database using a searching service I am going to detail in a bit.

public class SearchEntry
    {
        [Required]
        [DisplayName("Search Keyword(s)")]
        public string QueryText { get; set; }

        public IList<Product> ResultProductsList { get; set; }
    }

The annotations [DisplayName("Search Keyword(s)")] and [Required] help display and validate the model. These can be found in System.ComponentModel and System.ComponentModel.DataAnnotations respectively.

Views
Again, how you want to set the views up is up to you. I have two views and a partial view. One view as a home page, for example. Another to display the results. The partial view is a form for searching which I can reuse as I need to.

The partial view is strongly typed to the SearchEntry model.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.SearchEntry>" %>

    <% using (Html.BeginForm("Submit", "Search", FormMethod.Get)) {%>
                Enter keywords to search:
                <%: Html.TextBoxFor(model => model.QueryText) %>
                <input type="submit" value="Search" />
                <%: Html.ValidationMessageFor(model => model.QueryText,
                                              "At least one search keyword is required") %>
    <% } %>

Pretty straight forward!
The two views are really straight forward too. The home page for example, just renders the form by calling an action called SearchForm defined in the Search controller. You can change that around for sure.
Instead of using Html.RenderAction which is found in System.Web.Mvc.Html you can also use Html.RenderPartial too, depending on how you like your coffee, and needs.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Home
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <% Html.RenderAction("SearchForm", "Search"); %>

</asp:Content>

The results view may look any way you would like it to be. Again, this view is strongly typed to SearchEntry.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Model.SearchEntry>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Results for "<%: Model.QueryText %>"
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <% Html.RenderPartial("SearchForm"); %>

    <table>
        <thead>
            <tr>
                 <th>Name</th>
                 <th>Price</th>
            </tr>
          </thead>
          <tbody>
          <% foreach (var record in Model.ResultProductsList) { %>
              <tr>
                  <td><%: record.Name %></td>
                  <td><%: record.Price %></td>
              </tr>
           <% } %>
           </tbody>
    </table>
</asp:Content>

Controller
The controller is quite elegant, I find, it does little but accomplishes a lot. It simply glues everything together.

ISearchEngine searchEngine;

//initializes the search engine
public SearchController(IContextRepository contextRepository)
{
    searchEngine = new SearchEngine(contextRepository);
}

//returns the partial view
public ActionResult SearchForm()
{
    return View();
}

//retrieves results and returns the results to view
public ActionResult Submit(SearchEntry searchEntry)
{
    searchEntry = searchEngine.Search(searchEntry);
    return View("Results", searchEntry);
}

I like to retrieve the connection string from what I call ContextRepository. It simplifies things, I find.
The Submit method takes in the model submitted by the search form above and retrieves the results from the search engine service shown below.

Service

public class SearchEngine : ISearchEngine
{
        private IContextRepository ContextRepository;

        public SearchEngine(IContextRepository contextRepository)
        {
            ContextRepository = contextRepository;
        }

        public SearchEntry Search(SearchEntry searchEntry)
        {
            searchEntry.ResultProductsList =
                ProductsFullTextSearch(searchEntry.QueryText, ContextRepository.MasterContext).ToList();

            return searchEntry;
        }

        private IEnumerable<Products> ProductsFullTextSearch(string text, DataContext context)
        {
            return (GenericFullTextSearch<Products>(text, context) as IEnumerable<Products>);
        }

        private IEnumerable<TSource> GenericFullTextSearch<TSource>(string text, DataContext context)
        {
            //Find LINQ Table attribute
            object[] info = typeof(TSource).GetCustomAttributes(typeof(System.Data.Linq.Mapping.TableAttribute), true);
            //Get table name
            String table = (info[0] as System.Data.Linq.Mapping.TableAttribute).Name;
            //Full text search on that table
            return context.ExecuteQuery<TSource>(String.Concat("SELECT * FROM ", table, " WHERE CONTAINS(*, {0})"), text);
        }
}

That’s all! I’m sure a lot can be improved on. On it now!

Advertisements

2 Comments on “ASP.Net MVC2 and Full-Text Searching”

  1. Richard says:

    Hello, could you please give more details? What is ISearchEngine? ContextRepository? Could give more details of the implementation and the sequence of implementation of the model, controller and view? I will appreciate it so much.

    Thank you very much :)

    • Kowsheek says:

      Hi Richard. ISearchEngine and ContextRepository are components that I had used for this implementation. It is not something regular for MVC.
      I am guessing you would like a more detailed overview of it, I will be writing some tutorials about MVC soon, so you will find them useful.

      If instead you wanted to know more about this in particular, I will update the post. Let me know.

      Thanks for reading.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s