How To make Search for Umbraco site with Lucene.Net, Examine and XSLT

Being able to search content in a website is probably the most common and most important feature of any website. And there are a millions ways to do this,.. so why use Lucene indexing or Eximine for that matter..   If you have been looking inside the umbraco bin directury you will notice a wary nice Lucene.Net.dll file. This is one of the reasons for why i chose  Lucene indexing, because it is already there (c:
Umbraco uses Lucene indexing to search it self, if you’ve ever wondered where those files hidden, they’re in the “/data/_systemUmbracoIndexDontDelete” folder.  There you’ll find at least one .cfs file, a deletable and segments file.

In my setup i wanted to separate my indexes from the ones Umbraco generates,  so -i chose  Examine to do the heavy lifting, of generating indexes on so on.  Examine has loads of useful features, i will not recite them all but you can read about them here. So I downloaded the demo site from the FarmCode.com to take a closer look at the Examine set up, and because i was curious.. (c:

After a quick run-thought the demo-site and its macros and other code,  i copied all the related DLL.

  • Lucene.Net.dll(lates version)
  • Examine.dll
  • UmbracoExamine.dll
  • UmbracoExamine.Contrib.dll

And completed the setup, which can be found here on the FarmCode.com site.

I’ve noticed that the UmbracoExamine.Contrib.dll han an XSLT extension, so i added  it to my xsltExtensions.config

<?xml version="1.0" encoding="utf-8" ?>
<XsltExtensions>
        <ext assembly="/bin/UmbracoExamine.Contrib" type="UmbracoExamine.Contrib.XsltExtensions" alias="examine" />
</XsltExtensions>

and wrote a small XSLT macro to test it.

<xsl:stylesheet
        version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:msxml="urn:schemas-microsoft-com:xslt"
        xmlns:examine="urn:examine"
        xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib"
        exclude-result-prefixes="msxml examine umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib ">
<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>
<xsl:template match="/">
  [[<xsl:value-of select="examine:Search('something')"/>]]
</xsl:template>

But it resulted in an “Error parsing XSLT file: \xslt\SearchPosts.xslt” error.. grrr,.. Nothing i did could make it good again,.. the XSLT code was pretty simple and the xsltExtensions.confiq was carrect.  As it tuns out the “UmbracoExamine.Contrib.dll” is not released yest (c:  thanx Lee

So in stead of getting frustrated and angry i’ve decided to write my own SearchExtension Class ,..  (c: Her is the busines end

//remember to add these (c:
//using Examine;
//using UmbracoExamine.SearchCriteria;
//using UmbracoExamine;
//using Examine.SearchCriteria;

public static XPathNodeIterator Search(string searchTerm, string field)
{
      return Search(searchTerm, field, "MySearcher");
}

public static XPathNodeIterator Search(string searchTerm, string field, string searchProvider)
{
//get query string search param & error check
if (string.IsNullOrEmpty(searchTerm)) return null;
var criteria = ExamineManager.Instance.CreateSearchCriteria(IndexType.Content, Examine.SearchCriteria.BooleanOperation.Or);

ISearchCriteria filter = criteria
   .Field("bodyText", searchTerm.Fuzzy().Value)
   .Or()
   .Field("menuTitle", searchTerm.Fuzzy().Value)
   .Compile();
XDocument node = new XDocument();
XElement content = new XElement("nodes");
//get the search results from the mysearch provider
foreach (SearchResult result in ExamineManager.Instance.SearchProviderCollection[searchProvider].Search(filter).AsEnumerable().Where(res => (res.Score * 10) > 2).OrderBy(res => res.Score))
{
   XElement element2 = new XElement("node");
   XAttribute attribute = new XAttribute("id", result.Id);
   XAttribute attribute2 = new XAttribute("score", result.Score);
   element2.Add(new object[] { attribute, attribute2 });
   foreach (KeyValuePair<string, string> pair in result.Fields)
   {
      XElement element3 = new XElement("data");
      XAttribute attribute3 = new XAttribute("alias", pair.Key);
      XCData data = new XCData(pair.Value);
      element3.Add(new object[] { attribute3, data });
      element2.Add(element3);
   }
content.Add(element2);
}
node.Add(content);
return node.CreateNavigator().Select("/");
}

After you have copied your DLL to umbracos bin dir and added it to the  xsltExtensions.config like describe above,.. It’s time to write the XSLT macro,.. this is my version (c:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:mySearch="urn:mySearch"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib"
exclude-result-prefixes="msxml mySearch umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:variable name="currentKeyWord" select="$currentPage/data[@alias='KeyWords']"></xsl:variable>

<xsl:template match="/">
   <xsl:variable name="searchResult" select="mySearch:Search(string($currentKeyWord),string('KeyWords'))" />
   <xsl:if test="count($searchResult/nodes/node) != 0">
      <ul>
      <xsl:for-each select="$searchResult/nodes/node">
         <xsl:sort order="descending" select="./data[@alias = 'updateDate']"/>
            <li>
            <a href="{umbraco.library:NiceUrl(@id)}">
            <xsl:value-of select="./data[@alias = 'menuTitle']"/>
            </a>
            </li>
      </xsl:for-each>
      </ul>
   </xsl:if>
</xsl:template>

Notice that I am using ./data[@alias = 'updateDate'] to probe for the fields stored in my LuceIndex.  Thats sums it up (c:

Happy coding..

About these ads

2 Responses to How To make Search for Umbraco site with Lucene.Net, Examine and XSLT

  1. Bruno says:

    Hi,
    This looks pretty good. I will give it a go soon. I have two questions:
    1. Do you know if I can use Examine to search into documents as well ?
    2. How do you make the ‘most popular search word cloud’ on the right hand side of your page ?
    Thanks

    • maanehunden says:

      well, there is no built in support in Lucene to index PDF documents, and such. But you can probably wright one where you extract data manually, using some kind of pdf reader library.
      the other one, needs some custom code as well, you can store what people are searching for on a database or in an index like lucene,.. the you can analyze the data ..

      Happy Coding (c:

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: