1

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)

Eric Herlitz
  • 25,354
  • 27
  • 113
  • 157
Greg B
  • 14,597
  • 18
  • 87
  • 141
  • A few questions. Can your child pages, i.e. Page 1, Page 2 have its own child pages? Are the pages of the same types? – Eric Herlitz Jan 11 '17 at 09:18
  • @EricHerlitz The child pages don't themselves have children. Page 1,2 and 3 are all the same type, and the category pages are all of the same CategoryPage type. – Greg B Jan 11 '17 at 10:34

1 Answers1

3

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 :)

Eric Herlitz
  • 25,354
  • 27
  • 113
  • 157
  • Thanks for this Eric. I've just given it a go and section and subsection return the first two parts of the path, correct? In my question I was trying to simplify my example by putting my pages under the site root but in actuality the pages are 3 levels deep. Is it possible to get the _parent-page's path segment_? – Greg B Jan 11 '17 at 15:55
  • Correct, that is what they are supposed to do. And it is possible to extend the UnifiedSearchHit but not a very clean approach. In such cases I'd recommend using projections. I'll write an example on this when I get a minute, but the idea is to project what you want onto the `SearchSection` or `SearchSubsection` – Eric Herlitz Jan 11 '17 at 16:09
  • @GregB Did update the example with a custom field mapping which will be better than projections – Eric Herlitz Jan 11 '17 at 19:04