Highlight Index Search Results


  • When making a Full-Text Search query,
    in addition to retrieving documents that contain the searched terms in the results,
    you can also request to get a list of text fragments that highlight the searched terms.

  • This article provides examples of highlighting search results when querying a static-index.
    Prior to this article, please refer to Highlight search results for general knowledge about Highlighting,
    and for dynamic-queries examples.

  • In order to search and get fragments with highlighted terms when querying a static-index,
    the index field on which you search must be configured for highlighting,
    see examples below.

  • In this page:


Highlight results - Map index

Configure a Map index for highlighting:


In order to search and get fragments with highlighted terms, the index-field on which you search
must be configured with:

  • FieldStorage.Yes - store the field in the index
  • FieldIndexing.Search - allow Full-Text search
  • FieldTermVector.WithPositionsAndOffsets - store the term's position and offsets

// Define a Map index:
// ===================
public class Employees_ByNotes :
    AbstractIndexCreationTask<Employee, Employees_ByNotes.IndexEntry>
{
    // The IndexEntry class defines index-field 'EmployeeNotes'
    public class IndexEntry
    {
        public string EmployeeNotes { get; set; }
    }
       
    public Employees_ByNotes()
    {
        // The 'Map' function defines the content of index-field 'EmployeeNotes'
        Map = employees => from employee in employees
            select new IndexEntry
            {
                EmployeeNotes = employee.Notes[0]
            };
        
        // Configure index-field 'EmployeeNotes' for highlighting:
        // =======================================================
        Store(x => x.EmployeeNotes, FieldStorage.Yes);
        Index(x => x.EmployeeNotes, FieldIndexing.Search);
        TermVector(x => x.EmployeeNotes, FieldTermVector.WithPositionsAndOffsets);
    }
}

Query the index with Search:


List<Employee> employeesResults = session
     // Query the map index
    .Query<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
     // Search for documents containing the term 'manager'
    .Search(x => x.EmployeeNotes, "manager")
     // Request to highlight the searched term by calling 'Highlight'
    .Highlight(x => x.EmployeeNotes, 35, 2, out Highlightings managerHighlights)
    .OfType<Employee>()
    .ToList();
List<Employee> employeesResults = await asyncSession
     // Query the map index
    .Query<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
     // Search for documents containing the term 'manager'
    .Search(x => x.EmployeeNotes, "manager")
     // Request to highlight the searched term by calling 'Highlight'
    .Highlight(x => x.EmployeeNotes, 35, 2, out Highlightings managerHighlights)
    .OfType<Employee>()
    .ToListAsync();
List<Employee> employeesResults = session.Advanced
     // Query the map index
    .DocumentQuery<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
     // Search for documents containing the term 'manager'
    .Search(x => x.EmployeeNotes, "manager")
     // Request to highlight the searched term by calling 'Highlight'
    .Highlight(x => x.EmployeeNotes, 35, 2, out Highlightings managerHighlights)
    .OfType<Employee>()
    .ToList();
from index "Employees/ByNotes"
where search(EmployeeNotes, "manager")
include highlight(EmployeeNotes, 35, 2)

Query the index with Where:


List<Employee> employeesResults = session
     // Query the map index
    .Query<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
     // Request to highlight the searched term by calling 'Highlight'
    .Highlight(x => x.EmployeeNotes, 35, 2, out Highlightings managerHighlights)
     // Search for documents containing the term 'manager'
    .Where(x => x.EmployeeNotes.Contains("manager"))
    .OfType<Employee>()
    .ToList();
List<Employee> employeesResults = await asyncSession
     // Query the map index
    .Query<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
     // Request to highlight the searched term by calling 'Highlight'
    .Highlight(x => x.EmployeeNotes, 35, 2, out Highlightings managerHighlights)
     // Search for documents containing the term 'manager'
    .Where(x => x.EmployeeNotes.Contains("manager"))
    .OfType<Employee>()
    .ToListAsync();
List<Employee> employeesResults = session.Advanced
     // Query the map index
    .DocumentQuery<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
     // Request to highlight the searched term by calling 'Highlight'
    .Highlight(x => x.EmployeeNotes, 35, 2, out Highlightings managerHighlights)
     // Search for documents containing the term 'manager'
    .WhereEquals("EmployeeNotes", "manager")
    .OfType<Employee>()
    .ToList();
from index "Employees/ByNotes"
where EmployeeNotes == "manager"
include highlight(EmployeeNotes, 35, 2)

Process results:


// 'employeesResults' contains all Employee DOCUMENTS that contain the term 'manager'.
// 'managerHighlights' contains the text FRAGMENTS that highlight the 'manager' term.

StringBuilder builder = new StringBuilder().AppendLine("<ul>");

foreach (var employee in employeesResults)
{
    // Call 'GetFragments' to get all fragments for the specified employee Id
    string[] fragments = managerHighlights.GetFragments(employee.Id);
    foreach (var fragment in fragments)
    {
        builder.AppendLine($"<li>Doc: {employee.Id}</li>");
        builder.AppendLine($"<li>Fragment: {fragment}</li>");
        builder.AppendLine($"<li></li>");
    }
}

string fragmentsHtml = builder.AppendLine("</ul>").ToString();

// The resulting fragmentsHtml:
// ============================

// <ul>
//   <li>Doc: employees/2-A</li>
//   <li>Fragment:  to sales <b style="background:yellow">manager</b> in January</li>
//   <li>Doc: employees/5-A</li>
//   <li>Fragment:  to sales <b style="background:yellow">manager</b> in March</li>
//   <li></li>
// </ul>

Highlight results - Map-Reduce index

Configure a Map-Reduce index for highlighting:


In order to search and get fragments with highlighted terms in a Map-Reduce index:

  • The index-field on which you search must be configured with:

    • FieldStorage.Yes - store the field in the index
    • FieldIndexing.Search - allow Full-Text search
    • FieldTermVector.WithPositionsAndOffsets - store the term's position and offsets
  • The index-field by which you group-by must configured with:

    • FieldStorage.Yes - store the field in the index

// Define a Map-Reduce index:
// ==========================
public class ContactDetailsPerCountry :
    AbstractIndexCreationTask<Company, ContactDetailsPerCountry.IndexEntry>
{
    // The IndexEntry class defines the index-fields
    public class IndexEntry
    {
        public string Country { get; set; } 
        public string ContactDetails { get; set; }
    }
       
    public ContactDetailsPerCountry()
    {
        // The 'Map' function defines what will be indexed from each document in the collection
        Map = companies => from company in companies
            select new IndexEntry
            {
                Country = company.Address.Country,
                ContactDetails = $"{company.Contact.Name} {company.Contact.Title}"
            };
        
        // The 'Reduce' function specifies how data is grouped and aggregated
        Reduce = results => from result in results
            group result by result.Country into g
            select new IndexEntry
            {
                // Set 'Country' as the group-by key
                // 'ContactDetails' will be grouped per 'Country'
                Country = g.Key,
                
                // Specify the aggregation
                // here we use string.Join as the aggregation function
                ContactDetails = string.Join(" ", g.Select(x => x.ContactDetails))
            };
        
        // Configure index-field 'Country' for Highlighting:
        // =================================================
        Store(x => x.Country, FieldStorage.Yes);
        
        // Configure index-field 'ContactDetails' for Highlighting:
        // ========================================================
        Store(x => x.ContactDetails, FieldStorage.Yes);
        Index(x => x.ContactDetails, FieldIndexing.Search);
        TermVector(x => x.ContactDetails, FieldTermVector.WithPositionsAndOffsets);
    }
}

Query the index:


// Define the key by which the resulting fragments are grouped:
// ============================================================
HighlightingOptions options = new HighlightingOptions
{
    // Set 'GroupKey' to be the index's group-by key
    // The resulting fragments will be grouped per 'Country'
    GroupKey = "Country"
};

// Query the map-reduce index:
// ===========================
List<ContactDetailsPerCountry.IndexEntry> detailsPerCountry = session
    .Query<ContactDetailsPerCountry.IndexEntry, ContactDetailsPerCountry>()
     // Search for results containing the term 'agent'
    .Search(x => x.ContactDetails, "agent")
     // Request to highlight the searched term by calling 'Highlight'
     // Pass the defined 'options'
    .Highlight(x => x.ContactDetails, 35, 2, options, out Highlightings agentHighlights)
    .ToList();
// Define the key by which the resulting fragments are grouped
// ===========================================================
HighlightingOptions options = new HighlightingOptions
{
    // Set 'GroupKey' to be the index's group-by key
    // The resulting fragments will be grouped per 'Country'
    GroupKey = "Country"
};

// Query the map-reduce index:
// ===========================
List<ContactDetailsPerCountry.IndexEntry> detailsPerCountry = await asyncSession
    .Query<ContactDetailsPerCountry.IndexEntry, ContactDetailsPerCountry>()
     // Search for results containing the term 'agent'
    .Search(x => x.ContactDetails, "agent")
     // Request to highlight the searched term by calling 'Highlight'
     // Pass the defined 'options'
    .Highlight(x => x.ContactDetails, 35, 2, options, out Highlightings agentHighlights)
    .ToListAsync();
// Define the key by which the resulting fragments are grouped
// ===========================================================
HighlightingOptions options = new HighlightingOptions
{
    // Set 'GroupKey' to be the index's group-by key
    // The resulting fragments will be grouped per 'Country'
    GroupKey = "Country"
};

// Query the map-reduce index:
// ===========================
List<ContactDetailsPerCountry.IndexEntry> detailsPerCountry = session.Advanced
    .DocumentQuery<ContactDetailsPerCountry.IndexEntry, ContactDetailsPerCountry>()
     // Search for results containing the term 'agent'
    .Search(x => x.ContactDetails, "agent")
     // Request to highlight the searched term by calling 'Highlight'
     // Pass the defined 'options'
    .Highlight(x => x.ContactDetails, 35, 2, options, out Highlightings agentHighlights)
    .ToList();
from index "ContactDetailsPerCountry"
where search(ContactDetails, "agent")
include highlight(ContactDetails, 35, 2, $p0)
{"p0":{"GroupKey":"Country"}}

Process results:


// 'detailsPerCountry' contains the contacts details grouped per country.
// 'agentHighlights' contains the text FRAGMENTS that highlight the 'agent' term.

StringBuilder builder = new StringBuilder().AppendLine("<ul>");

foreach (var item in detailsPerCountry)
{
    // Call 'GetFragments' to get all fragments for the specified country key
    string[] fragments = agentHighlights.GetFragments(item.Country);
    foreach (var fragment in fragments)
    {
        builder.AppendLine($"<li>Country: {item.Country}</li>");
        builder.AppendLine($"<li>Fragment: {fragment}</li>");
        builder.AppendLine($"<li></li>");
    }
}

string fragmentsHtml = builder.AppendLine("</ul>").ToString();

// The resulting fragmentsHtml:
// ============================

// <ul>
//   <li>Country: UK</li>
//   <li>Fragment: Devon Sales <b style="background:yellow">Agent</b> Helen Bennett</li>
//   <li></li>
//   <li>Country: France</li>
//   <li>Fragment: Sales <b style="background:yellow">Agent</b> Carine Schmit</li>
//   <li></li>
//   <li>Country: France</li>
//   <li>Fragment: Saveley Sales <b style="background:yellow">Agent</b> Paul Henriot</li>
//   <li></li>
//   <li>Country: Argentina</li>
//   <li>Fragment: Simpson Sales <b style="background:yellow">Agent</b> Yvonne Moncad</li>
//   <li></li>
//   <li>Country: Argentina</li>
//   <li>Fragment: Moncada Sales <b style="background:yellow">Agent</b> Sergio</li>
//   <li></li>
//   <li>Country: Brazil</li>
//   <li>Fragment: Sales <b style="background:yellow">Agent</b> Anabela</li>
//   <li></li>
//   <li>Country: Belgium</li>
//   <li>Fragment: Dewey Sales <b style="background:yellow">Agent</b> Pascale</li>
//   <li></li>
// </ul>

Customize highlight tags

  • Default tags:

    • Please refer to Highlight tags to learn about the default html tags used to wrap the highlighted terms.
  • Customizing tags:

    • The default html tags that wrap the highlighted terms can be customized to any other tags.

    • Customizing the wrapping tags when querying an index is done exactly the same as when making
      a dynamic query where a HighlightingOptions object is passed to the Highlight method.

    • Follow the example in Highlight - customize tags.