Tuesday, March 18, 2014

A Sitecore 7.2 feature worth the wait: Search-driven controls

As always, I want to provide both the technical and business benefits to a discussion topic.  I've written about this use case in the past, but with Sitecore 7.2 there is now an out-of-the-box feature that it makes the solution to it very straightforward and available to both the business and technical sides of the house.

Basically, since Sitecore controls are so flexible and can bind to a "datasource" based on any number of conditions, the ability to use search interfaces and results to drive this datasource is a natural improvement to the platform.

So take for example the controls on the right hand side of this page:

A Launch Sitecore "Contributor" page with added controls in the right column

The Articles control at the top is set up to look at a specific field of this "contributor" content type / data template.  All Articles have a checklist field so that we can appropriately credit our contributors with their article contribution.  In this way, the control is going to always show a full list of my articles without my having to go back to our development team and rework the control every time I write a new one (which, at any rate, is not all that often).

Similarly, the control below the articles control shows another list.  This list can be driven by a pre-built query against the entire content tree.  I've always been a fan of this technique, as we can create a nice bridge between our development team and our business team.  The business folks could describe in words what they want:

"I need a list of a all articles that have been tagged for Marketers".

Developers (or really anyone that has become familiar with Sitecore queries could come up with something like this item:
A pre-built search query item


In reality it isn't even necessary to be familiar with this search syntax since the "Build Query" feature within this Content Editor view would allow you to use the search interface within Sitecore to prepare the pre-built query item.  Once the item was built, now the business folks could use this item as a datasource for the control and our use case solution for an ongoing dynamic query control would once again be complete.

(In older solutions to this use case, we used the same technique as above, but used Sitecore Query notation).

Now, in 7.2, using these search queries as control datasources is even more cool and direct.  Let's rework the control on our page.  The good thing is, Chris Castle's Launch Sitecore control is already written so well that it doesn't need changing:

Selecting the LS Queried List control for our page

As the dialog appears to select an associated datasource for the control, we can see that we now get the ability to Search for Content on the left (previously we would have chosen a specific content item):

Searching for associated content

There are two areas we could use here to come up with our result set.  The right hand side of the screen will show us the available facets (template, date range, author, etc.).  These facets are impressive by default and fully extensible to enable the inclusion of specific attributes important to your business.  In addition, the drop arrow on the left of the search box provides a choice to use pre-built search filters.  For the example below, I'll use this technique to choose:
  • Items that match the Article Data Template (they have to be Launch Sitecore articles)
  • Items that were initially created by Mike Casey
Both Template and Article are pre-built search filters, so I can easily choose them from the list.  There's also a nice "intellisense"-like feature that allows me to start typing and see available options based on my keystrokes.

Setting up the search query with pre-built search filters
If I click on the magnifying glass to run the query so far, I notice I have 65 results:

Initial search results for Mike Casey as author, Article as Data Template

My team will tell you that I haven't contributed nearly that many articles.  In fact, in the Template facet on the right, we see that of the 65 items returned, 22 were articles, 9 were images, and so on.  By default this is a logical "or" query, so we're getting too many results for our list's purpose.

By clicking to the left of each chosen search filter, we can toggle through a "must have", "must not have" "can have" logical operator set.  For our list, we want to ensure that both filter criteria are true:

Tweaking the query with logical operators
With the "Must Have" criteria in place and by clicking the magnifying glass again, we get the expected four article result, and we are satisfied with our search query:

Finished product

Now think about the fact that you can set up personalization or multvariate tests on this control using different search criteria.  The search criteria can be coupled with any other conditions Sitecore has access to (GeoIP, campaign, etc.).  You're off an running with a truly dynamic experience for your visitor.
 

Snippet for the Developers:

For the developers, the reason that this already works in your current Launch Sitecore package is that Chris Castle thought through the various datasource items that might be passed.  Here is a little snippet of the resulting logic if the datasource is deemed to be a query string (rather than a specific item GUID).  This full code is available via the DataSourceList.ascx control in the project:
 
        // if the datasource was not a query item try to process the datasource as a query
try
{
//Open search context based off the current item
using (IProviderSearchContext context = ContentSearchManager.CreateSearchContext((SitecoreIndexableItem)(Sitecore.Context.Item)))
{
string languageCode = Sitecore.Context.Language.CultureInfo.TwoLetterISOLanguageName.ToString();
IQueryable<Item> queryable = (from toItem in LinqHelper.CreateQuery(context, UIFilterHelpers.ParseDatasourceString(Attributes["sc_datasource"]))
where toItem.Language == languageCode
select toItem.GetItem());

// the master index will have each version so we need to remove the duplicates.
if (Sitecore.Context.Item.Database.Name.ToLower() == "master")
return queryable.ToList<Item>().Distinct(new ItemIDComparer()).ToList<Item>();
else
return
queryable.ToList<Item>();
}
}