How can I create a "category" Facet based on the parent page?

1k views Asked by At

I've got a hierarchy of pages such as

- Root
   - Category 1
        - Page 1
        - Page 2
   - Category 2
        - Page 3

I want to use Find to create a filter based on the Category page names. Here's what I've got so far, the bit I can't figure out is line 4

var result = _searchClient.Search()
                  .For(query)
                  .Filter(x => x.Ancestors().Match(rootPageLink.ID.ToString()))
                  .FilterFacet("Categories", x => x.ParentLink) // This doesn't work
                  .HistogramFacetFor(x => x.Price, 100)
                  .Skip((pageNumber - 1) * pageSize)
                  .Take(pageSize)
                  .GetContentResult();

Obviously this doesn't work because Filter() expects a Filter as the second argument but you can see what I'm trying to do. It's roughly analagous to a SQL query like GROUP BY ParentLink and then display info like

Category 1 (2 pages)
Category 2 (1 page)

1

There are 1 answers

3
Eric Herlitz On BEST ANSWER

Considering

  • your structure looks like that
  • you are using UnifiedSearch

The ISearchContent interface of UnifiedSearch contains two useful properties in your scenario

  • SearchSection
  • SearchSubsection

Consider you have a path like this

/returns/misc/yourstuff

When looking at Your Stuff as a unified search hit the SearchSection would be Returns and the SearchSubsection would be Misc

When looking at Misc hit the SearchSection would be Returns and the SearchSubsection would be null or not exist on the hit.

This means

  • The SearchSection is always the first node below the root unless your result is a first level node
  • The SearchSubsection is always the first node below the SearchSection unless your result is a first or second level node

In your scenario the category pages would always be represented as SearchSections.

I'm unable to test this at the moment but something like this should get your started.

var result = _searchClient.Search()
                  .For(query)
                  .Filter(x => x.SearchSection.Exists())
                  .Filter(x => x.SearchSection.Match("yourcategorypage"))
                  .TermsFacetFor(x => x.SearchSection) // create facets if you like, fun stuff
                  //.FilterFacet("Categories", x => x.ParentLink) // This doesn't work
                  //.Filter(x => x.Ancestors().Match(rootPageLink.ID.ToString()))
                  .HistogramFacetFor(x => x.Price, 100)
                  .Skip((pageNumber - 1) * pageSize)
                  .Take(pageSize)
                  .GetContentResult();

== Edit below==

To customize the SearchSection using projections something similar to this should be implemented, assuming you want the SearchSection to always be the closest parent of type CategoryPage.

To implement in your Find Initialization

// use the appropriate datatype if PageData isn't applicable, i.e. ArticlePage
SearchClient.Instance.Conventions.ForInstancesOf<PageData>()
    .ExcludeField(x => x.SearchSection()) // remove the default mapping
    .IncludeField(x => x.SearchSection(true)); // add your own mapping

SearchSection extension method

public static class FieldExtensions
{
    public static string SearchSection<T>(this T page, bool overriding) where T : PageData
    {
        // we need to check if this page is a CategoryPage first
        if(page is CategoryPage) {
            return ""; // I assume nothing?
        }

        var ancestors = loader.GetAncestors(page.ContentLink).OfType<CategoryPage>();
        if(ancestors.any()){
            var closestCategoryPage = ancestors.First();
            return closestCategoryPage.whateverproperty;
        }

        // customs: nothing to declare
        return "";
    }
}

Or something like that, you'll figure it out :)