Using Solr to group search results
You can group search results, based on the value of a common field
Note
This topic is valid for Sitecore 9.0 and later.
Solr has a result grouping feature. You use this feature to sort documents into groups, based on a common field value. Solr returns the top documents for each group.
For example, if a user searches for "DVD", Solr can return three categories such as "TV and Video," "Movies," and "Computers," with three results per category. In this case, the query term "DVD" appears in all three categories. Solr groups the results together in order to be more useful for the end-users.
You do not have to configure anything in Solr to use this feature.
There is more information here:
https://cwiki.apache.org/confluence/display/solr/Result+Grouping
Sitecore has an API that you use to access the result grouping feature of Solr. The API is designed as an extension to the existing ContentSearch API.
Follow these steps to use the API in your project:
Reference the following DLL files into your project (you can get them from the
bin
folder of your Sitecore website):SolrNet.dll
Sitecore.ContentSearch.dll
Sitecore.ContentSearch.SolrProvider.dll
Sitecore.ContentSearch.SolrNetExtension.dll
Import the following namespace:
Sitecore.ContentSearch.SolrNetExtension
(an extension of SolrNet that contains the implementation of the SuggesterComponent)
You can now use the Result Grouping API as part of ISearchIndex
:
Create your query as string or any implementation of
ISolrQuery
.Call
IProviderSearchContext.Query()
method and inspect the returned result of typeSolrQueryResults<T>.
The returned result contains a Grouping
property that is a Dictionary<string, GroupedResults<T>>
, where Key
is the group field name and Value
is an object of GroupedResults
that represents the count of unique matching documents that are grouped along with the list of grouped documents (ICollection<Group<T>>
).
Each Group<T>
object contains the Solr documents, group value, and the number of documents in the group.
For example:
![]() |
Query
Executes the query against Solr.
Syntax
public static SolrQueryResults<T> Query<T>( this IProviderSearchContext context, string q, QueryOptions options )
Parameters
Context
Type:
Sitecore.ContentSearch.IProviderSearchContext
The search provider context
q
The Solr query string
Options
Type:
SolrNet.Commands.Parameters.QueryOptions
Query options
Type parameters
T
The type of the result item
Remarks
The method will execute the query (the first argument) against Solr with the defined set of options (the second parameter) and retrieve the result as a dictionary of values.
The following example illustrates the basic usage of result grouping. <%@ Page Language="C#" AutoEventWireup="true" %> <%@ Import namespace="Sitecore.ContentSearch.SolrProvider.SolrNetIntegration" %> <%@ Import namespace="Sitecore.ContentSearch" %> <%@ Import namespace="Sitecore.Configuration" %> <%@ Import namespace="Sitecore.Data" %> <%@ Import namespace="Sitecore.Data.Items" %> <%@ Import namespace="SolrNet.Commands.Parameters" %> <%@ Import namespace="Sitecore.ContentSearch.SearchTypes" %> <%@ Import Namespace="Sitecore.SecurityModel" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Solr | Grouping results demo</title> <script runat="server"> private void PerformSearch(object sender, EventArgs e) { using (var ctx = ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext()) { var input = string.IsNullOrEmpty(searchTextBox.Value) ? "*" : searchTextBox.Value.Replace(" ", "+"); string q = String.Format("(name_t:{0} OR color_t:{0}) AND _parent:110d559fdea542ea9c1c8a5df7e70ef9", input); var options = new QueryOptions { Grouping = new GroupingParameters { Fields = new [] { "type_t" }, Limit = 10 } }; var result = ctx.Query<SitecoreUISearchResultItem>(q, options); StringBuilder htmlBuilder = new StringBuilder(); foreach (var group in result.Grouping) { foreach (var grp in group.Value.Groups) { htmlBuilder.Append("<div class=\"group\">"); htmlBuilder.AppendFormat("<div class=\"group-header\">{0}</div>", grp.GroupValue); htmlBuilder.Append("<div class=\"group-item\">"); foreach (var item in grp.Documents) { htmlBuilder.AppendFormat("<span>{0} ({1})</span>", item["Name"], item["Color"]); } htmlBuilder.Append("</div>"); htmlBuilder.Append("</div>"); } } searchResults.InnerHtml = htmlBuilder.ToString(); } } private void Page_Load(object sender, EventArgs e) { if (!IsPostBack && Request["create"] == "true") { using (new SecurityDisabler()) { GenerateTheItems(); } } } #region Create the demo items private void GenerateTheItems() { var master = Factory.GetDatabase("master"); if (master.GetItem("/sitecore/templates/sample/Vehicle") != null) { return; } var sampleTemplates = master.GetItem("/sitecore/templates/sample"); var vehicleTemplate = CreateTemplate(master, sampleTemplates); var home = master.GetItem("/sitecore/content/Home"); CreateItem(home, vehicleTemplate, "Toyota", "Corolla", "Red", "Sedan"); CreateItem(home, vehicleTemplate, "Toyota", "Avanza", "Blue", "MPV"); CreateItem(home, vehicleTemplate, "Toyota", "Camry", "Blue", "Sedan"); CreateItem(home, vehicleTemplate, "Honda", "Civic", "White", "Sedan"); CreateItem(home, vehicleTemplate, "Perodua", "Myvi", "White", "Hatchback"); CreateItem(home, vehicleTemplate, "Perodua", "Myvi", "Red", "Hatchback"); Response.Write("Done!"); } private void CreateItem(Item home, TemplateItem vehicleTemplate, string model, string name, string color, string type) { var car = home.Add(model, vehicleTemplate); car.Editing.BeginEdit(); car["Color"] = color; car["Type"] = type; car["Name"] = name; car.Editing.EndEdit(); } private TemplateItem CreateTemplate(Database master, Item parent) { var vehicleTemplate = master.Templates.CreateTemplate("Vehicle", parent); var dataSection = vehicleTemplate.AddSection("Data"); dataSection.AddField("Type"); dataSection.AddField("Color"); dataSection.AddField("Name"); return vehicleTemplate; } #endregion </script> <style> .group { font-family: sans-serif; border: #aecce6 solid 1px; margin: 10px; } .group-header { font-size: larger; font-weight: bold; background-color: aliceblue; padding: 10px; } .group-item { padding: 10px; display: grid; } </style> </head> <body> <form id="form1" runat="server"> <div align="center"> <input type="text" id="searchTextBox" style="width: 80%;" placeholder="Enter car model or color" runat="server" autocomplete="off" /> <asp:Button Text="Search" OnClick="PerformSearch" runat="server" /> </div> <div id="searchResults" runat="server"></div> </form> </body> </html>