Keyvan Nayyeri

God breathing through me

Search Relevancy Extension for Graffiti

One of the very nice features that I saw in BlogEngine.NET was a control that displays relevant posts based on the search query that a visitor has entered in search engines (i.e. Google) to come to a BlogEngine site.

This control is very helpful for some reasons:

  • It can help your visitors coming from search engines to find the most appropriate content for what they're looking for.
  • If you have written something new about a topic and visitor has found the older post then he or she can find the new stuff as well. This is especially helpful for Graffiti because Beta 1 doesn't support trackbacks.
  • Such a control can keep search engine visitors on your site for a longer time. This is an important parameter in Web 2.0 and 3.0 statistics.

Seeing that control in BlogEngine inspired me to write similar control for Graffiti as a Chalk extension. The process is straightforward and simple and I describe it here.

Background

First let me describe the process in theory.

To find the relevant posts for a visitor who's coming from a search engine we need to follow a few steps:

  • Get the referrer URL for each request.
  • Parse the referrer URL to see if it's coming from a search engine.
  • Find the query text from the URL.
  • Search for the query in local Graffiti application to find relevant posts.
  • Display the relevant posts list.

This process is logically true and it's implementation would be easy (as you see in a moment). I personally would like such examples because of their fun and usage of ASP.NET concepts. Recently I found that ASP.NET development is the most easy task for me! Moreover, I try to cover some development concepts around Graffiti in my examples and they can help you see real world examples to learn how to use Graffiti API.

Implementation

As you could guess the implementation consists of a single Chalk extension for Graffiti that is a single class named Generator.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

using System.Text.RegularExpressions;

using Graffiti.Core;

using Telligent.Labs.LSearch;

 

namespace Graffiti.SearchRelevancy

{

    [Chalk("searchRelevancy")]

    public class Generator

    {

There is a public string function called GetRelevantPosts which gets two arguments including the number of relevant posts that you want to display as well as a string text pattern to be shown for the header text of your output.

public string GetRelevantPosts(int count, string pattern)

{

    string referrerUrl = string.Empty;

 

    try

    {

        HttpContext context = HttpContext.Current;

        referrerUrl = context.Request.UrlReferrer.ToString();

    }

    catch

    {

        // Do nothing!

    }

 

    if (!string.IsNullOrEmpty(referrerUrl))

    {

        try

        {

            string keywords = GetKeywords(referrerUrl);

 

            if (!string.IsNullOrEmpty(keywords))

            {

                ResultSet<Post> posts = GetRelevantItems(keywords, count);

 

                if ((posts != null) && (posts.Count > 0))

                    return GetHtmlCode(pattern, keywords, posts);

            }

        }

        catch (Exception ex)

        {

            Log.Error("Search Relevancy Extension", ex.Message + ex.StackTrace);

        }

    }

 

    return string.Empty;

}

This method uses the HttpContext.UrlReferrer to fetch the referrer URL for the current request then passes this URL to GetKeywords function in order to find the possible query text for the search engine. If there is any query available then it calls the GetRelevantItems method to retrieve a list of relevant posts and passes these items to GetHtmlCode to generate and return the final HTML output.

GetKeywords function simply gets the referrer URL as the argument and tries to parse it in order to find and return the string value of the query text.

private string GetKeywords(string referrerUrl)

{

    List<string> patterns = new List<string>();

    patterns.Add("search");

 

    foreach (string pattern in patterns)

    {

        if (Regex.IsMatch(referrerUrl, pattern))

        {

            List<string> queryPatterns = new List<string>();

            queryPatterns.Add(@"q=([\w\.\+\-\%]+)");

            queryPatterns.Add(@"q=([\w\.\+\-\%]+)&");

 

            foreach (string queryPattern in queryPatterns)

            {

                Regex regEx = new Regex(queryPattern, RegexOptions.IgnoreCase);

 

                Match match = regEx.Match(referrerUrl);

 

                if (!string.IsNullOrEmpty(match.Value))

                    return HttpContext.Current.Server.UrlDecode(match.Groups[1].Value);

            }

        }

    }

    return null;

}

This method is completely written based on the regular expressions and tries to match a few patterns to find the query text and return it. I don't think there is any special point to mention about this except that I wrote it for Google and Live search engines so you may need to add your own patterns for other search engines as well.

The next private function is GetRelevantItems which gets the keywords and the number of relevant items to find. It simply returns a generic object of ResultSet<Post> type.

private ResultSet<Post> GetRelevantItems(string keywords, int count)

{

    try

    {

        SearchQuery query = new SearchQuery();

        query.QueryText = keywords;

        query.Sort = SearchSort.Relevance | SearchSort.Descending;

        query.PageSize = count;

        query.PageIndex = 0;

 

        SearchIndex index = new SearchIndex();

 

        return index.Search(query);

    }

    catch (Exception ex)

    {

        Log.Error("Search Relevancy Extension", ex.Message + ex.StackTrace);

    }

    return null;

}

The main reason for me to write these examples is showcasing different aspects of Graffiti API and its development and the below paragraphs are some key points that you can learn from this example about searching with Graffiti API.

Graffiti uses Lucene to provide search functionality. Telligent has applied Lucene in a separate project and has ported it to Graffiti to let you search easily in your content. Telligent.Labs.LSearch assembly is responsible to provide search functionality on top of the Lucene for you. It has an abstract Index<T> generic class that you need to implement for your own search scenarios. For Graffiti, Telligent has implemented this as Graffiti.Core.SearchIndex class which is actually an implementation of the Index<T> where type is Post.

SearchIndex class provides some methods for searching in a Graffiti application including Search method which gets an instance of the Telligent.Labs.LSearch.SearchQuery object for the query and returns an instance of the ResultSet<Post> for results. Above method uses the SearchIndex along a SearchQuery to find relevant items and returns them.

And finally the last private helper method is GetHtmlCode that gets a few parameters to generate the HTML output for the extension. I don't think it needs any description!

private string GetHtmlCode(string pattern, string keywords, ResultSet<Post> posts)

{

    StringBuilder sb = new StringBuilder();

    sb.AppendFormat("<div id=\"SearchRelevancy\"><p>" + pattern + "<ul>", keywords);

 

    foreach (Post post in posts)

    {

        sb.AppendFormat("<li><a href=\"{0}\">{1}</a></li>",

            new Macros().FullUrl(post.Url), post.Title);

    }

 

    sb.Append("</ul></p></div>");

    return sb.ToString();

}

Download

You can download a package including the binary and source code files of Search Relevancy extension for Graffiti from here.

How to Use

After downloading the package and uploading the assembly file to bin folder on your server, you can modify your theme files to use this extension. I personally would prefer to insert this extension somewhere in my Layout.view page because it lets all my pages to show this extension.

You just need to add a single line of code to your view files where you want this extension to appear as is shown below. There are two parameters required for this extension: the number of relevant items to display and the text pattern for the header text that must have a placeholder with {0} index for keywords.

$searchRelevancy.GetRelevantPosts(5, "You have searched for '{0}' so following posts may be helpful for you:")

Here is a sample snapshot of this extension in action when I searched for "keyvan nayyeri" in Google to navigate to my site:

Search Relevancy Extension for Graffiti

Now tell me how sweet and simple is Graffiti development?!

1 Comments

Reducing the bounce rate of tech blogs with Subtext and Lucene.net

Leave a Comment





Ads Powered by Lake Quincy Media Network