This project is read-only.
The version of Examine that supports facets neads to be built from the branch "Facets"

Facet support is experimental and may be subject to change

This implementation of faceted search with Lucene has a small memory footprint and supports a very large number of facets.

Facets quickly explained

Facets are a way to summarize and navigate search results. For instance if you search a shop for “Wool” you’ll get the 10 best matching products and also the total number of items in some categories, say, “Shirts (400), Headgear(201), Shoes(4)”. These can be clicked to filter the search results only to show items in those categories. That is facets. Instead of product categories they might as well be years, months or authors. They can also be hierarchical like “Shirts/Men, Shirts/Women Shirts/Children/Toddlers”. Just like nodes in Umbraco. In the hierarchical case “Shirts” is the total count of search results that are shirts in some way. “Shirts/Men” is the subset of the shirts that are for Men.

How to configure

Use Type=”FACET” or Type=”FACETPATH” for the fields that contains facet values in your index set. This is typically in the file config/ExamineIndex.config
See "Configuring from code" later on this page for other options.

For example, here Column1, Column2 and Column4 contain facet values
<IndexSet SetName="Simple2IndexSet" IndexPath="~/App_Data/SimpleIndexSet2">
        <add Name="Column1" Type="FACET" />
        <add Name="Column2" Type="FACET" />
        <add Name="Column3" />
        <add Name="Column4" Type="FACETPATH" />
        <add Name="Column5" />
        <add Name="Column6" />

How to use

A “facet” is represented by the class FacetKey that has a field name and a value. For instance all nodes with the value “v0” in the field “Column1” will have FacetKey(“Column1”, “v0”)
The search results you get from Examine have a new property called FacetCounts. This contains the count for all facets for all the nodes that matched your query. You can get the count for a known facet by using results.FacetCounts[new FacetKey(“Column1”, “v0”)];
You can also iterate the FacetCounts in which case you get key-value pairs with FacetKey and count. The following Razor snippet lists all facets and their count from a search result.
@foreach( var facetCount in results.FacetCounts ) {
   <div>@facetCount.Key: @facetCount.Value</div>

If you want the most frequent facets you can use the method FacetCounts.GetTopFacets(count). This method uses a priority queue and is significantly faster than simply sorting. This method also allows you to get the most frequent facets with specific field names.
The example below returns the top 10 facets with the field names "Tags" and "Authors"
@foreach( var facetCount in results.FacetCounts.GetTopFacets(10, "Tags", "Authors") ) {
   <div>@facetCount.Key: @facetCount.Value</div>

You can filter your search results by facets with the new extension method Facets that can be combined with Examine’s existing query methods. So to filter the results by some facets you write

   searcher.CreateSearchCriteria().Facets(new FacetKey("Column1", "a0"), new FacetKey("Column4", "Tax2"))
                .And().Field("Whatever", "Foo");

That is how you make “drill-down”, e.g. filter by some facets the user has selected.

Type=”FACETPATH” is a special kind of facets where hierarchical values are separated by “/”. So if your node has the value “Something/Sub1/SubSub4” this single value will result in the facets “Something”, “Something/Sub1” and “Something/Sub1/SubSub4”.

Configuring from code

When it comes to facet you may want some custom logic that splits or otherwise transforms values. A good example is a tag picker in Umbraco. In that case you can add custom fields to Lucene documents and register custom facet extractors in the App_Start event in your global.asax.
This is what this example does:

var searcher = ExamineManager.Instance.SearchProviderCollection["Simple2Searcher"] as LuceneSearcher;

            //Here a facet extractor is configured from code
            var config = searcher.FacetConfiguration ?? new FacetConfiguration();
            config.FacetExtractors.Add(new TermFacetExtractor("CustomDocField"));

            var ix = ExamineManager.Instance.IndexProviderCollection["Simple2Indexer"] as LuceneIndexer;

            //Here custom fields are written directly to the document regardsless of Examine's config
            ix.DocumentWriting += (sender, args) =>
                    string v;
                    if( args.Fields.TryGetValue("Column1", out v))
                        //Here the umbraco value of a tag picker could be split into individual tags  

                        //This is how to add a facet with level (i.e. size/importance)
                        //Remember to use a float value. Not int.
                        args.Document.Add(new Field("CustomDocField", new PayloadDataTokenStream(v + "_WithLevel").SetValue(.25f)));
                        //Here we add a normal field
                        args.Document.Add(new Field("CustomDocField", v + "Test2", Field.Store.NO, Field.Index.NOT_ANALYZED));

If in doubt look at the HomeController’s Search method in the Examine.Web.Demo project.

Last edited Feb 27, 2013 at 1:40 PM by nielskuhnel, version 4