Include Explanations in Index Query


  • This article provides examples of including explanations when querying a static-index.

  • Prior to this article, please refer to Include Query Explanations for dynamic-query examples
    and general knowledge about including explanations to retrieve the score of resulting documents.

    • Including explanations is available only when using Lucene as the underlying search engine for static-indexes.
    • You can configure which search engine will be used. Learn how in Selecting the search engine.


Include explanations when querying Map index

public class Products_BySearchName : AbstractIndexCreationTask<Product>
{
    public class IndexEntry
    {
        public string Name { get; set; }
    }

    public Products_BySearchName()
    {
        Map = products => from product in products
            select new IndexEntry()
            {
                Name = product.Name
            };

        // Configure the index-field 'Name' for FTS
        Indexes.Add(x => x.Name, FieldIndexing.Search);
    }
}
List<Product> products = session
     // Query the index
    .Query<Products_BySearchName.IndexEntry, Products_BySearchName>()
     // Convert the IRavenQueryable to IDocumentQuery
     // to be able to use 'IncludeExplanations'
    .ToDocumentQuery()
     // Call 'IncludeExplanations', provide an out param for the explanations results
    .IncludeExplanations(out Explanations explanations)
     // Convert back to IRavenQueryable
     // to continue building the query using LINQ
    .ToQueryable()
     // Define query criteria
     // e.g. search for docs containing Syrup -or- Lager in their Name field
    .Search(x => x.Name, "Syrup Lager" )
    .OfType<Product>() 
    .ToList();

// When running the above query on the RavenDB sample data
// the results contain 3 product documents.

// To get the score details for the first document from the results
// call 'GetExplanations' on the resulting Explanations object as follows:
string[] scoreDetails = explanations.GetExplanations(products[0].Id);
List<Product> products = await asyncSession
     // Query the index
    .Query<Products_BySearchName.IndexEntry, Products_BySearchName>()
     // Convert the IRavenQueryable to IDocumentQuery
     // to be able to use 'IncludeExplanations'
     .ToAsyncDocumentQuery()
     // Call 'IncludeExplanations', provide an out param for the explanations results
    .IncludeExplanations(out Explanations explanations)
     // Convert back to IRavenQueryable
     // to continue building the query using LINQ
    .ToQueryable()
     // Define query criteria
     // e.g. search for docs containing Syrup -or- Lager in their Name field
    .Search(x => x.Name, "Syrup Lager" )
    .OfType<Product>() 
    .ToListAsync();

// When running the above query on the RavenDB sample data
// the results contain 3 product documents.

// To get the score details for the first document from the results
// call 'GetExplanations' on the resulting Explanations object as follows:
string[] scoreDetails = explanations.GetExplanations(products[0].Id);
List<Product> products = session.Advanced
     // Query the index
    .DocumentQuery<Products_BySearchName.IndexEntry, Products_BySearchName>()
     // Call 'IncludeExplanations', provide an out param for the explanations results
    .IncludeExplanations(out Explanations explanations)
     // Define query criteria
     // e.g. search for docs containing Syrup -or- Lager in their Name field
    .Search(x => x.Name, "Syrup Lager" )
    .OfType<Product>() 
    .ToList();

// When running the above query on the RavenDB sample data
// the results contain 3 product documents.

// To get the score details for the first document from the results
// call 'GetExplanations' on the resulting Explanations object as follows:
string[] scoreDetails = explanations.GetExplanations(products[0].Id);
List<Product> products = await asyncSession.Advanced
     // Query the index
    .AsyncDocumentQuery<Products_BySearchName.IndexEntry, Products_BySearchName>()
     // Call 'IncludeExplanations', provide an out param for the explanations results
    .IncludeExplanations(out Explanations explanations)
     // Define query criteria
     // e.g. search for docs containing Syrup -or- Lager in their Name field
    .Search(x => x.Name, "Syrup Lager" )
    .OfType<Product>() 
    .ToListAsync();

// When running the above query on the RavenDB sample data
// the results contain 3 product documents.

// To get the score details for the first document from the results
// call 'GetExplanations' on the resulting Explanations object as follows:
string[] scoreDetails = explanations.GetExplanations(products[0].Id);
from index "Products/BySearchName"
where search(Name, "Syrup Lager")
include explanations()
// Or:
from index "Products/BySearchName"
where search(Name, "Syrup") or search(Name, "Lager")
include explanations()

Include explanations when querying Map-Reduce index

// This index counts the number of units ordered per category in all Product documents
// ===================================================================================

public class NumberOfUnitsOrdered_PerCategory : 
    AbstractIndexCreationTask<Product, NumberOfUnitsOrdered_PerCategory.IndexEntry>
{
    public class IndexEntry
    {
        public string Category { get; set; }
        public int NumberOfUnitsOrdered { get; set; }
    }
    
    public NumberOfUnitsOrdered_PerCategory()
    {
        Map = products => from product in products
            // Load the products' category 
            let categoryName = LoadDocument<Category>(product.Category).Name
            
            select new IndexEntry()
            {
                Category = categoryName,
                NumberOfUnitsOrdered = product.UnitsOnOrder
            };

        Reduce = results => from result in results
            group result by result.Category
            into g
            let unitsOrdered = g.Sum(x => x.NumberOfUnitsOrdered)
            
            select new IndexEntry()
            {
                Category = g.Key,
                NumberOfUnitsOrdered = unitsOrdered
            };
    }
}
List<NumberOfUnitsOrdered_PerCategory.IndexEntry> results = session
     // Query the Map-Reduce index
    .Query<NumberOfUnitsOrdered_PerCategory.IndexEntry, 
        NumberOfUnitsOrdered_PerCategory>()
     // Convert the IRavenQueryable to IDocumentQuery
     // to be able to use 'IncludeExplanations'
    .ToDocumentQuery()
     // Call 'IncludeExplanations', provide:
     // * The group key for each result item
     // * An out param for the explanations results
    .IncludeExplanations(
        new ExplanationOptions { GroupKey = "Category" }, 
        out Explanations explanations)
     // Convert back to IRavenQueryable
     // to continue building the query using LINQ
    .ToQueryable()
     // Query for categories that have a total of more than a 400 units ordered
    .Where(x => x.NumberOfUnitsOrdered > 400)
    .ToList();

// Get the score details for an item in the results
// Pass the group key (Category, in this case) to 'GetExplanations'
string[] scoreDetails = explanations.GetExplanations(results[0].Category);
List<NumberOfUnitsOrdered_PerCategory.IndexEntry> results = await asyncSession
     // Query the Map-Reduce index
    .Query<NumberOfUnitsOrdered_PerCategory.IndexEntry, 
        NumberOfUnitsOrdered_PerCategory>()
     // Convert the IRavenQueryable to IDocumentQuery
     // to be able to use 'IncludeExplanations'
    .ToAsyncDocumentQuery()
     // Call 'IncludeExplanations', provide:
     // * The group key for each result item
     // * An out param for the explanations results
    .IncludeExplanations(
        new ExplanationOptions { GroupKey = "Category" }, 
        out Explanations explanations)
     // Convert back to IRavenQueryable
     // to continue building the query using LINQ
    .ToQueryable()
     // Query for categories that have a total of more than a 400 units ordered
    .Where(x => x.NumberOfUnitsOrdered > 400)
    .ToListAsync();

// Get the score details for an item in the results
// Pass the group key (Category, in this case) to 'GetExplanations'
string[] scoreDetails = explanations.GetExplanations(results[0].Category);
List<NumberOfUnitsOrdered_PerCategory.IndexEntry> results = session.Advanced
     // Query the Map-Reduce index
    .DocumentQuery<NumberOfUnitsOrdered_PerCategory.IndexEntry, 
        NumberOfUnitsOrdered_PerCategory>()
     // Call 'IncludeExplanations', provide:
     // * The group key for each result item
     // * An out param for the explanations results
    .IncludeExplanations(
        new ExplanationOptions { GroupKey = "Category" }, 
        out Explanations explanations)
     // Query for categories that have a total of more than a 400 units ordered
    .WhereGreaterThan(x => x.NumberOfUnitsOrdered, 400)
    .ToList();

// Get the score details for an item in the results
// Pass the group key (Category, in this case) to 'GetExplanations'
string[] scoreDetails = explanations.GetExplanations(results[0].Category);
List<NumberOfUnitsOrdered_PerCategory.IndexEntry> results = await asyncSession.Advanced
     // Query the Map-Reduce index
    .AsyncDocumentQuery<NumberOfUnitsOrdered_PerCategory.IndexEntry, 
        NumberOfUnitsOrdered_PerCategory>()
     // Call 'IncludeExplanations', provide:
     // * The group key for each result item
     // * An out param for the explanations results
    .IncludeExplanations(
        new ExplanationOptions { GroupKey = "Category" }, 
        out Explanations explanations)
     // Query for categories that have a total of more than a 400 units ordered
    .WhereGreaterThan(x => x.NumberOfUnitsOrdered, 400)
    .ToListAsync();

// Get the score details for an item in the results
// Pass the group key (Category, in this case) to 'GetExplanations'
string[] scoreDetails = explanations.GetExplanations(results[0].Category);
from index "NumberOfUnitsOrdered/PerCategory"
where NumberOfUnitsOrdered > 400
include explanations($p0)
{"p0" : { "GroupKey" : "Category" }}

Syntax

// Use this overload when querying a Map index
IDocumentQuery<T> IncludeExplanations(out Explanations explanations);

// Use this overload when querying a Map-Reduce index
IDocumentQuery<T> IncludeExplanations(ExplanationOptions options, out Explanations explanations);
Parameter Type Description
explanations Explanations An out param that will be filled with the explanations results.
options ExplanationOptions An object that specifies the GroupKey when querying a Map-Reduce index.

public class Explanations
{
    // Returns a list with all explanations.
    // Pass the document ID of a document from the results to get its score details (Map index)
    // Pass the GroupKey of an item from the results to get its score details (Map-Reduce index)
    public string[] GetExplanations(string key);
}
public sealed class ExplanationOptions
{
    // The group key that was used to group by the items in the Map-Reduce index
    public string GroupKey { get; set; }
}