I know, that sounds really complex. But the solution is surprisingly simple, so keep on reading!

And let's do this the proper way: Why would I (or you) need a Custom Search Query Token to filter a shared Search Scope by the Context Home Item?

Lately I had some requirements, where I get in conflict with the search criteria settings of the sites within a tenant.

One task was to create a fulltext search for every SINGLE site within the tenant.

I thought that this should be an easy task in SXA:

  • Create a shared search scope
  • Set the templates which should be searched for in the Query Builder
  • Set the "Associated Content" field for each site in the settings item to the current site (or leave the field empty)

It worked within minutes!

Another task was to grab all news items on a news overview page from ALL sites within the tenant. There was a seperate search scope to query these news items as well.

What to do now? Changing the "Associated Content" field to the "tenant" item works fine. I got all the news items from all sites, but my fulltext search also displayed pages from other sites – which it was not supposed to.

There are a few options to fix this, but first I set up the "Associated Content" field to the "tenant" item. And that's really where this topic begins, because no out-of-the-box solution fits my needs (please feel free to correct me if I'm wrong!).

Three roads lead to Rome

I had to make a decision here on how to proceed. I considered 3 options:

  1. Create a search scope for every single site within the tenant ... Doh! That could be lot of work if there are a lot of sites.
  2. Take a closer look on how the "resolveSearchQueryTokens" pipeline works. The SXA query tokens are also part of this pipeline.
  3. Ask around in the SXA Slack Channel for a better solution.

More options are really appreciated – if there are any – because we are in a multisite environment and the search scopes live within a shared site.

After a bit more research and careful investigating, I decided to go with the custom search query token.

Oh, I promised you an easy solution, but already wrote so much. Sometimes I just want to share the background or the challenge of a task as well! Bear with me, we'll get to the juicy part now!

How to create a Custom Search Query Token with Sitecore SXA

1. Create a new Visual Studio Project

Call it for example "My.Foundation.Search"

2. Copy the "CurrentLocation"-Processor

The processor is located in the "Sitecore.XA.Foundation.Search.Pipelines.ResolveSearchQueryTokens"-Namespace, it looks like:

using Sitecore.ContentSearch.Utilities;
using Sitecore.XA.Foundation.Search.Attributes;

namespace Sitecore.XA.Foundation.Search.Pipelines.ResolveSearchQueryTokens
{
    public class CurrentLocation : ResolveBasicSearchQueryTokens
    {
        [SxaTokenKey]
        protected override string TokenKey
        {
            get
            {
                return "UnderCurrentPage";
            }
        }

        protected override void ProcessModel(
            SearchStringModel model,
            ResolveSearchQueryTokensEventArgs args)
        {
            model.Type = "location";
            model.Value = args.ContextItem.ID.ToString();
        }
    }
}

It was not easy to find the right processor, because the token is called "UnderCurrentPage", but the class is called "CurrentLocation".

But as you can see this is very similar to my requirement. There is a context item, but I need the "Home Item" of this context item, so I have to do some adjustments.

3. Create a class within your project

Paste the code of the original processor and name it "CurrentHomeItem", adjust your namespace for example "My.Foundation.Search.Pipelines.ResolveSearchQueryTokens"

4. Change the TokenKey

First of all, change the TokenKey value. In my case I took "UnderCurrentHomeItem"

5. Do the "magic"

So what kind of magic should happen now? DotPeek magic for sure!
Also difficult to find if you are not used to working in a multisite environment. If you did, you must have already seen the "IMultisiteContext" interface, it's a true gem!

Inject the interface into your class:

private readonly IMultisiteContext _multiSiteContext;

public CurrentHomeItem(IMultisiteContext multiSiteContext)
{
    _multiSiteContext = multiSiteContext ?? throw new ArgumentNullException(nameof(multiSiteContext));
}

Magic is done! 🧙🏼‍♂️

Now set the model value to:

model.Value = _multiSiteContext.GetHomeItem(args.ContextItem).ID.ToString();

And that's it!

6. Add your proccessor to the pipeline

Create a new config file for example: "AppConfig/Include/Foundation/My.Foundation.Search.config" and add your class to the "resolveSearchQueryTokens"-pipeline:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <resolveSearchQueryTokens>
                <processor type="My.Foundation.Search.Pipelines.ResolveSearchQueryTokens.CurrentHomeItem, My.Foundation.Search" resolve="true" />
            </resolveSearchQueryTokens>
        </pipelines>
    </sitecore>
</configuration>

7. Set the token within your query, like this:

QueryBuilder

8. Set the search scope in your search results and / or searchbox

SearchResultScope

Your search results are now displayed based on the "Context Home Item", even if the "Associated Content" field points to the "tenant".

Just keep in mind that on a shared site your query builder will no longer display any results! But on the other sites the query will return exactly the result you expected. 😉

Happy Tokening
Dirk

The author

dirk-autor
Dirk Schäfauer
Dirk bei LinkedIn