Anders G. Nordby

Lead Consultant at Itera

Tag Archives: truffler

Using Truffler to search and filter products on a site

I recently had the pleasure of using Truffler as a search engine for one of our customers. The site is an EPiServer CMS 6 R2 site with PageTypeBuilder. This article describes some of the decisions I made about how to use Truffler on this particular site. Some of these decisions were more-or-less obvious, and some evolved by trying out various ways to do things (lots of trial without much error).

WHAT PAGE TYPES TO INDEX

The site has more than thirty page types, but only a few (products and articles) should be indexed with truffler. My solution to this is really simple. As always, when I develop with PageTypeBuilder, I create a BasePage (which inherits from TypedPageData) which all page types in the solution inherits from. In my BasePage, I included the following property:

public virtual bool IndexWithTruffler { get { return false; } }

This can then be overridden in the various pagetypes that should/could be indexed, either by simply returning true, or by letting the editor choose whether or not to index by using an editorial property. In my global.asax file, all I need now is this:

PageIndexer.Instance.Conventions
	.ForInstancesOf<BasePage>().ShouldIndex(x => x.IndexWithTruffler);

FILTERED SEARCH WITH TRUFFLER

One of the main reasons for choosing Truffler, was that the visitors of the site should be able to search and filter the products based on various categories. We ended up with more than twenty different categories in addition to the freetext search.

I found some very good examples in the Truffler documentation of how to do filtering, but using the code from the documentation directly would lead to very cluttered code. After some experimentation, I found out that using extension methods was the way to simplyfy my code. With a helper class Criteria (holding the current seach criteria), the various filters are mostly one-liners. A couple of examples:

public static ITypeSearch FilterVolume(this ITypeSearch search, double? size)
{
	return size == null || size.Value.Equals(0)
		? search
		: search.Filter(x => x.VOLUM.Match(size.Value));
}

public static ITypeSearch FilterDistrict(this ITypeSearch search, string district)
{
	return string.IsNullOrEmpty(district)
		? search
		: search.Filter(x => x.DISTRIKT_TEKST.MatchCaseInsensitive(district));
}

...

I can then use my filters like this

var client = Client.CreateFromConfig();
var search = client.Search();

search = search.FilterDistrict(criteria.District);
search = search.FilterVolume(criteria.VolumeInLiters);
...

which I think makes the code quite readable, even with the twenty-plus filters we have.

The FilterFreeText was really tricky, and took quite some time to get right, but the final result is clean and simple:

public static ITypeSearch FilterFreeText(this ITypeSearch search, IClient client, string freetext)
{
	if (string.IsNullOrEmpty(freetext))
		return search;

	foreach (var w in freetext.Split(' '))
	{
		var word = w;
        var freeTextFilter = client.BuildFilter();

        freeTextFilter = freeTextFilter.Or(x => x.PageName.AnyWordBeginsWith(word));
        freeTextFilter = freeTextFilter.Or(x => x.DISTRIKT_TEKST.AnyWordBeginsWith(word));
		...(some more fields)...

        search = search.Filter(freeTextFilter);
	}

	return search;
}

FACETING WITH TRUFFLER

The site only uses terms facets, which are very easy to set up with Truffler. A few times I had to create simple “helper” properties to make this work; e.g. for Volume (a number), I added the indexable property

public string VolumeString
{
	get { return VOLUM.HasValue ? VOLUM.Value.ToString("0.00") : string.Empty; }
}

I created a TrufflerFacets class to hold and expose the facets, like this

public class TrufflerFacets
{
	private IEnumerable<TermCount> FacetDistrict { get; set; }
	private IEnumerable<TermCount> FacetVolume { get; set; }
	...

	public IEnumerable<string> District
	{
		get { return FacetDistrict.TermsToList().OrderBy(term => term); }
	}

	public IEnumerable<string> Volume
	{
		get { return FacetVolume.TermsToList().OrderBy(term => term); }
	}

	...

which lets me write the actual facets populated during the search as one-liners

search = search.TermsFacetFor(x => x.VolumeString);
search = search.TermsFacetFor(x => x.DISTRIKT_TEKST);
...

and then populating the facets to be displayed on the site like this

var result = search.Take(0).GetResult();
FacetVolume = result.TermsFacetFor(x => x.VolumeString);
FacetDistrict = result.TermsFacetFor(x => x.DISTRIKT_TEKST);
...

FINAL NOTES

As expected (since Joel Abrahamson is the man behind both of these) Truffler and PageTypeBuilder go really well together. I’d also like to point out that the very few issues and difficulties I had were immediately taken care of – I got direct and to-the-point help with everything from the Truffler guys! The documentation on the site is great – I found most of what I needed there – and a lot could be used directly as written in the documentation.

Installing Truffler was a breeze, simply registering a developer account on the Truffler website, and getting the NuGet package, and I was up and running in about a minute or two! Literally! You won’t believe how easy it is to get started until you try it out for yourself.

I’ve noticed that Joel Abrahamson has just started a series on how to use Truffler:

However, I thought it would be nice to share my experiences anyway.

Advertisements