Wednesday, January 2, 2019

Extending SXA search queries with SearchQueryTokens

Tokens in Sitecore Search

SXA comes with a set of tokens that you can use in search queries to apply additional search filters.

They can be very useful when creating queries that will be used on multiple pages. In my case, I wanted to extend a search scope with some business logic based on the value of a field on the current page.

The "ItemsWithTheSameValueInField" came close to what I needed but not entirely..  but these tokens can be easily extended. Let's implement our own ResolveSearchQueryTokensProcessor!

ResolveSearchQueryTokens Processor

To start my implementation, I had a (dot)peek at the ItemsWithTheSameValueInField source code as mine would be rather similar. First thing to do is define the key for our SXA token:
[SxaTokenKey]
protected override string TokenKey => FormattableString.Invariant(FormattableStringFactory.Create("{0}|FieldName", "YourTokenName"));

Next thing is override the Process method:
public override void Process(ResolveSearchQueryTokensEventArgs args)
{
  if (args.ContextItem == null)
    return;

  for (var index = 0; index < args.Models.Count; ++index)
  {
    var model = args.Models[index];
    if (model.Type.Equals("sxa", StringComparison.OrdinalIgnoreCase) && ContainsToken(model))
    {
      var fieldName = model.Value.Replace(TokenPart, string.Empty).TrimStart('|');
      var field = args.ContextItem.Fields[fieldName];
      if (field?.Value != null)
      {
        args.Models.Insert(index, BuildModel(fieldName, field.Value));
      }

      args.Models.Remove(model);
    }
  }
}

Let's see what is happening here:

Find our SearchStringModel

The code loops over all the SearchStringModel objects in the arguments. Each SearchStringModel defines a part of the query and has a value, a type and an operation.  When looking at the search box interface like in the above image:
  • the type of the SearchStringModel is the first part (in the image above that would be "Sxa") 
  • the value of the model is the second part (e.g. "ItemsWithTheSameValueInField|FieldName")
  • the operation is "must", "should" or "not"

So we search until we find a model of type sxa. The ContainsToken function adds this check to make sure we have our the token that corresponds with our code:
protected override bool ContainsToken(SearchStringModel m)
{
  return Regex.Match(m.Value, FormattableString.Invariant(FormattableStringFactory.Create("{0}\\|[a-zA-Z ]*", "YourTokenName"))).Success;
}

If the model is found, we fetch the provided FieldName and test if the context item has a value in this field. If that is the case, we build a new SearchStringModel to be inserted in the list. The current model is removed as that has been handled.

Build a SearchStringModel

protected virtual SearchStringModel BuildModel(string fieldName, string fieldValue)
{
  var name = SomeBusinessLogicWithFieldName(fieldName);
  var value = SomeBusinessLogicWithFieldValue(fieldValue);
  return new SearchStringModel("custom", FormattableString.Invariant(FormattableStringFactory.Create("{0}|{1}", name, value)))
  {
    Operation = "must"
  };
}

I am building a SearchStringModel based on the field name and value. The model type is "custom" as we are not using predefined Sitecore fields. You can test such queries in a search box as well. If you would use them in a SXA scope, the scope string would look like "+custom:fieldName|fieldValue".


ResolveSearchQueryToken configuration

To tell Sitecore that we have our own tokens, we have to add the processor to a pipeline:
<sitecore>
  <pipelines>
    <resolveSearchQueryTokens>
   <processor type="YourNameSpace.YourType, YourAssembly" resolve="true" patch:before = "*[1]" />
    </resolveSearchQueryTokens>
  </pipelines>
</sitecore>
With this patch I am including our tokens as the first one in the list.

Once this config patch and the code is available, your Sitecore instance will detect the SxaToken and it will be available in the search list once you type "Sxa".


Conclusion / Issues

I have used this succesfully in scopes with SearchResults components to make them aware of the context item (adapting the actual query based on a field value of the current page). You can of course make the code as complex as you would like
  • pass more parameters in
  • create multiple models based on your data
  • ...

I did find one issue though: in the SearchResults component we have a ContextItem. But that is not always the case. Our code will not break (we do check for null) but the extra filtering is not happening in that case. Sxa's checklist facet component -in 1.7.1- does not send the ContextItem and it does make sense that a facet uses the same data as the corresponding results component.
I've created a Sitecore Support ticket to fix this and did get a patch (ref. 301406).

But (again) a very nice extension point for the Sitecore Experience Accelerator.

Thursday, December 20, 2018

SXA Pagination on a custom component

SXA Pagination component


The SXA pagination component adds a paging feature to your lists. Out of the box, it can work together with the (also out-of-the-box) PageList, FileList and EventList components. But what if you want this to work with your own component?

A custom paginable component


Sitecore StackExchange to the rescue! I found this nice answer https://sitecore.stackexchange.com/q/8105/237 that made me think it was going to be easy...

So for starters I have:
  • a controller with an action method
    (and a view with variants and such, but that is not in scope here - I've written another post if you want to know how to create our own rendering with variants)
  • a repository that gets items from the index (based on a Search scope.. might be my next post)
As I am getting too many items at once, I would like to add paging to this component. Preferably the out-of-the-box paging component (so I don't need to write it).

The answer on SSE indicated that a bit of configuration would do it.. but unfortunately it did not. My repository does not inherit from ListRepository (nor implements IListRepository) as that doesn't fit my needs. The steps I had to take:

Configuration in Sitecore

I'm not going to describe how to add your rendering to Sitecore and SXA - that is described in the official docs.

Your rendering does need rendering parameters. The rendering parameters temlate for your component should inherit IPaginable and IPagination (both from the Feature/Experience Accelerator/Page Content/Rendering Parameters folder).

This will give you the necessary parameters to make your component compatible with the pagination.

The parameters are described in the official docs on the pagination subject but your main focus is:
  • Page Size: the number of items on a page
  • List Signature: a string that will be used as signature - the same string needs to be entered in the rendering parameters of the pagination component to tell both renderings that they belong together. Note that this string will also appear in your querystring when paging..

The configuration mentioned in the SSE answer is now moved into the Sitecore item. So, on your rendering item you need to add IsPaginationEnabledRendering with a value true to the OtherProperties.


Changes to the repository

Our repository has a function GetModel to create the model for the controller. It actually calls VariantsRepository.FillBaseProperties and adds the items we fetch from the index to the model - the model inherits from VariantsRenderingModel.

This function needs an extra parameter of type IListPagination. This parameter will provide the Skip and PageSize information that we need to get the right page from the data. So this is the place where we reduce the full data set to the requested page.

Change to the controller

To be compatible with the Pagination component, our controller needs to derive from the PaginableController. We had to override 2 functions:

PaginationConfiguration

protected override IListPagination PaginationConfiguration
{
  get
    {
      var listPagination = ListPaginationContext.GetCurrent().Get(Rendering.Parameters["ListSignature"]);
      if (listPagination != null)
      {
        return listPagination;
      }

      var parameters = Rendering.Parameters;
      var scope = parameters["Scope"];
      return new ListPagination(parameters.ToDictionary(p => p.Key, p => p.Value), searchResultsRepository.GetItems(scope).Count());
    }
}
The first function to override is the one that get the PaginationConfiguration. The only changes made to the original function are the call to the repository - this needs an extra parameter in our case (and is not the default ListRepository as mentioned before).

Once this function is in place, use it to pass the PaginationConfiguration to the repository (GetModel - see above). At this point you should see that your component already can select the correct page from the data.


OnActionExecuting

We couldn't see any pagination yet though. To get that working, we needed to override one more function:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
  if (ListPaginationContext.GetCurrent().Initialized)
  {
    return;
  }

  var paginationService = ServiceLocator.ServiceProvider.GetService<IPaginationService>();
  foreach (var sitecoreRendering in Sitecore.Mvc.Presentation.PageContext.Current.PageDefinition.Renderings.Where(r => paginationService.IsPaginationEnabledRendering(ID.Parse(r.RenderingItemPath))))
  {
    var scope = Rendering.Parameters["Scope"];
    var itemsCount = searchResultsRepository.GetItems(scope).Count();
    ListPagination listPagination;
    if (itemsCount <= 0)
    {
      var parameters = sitecoreRendering.Parameters;
      listPagination = new ListPagination(parameters.ToDictionary(p => p.Key, p => p.Value), searchResultsRepository.GetItems(scope).Count());
    }
    else
    {
      var parameters = sitecoreRendering.Parameters;
      listPagination = new ListPagination(parameters.ToDictionary(p => p.Key, p => p.Value), itemsCount);
    }

    IListPagination pagination = listPagination;
    if (!ListPaginationContext.GetCurrent().Contains(pagination))
    {
      ListPaginationContext.GetCurrent().Add(pagination);
    }
  }

  ListPaginationContext.GetCurrent().Initialized = true;
}
This function will initialize the Pagination Context. But again, by default it will use the ListRepository and we didn't have that one - so we had to override it and use our own repository.

Conclusion

It's no rocket science if you know how to do it.. :)

I did spend some time figuring this out - but next time it will go much much faster and I will have paging on my components in no time. And maybe with this guide you will too..

ps: using SXA 1.7.1 at time of writing

Monday, October 29, 2018

Sitecore 9.1 Forms - Prefilling fields & security

Sitecore 9.1 Forms

The Forms feature has 2 major new features in version 9.1 of the Sitecore platform. You'll get conditional fields and prefilling of fields. It's about that last one I wanted to give you a little bit of advice...

Prefilling fields

Without going into too much details (Sitecore 9.1 is only released in tech preview at the time of writing - release version will be available soon) the prefilling is based on value providers - classes that will get the values based on a parameter defined with the field. 

Prefilling is a nice feature, as it makes the user experience better and the conversion rate of the forms higher - visitors are lazy ;)

But.. before you jump into prefilling everything you think you know about me, read this article: https://www.campaignion.org/blog/all-you-have-know-about-pre-filling-forms

It is a good overview of what you can an can't do when prefilling forms. And there is one option related to Sitecore that I want to address.

Prefilling xConnext data

Sitecore environments usually have (a lot of) information about their visitors in the xDB database. It is very tempting to use that data to prefill the forms. But there might be catch..  

Consider the following scenario (which is not so far fetched) on a website:
  • a form with just an e-mail address to subscribe to the newsletter
  • a contact form (with email, name, address, ...)
  • all forms will identify the contact
  • all forms use prefilling on fields where possible (all ootb xXonnect fields)

So, what is the catch? Well, if I subscribe you (yes, you.. not me) to the newsletter and the system already knows you it will attach my session to your profile. No harm done, until I visit the contact form and see all your contact data. 

How do you prevent this? You probably want to keep the contact identifcation as that gives your marketers the information they want in the xConnect reports. Adding a Captcha to your form will help a bit (and this might be a good idea for other reasons as well). But actually, the only good solution is -as described in the mentioned post above- to only prefill data from a datastore (like xDB) when your user is really identified - meaning logged in or truly identified through an identifier in the querystring. 

Conclusion

Prefilling is a nice new feature (and it looks better than the WFFM version). And I hope the community will start sharing value providers. But be aware of what you do with your visitors data.. after all, you don't want to end up in a next version of Mikkel Rømer "white hat hackers" session.

Wednesday, October 24, 2018

What's new in SXA 1.8

In the very near future, SXA 1.8 will be available for all - together with Sitecore 9.1.
I had the privilege to take an early look and it looks good.  So ..

What's new?

I'll try to go over the new stuff, based on my notes from the Symposium session by Adam, things I heard in the community and the documention - and provide screenshots were possible.

Installation

Let's start in the beginning. The installation guide mentions "Sitecore Experience Platform 9.0 update 2 or 9.1" as prerequisite. So no Sitecore 8 anymore.. 

As the need to support Sitecore 8 limited the SXA team for certain features, this is a good evolution.

For a developer

  • Bootstrap 4

    This one was announced so no big news, but front-end people will still be very happy that this made the release. Bootstrap 4 comes as an extra option for the grid system - bootstrap 3 is also still available (probably for upgrade scenarios).
  • Link field best practices

    This is a Powershell script available on templates (folders) that will verify the source fields of the templates and suggest better options regarding multisite solutions (e.g. the use of the $site token).
    I must admit that when looking for this script, I found even more interesting (already existing) scripts that come with SXA. Nice...

  • Embedded renderings in rendering variants

    Personally I think this is one of the coolest new features in this version. When this was demoed in the Symposium session, quite some people apparently had no clue what to do with this. But it gives us even more options to get the renderings just the way we want them. Yes, no we can add a breadcrumb within a hero section filled with just page content... and our logic stays were it should be (in the breadcrumb component). I'm sure I will use this one! (to be honest, I already did try this on my preview-test environment and it works like a charm.. )
  • Adding modules

    On tenants and sites, we get scripts to add (missing) modules. Running the script will list all missing modules and you can install the selected ones.
    But, we can also do this the other way: adding a (new) module to multiple sites at once. On your module in /sitecore/System/Settings/Foundation/ or /sitecore/System/Settings/Feature/ you'll find the script. 
  • Custom html (views) per site

    The SXA components have their views, located in the Views folder. That remains the default and fallback location. But you can on each site define -on the Settings item- a CustomRenderingViewPath. This path can contain custom views for the renderings in that site. They need to use the same names and folders - if they do not exist the default is used (so you don't need to copy all views if you just want to change one of them).
    Just like the previous topic a nice feature for multi-site solutions.

For an editor

  • Defining the sections of the toolbox

    Up until version 1.7.1, the toolbox sections were determined by the paths in your renderings folder in the layouts section of Sitecore. This was not very user friendly as you want to group your renderings based on their feature (Helix...). That makes sense to the developers but not to the editors. We already had the "available renderings" section for each site but that wasn't used due to .. well, they had their reason in the early versions but that was gone now. So now we have the option to define the sections in the toolbox based on the available renderings. Note that by default the necessary checkbox (on the "available renderings" item in each site) is off.  As I asked for this one, I'm very happy to see it..
  • Updated editing modes

    The wireframe mode got a revamp and is looking quite a bit better. And a new mode was added: grayscale.
  • Highlighting partial designs

    When hovering over a partial design in the Partial Designs dropdown section in the Experience Editor the current partial design is shown in green border(s) in the editor. This makes it clear for editors where the partials are located and what they contain.
  • Cross site linking

    This might seem very trivial but in a multisite SXA environment it's not always so. You now get the option to set on each site what the "linkable" sites are.
    By default it is the current site only, but you can select all linkable sites or all linkable sites from the current tenant. To define a site as "linkable", you should mark the checkbox in your site's "settings/site grouping/site".


For visitors

WCAG 2.0 compatibility


Some additions were made for the accessibility compliance of SXA. On my test environment the "tab" key seemed to work, and I added the "skip" meta component:




Conclusion

I can be very short on this: it seems like a very nice version with some exciting features. And the upgrade guide is only 9 pages... :)

Monday, October 22, 2018

Using tokens in Sitecore SXA variant data attributes

Sitecore SXA variant definitions


Variants are an important part of SXA. Although SXA is so much more, learning how to use these is rather crucial for anyone who want to create sites with SXA. If you need more information on this, a good place to start are these official docs:
You can do a lot with these variants. Especially when you discover the power of the "Reference" in a variant definition, lots of scenario's become possible...  and there are some hidden gems. 

Our case

We had to create a rendering that displayed blocks of content - so far no problem, this is just displaying fields from a list of items. But the editor had to be able to set a background on each individual block (and some other properties). Short version: we had to be able to set a css class on every block. We could have asked our editors to create a rendering for each block they needed and set parameters on that rendering, but as we had reasons not to do that.

So we started looking at the variant definitions options, tried a few things without luck and ended up asking help on Stack Exchange: https://sitecore.stackexchange.com/q/12806/237

As usual, the SXA dev team came to the rescue (- thx Dawid for this one) and showed me a little hidden gem.

Using tokens in data attributes

Apparently you can use some tokens in the data attrbutes that are available in each part of the variant definition. As shown in the answer on SSE, we can use "class" as data attribute and fill that with a value from a field on the current item by using the $(fieldName) notation.

This means we can use a Reference in our variant to loop over a set of items, and for each of those items render a section (e.g. "div") with a class on it. That'll do it..

But.. we had one small issue. The default implementation will work with text fields. But for link-type fields it will generate a link to the target item. We don't want our editors to type values for fields like this - it should be a selection. So we got a patch from Support that extended this functionality to ValueLookupFields to fetch the value.

Now we could give our editors a dropdown selection of styles and use those in our variant. That's what we needed! (as soon as the site goes live, I can add a screenshot of the result)

I had a look at what Support provided for us and although it is not designed to be expanded easily, we can do even more with this (and so we did).

public class RenderSection : Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection
{
 protected override string GetAttributeTokenValue(string fieldName, Item item)
 {
   Field field = item.Fields[fieldName];
   if (field == null)
  return string.Empty;
   ....
   return base.GetAttributeTokenValue(fieldName, item);
 }
}
<sitecore>
    <pipelines>
      <renderVariantField>
        <processor patch:instead="*[@type='Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.XA.Foundation.RenderingVariants']" type="Sitecore.Support.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.Support.00000" resolve="true" />
      </renderVariantField>
    </pipelines>
</sitecore>

We noticed they had overwritten the function that handles the tokens in case of a variant section (if you want this to work on other variant options like text or field, you need to overwrite those too). Where Sitecore/SXA is designed to be extended, they usually make it easier for us :)

So as mentioned, it is not (yet) designed to be expanded but it can. You could write any logic you want in the above funtion based on the "fieldName" (which propable doesn't even need to be an existing field name).

This opens a new set of possibilities to render everything we need with SXA variants!

Just one little request for a future version:  make this easier to extend.. ;)

Wednesday, August 8, 2018

Sitecore SXA, rules engine & containers in partial designs

SXA and partial designs

While working on a Sitecore site with SXA I created several partial designs (as you should when working with SXA). What a partial design is, and how it is used to create page designs that define the layout of your pages in SXA can be read in the official docs.

What you should know about partial designs is that you edit them on the partial design item itself, not on the pages where they are used. On the final page in the experience editor, you can edit the content shown by the partial design but you cannot change the design itself (add, remove or change components or their properties). Still, partial designs are very useful and help you keep the number of components on a page reasonable. On many occasions though, you will want the editors to be able to add components to the page that are not preset in the partial design. This can be done by adding a "container" component in the partial design (note: this is one way - not necessarily the way) on the spot where you want to give the editors a new placeholder (a placeholder is a spot where new components can be added). And so we did...

And then came the designers... and they gave our pages a very nice design. But they had a problem: empty containers!

The containers that were added to the partial design might remain empty. And with our design, this made it hard for the designers to get the required result. So: lets remove those empty containers.

SXA & the Container component

As mentioned, the container component was just added to have a placeholder available. It's a typical "structure" component that adds a wrapper for other renderings.

In the experience editor we want the placeholder to be always available (to be able to add components) but in normal or preview mode we want it gone when no renderings were added. 

The container component generates a placeholder named "container-x" where x is a dynamic placeholder id. 
Hint: when using the container, you should also set the placeholder restrictions for the generated placeholders - as you should do always probably ;)  More info on how to do this with SXA can be found on the official docs.
 To remove the components, I used the Sitecore rules engine. 

Using the rules engine

We want to use conditional rendering to hide the component when the related placeholder is empty. The action to hide a rendering is out-of-the-box available, so we only need the "condition". 

In this condition, we first check the PageMode to make sure we are in normal or preview mode.

We could ask the administrator adding the rule and using our condition to pass the related placeholder as a parameter (the easy way) but we can actually retrieve it.  The rendering (container) has a parameter called DynamicPlaceholderID that contains the generated dynamic placeholder id. In our case we hard-coded the prefix "container-" but you might do something pretty with that as well.

Last thing to do is find out if there is a rendering on the page in that placeholder..  which can be done with a query on the renderings list. 

Let's wrap this up and show the code:
public class WhenPlaceholderIsEmpty<T> : WhenCondition<T> where T : RuleContext
{
    protected override bool Execute(T ruleContext)
    {
        Assert.ArgumentNotNull(ruleContext, "ruleContext");
        if (!Sitecore.Context.PageMode.IsNormal && !Sitecore.Context.PageMode.IsPreview)
        {
            return false;
        }

 var conditionalRenderingsRuleContext = ruleContext as ConditionalRenderingsRuleContext;
        if (conditionalRenderingsRuleContext == null)
        {
            return false;
        }
  
        var dynamicPlaceholderId = HttpUtility.ParseQueryString(conditionalRenderingsRuleContext.Reference.Settings.Parameters).Get("DynamicPlaceholderID");
        if (dynamicPlaceholderId == null)
 {
            return false;
        }

        if (conditionalRenderingsRuleContext.Item.Visualization.GetRenderings(Sitecore.Context.Device, false)
            .ToList().Any(r => r.Placeholder.EndsWith("container-" + dynamicPlaceholderId, StringComparison.OrdinalIgnoreCase)))
        {
            return false;
        }

        return true;
    }
}

To quickly add this to condition to Sitecore, go to /sitecore/system/Settings/Rules/Definitions/Elements and create a new Element Folder.  In the created folder find tags/default and select Conditional Renderings as tag. In the root of the created element folder, add a new Condition. Fill in the Text like "when the placeholder in the container is empty" and add the Type (referal to the class above). Don't forget to publish the created items ;)

Personalization

Go to a partial design that contains a Container rendering. Open the presentation details (in content editor - you might also use the experience editor) and edit the controls. Use the personalization feature on the Container rendering and add a personalization rule using the newly created condition and the standard available "hide" action.  Save & publish.


Conclusion

Partial designs are a great way to organize your pages in SXA. And yes, they do put have some limitations. But it wouldn't be Sitecore if there was no way to do something about that and the rules engine is a rather powerful tool to help you achieve what is needed.

Monday, July 23, 2018

Overview pages with SXA search components

Sitecore SXA search components

Sitecore eXperience Accelerator comes with a bunch of components to create a search solution for your site. A few years ago -with SXA 1.1- I wrote a post on (some) of the search components and how to use them to create a site search. 

It wouldn't be Sitecore nor SXA when you could not tweak this a lot.. you can create specific searches, facetted results, and lots more..  Just check out the official docs.

When I found out that we can trigger the search results without user interaction I saw new possibilities.  We can create overview pages with the search components! This is no rocket science but probably worth sharing for people who are not so acquainted with SXA yet.


Create an overview page with SXA search components

Let's explain our setting: we have a number of products and want to create a page where the visitor can easily find the product (s) needs. We'll show the products and let them be filtered based on a few facets in order to get the visitor quickly to the wanted products with a link to the details.

So we have items of a template "Product" that are located in folders underneath our "product root node". 

Search scope

First thing to do is define our search scope. A search scope is used to limit the search results based on a query. Go to /Settings/Scopes in your sites folder to define a scope. In our case we created a Product Scope with a query on location (our product root node) and template (our product template). Use the "Build Query" dialog to create the query you need (the dialog performs the actual query so can preview the results). 



Search results

We can already create the search results to see if our listing is working. Select a page (or partial design) where you want the overview. Go to the experience editor and from the SXA toolbox drop the Search Results component on the page. 

The datasource of the search component is not really important here. This item contains the "Results not found" text, but we should always have results. 

What is important though are the properties of the control in the SearchCriteria section.
Make sure to:
  • select the scope (your created scope should be available here)
  • select "Automatically fire search when no criteria set" - this will make sure that your search is done automatically on the page




Variant
You'll probably want to create a variant for your search results. Create a new variant definition under /Presentation/Rendering Variants/Search Results in your site and select the variant on your search results component. In this variant you can define which fields (from the Product template) should be shown. Don't forget to set "Is Link" on at least one (the title) to have a link to the detail page.
More information on creating a variant on the official doc site.



More...
You can extend your page with more search components as needed. You'll probably want the "Page Selector" and "Page Size" components, maybe also the "Sort Results"..    More info on the available components for search can be found on the official doc site.

Facets

Next step is to add our facets to have filters on the results. Our template contains a few fields that we want to filter on. In our case those fields were of type DropList. This means that the raw value of the field is actually just a Guid. Of course, you don't want to display guids to your visitors..  so we had to add extra fields to our index to do this. 

Facet index field
It's been asked on StackExchange, but the information can be found on the official doc site. And yes, it's as simple as adding a few definitions for computed fields (not even code, as the code has been provided by SXA).

To be able to create the facet to a field that has a guid, add the following in a config patch file (check the config file for your search provider for the exact location where to patch this):
<field contentfield="title" fieldname="referenced" referencefield="link" type="Sitecore.XA.Foundation.Search.ComputedFields.ResolvedLinks, Sitecore.XA.Foundation.Search"></field>
where:
  • fieldName: defines the index field name
  • referenceField: name of the field in your template
  • contentField: name of the field to be fetched from the referenced item
Don't forget to rebuild your index after these changes...

Once we have the field in our index, we can create the facet in SXA.

SXA Facet
We will use facets to refine the search results. Facets in SXA are added to /Settings/Facets in your site. 

There are a few facet types (bool, date, integer, list ...) - we used list types in our case. It might be a bit tricky to get the "Field Name" field correctly as this has to be like in your index. Easiest way to be sure is to check your index (e.g. use the Solr admin interface) to find the exact field name in the index. In my case it was "fieldName_sm" (where fieldName is the one from our patch above). 

Adding the facets to the page
Last thing we need to do is add the facet components to our page, using the created facet settings.
Go back to the page (item) where you added the search results and open it again in the experience editor.

For each facet you have, add the Filter component you want. I used Dropdown and Checklist, but this will depend on what you actually want to filter on and how you want to present it to the visitor. 

When the component is dropped on the page, create a new datasource for it. In this datasource item, you can alter a few texts used on the component but the most important one is the Facet field. Select the appropriate facet here.

The results

When all this is done and published, you should have a nice overview page that has filters, paging, .. and everything you need to get your visitors to your detail pages. Good luck!

Extra

You will notice that filtering the results creates a new url each time. This means we could immediatly link to a filtered version of our page if we would want to...


ps: we are using SXA 1.7.1 at the time of writing