Query for Suggestions


  • Given a string term, the Suggestion feature will offer similar terms from your data.

  • Word similarities are found using string distance algorithms.

  • Examples in this article demonstrate getting suggestions with a dynamic-query.
    For getting suggestions with an index-query see query for suggestions with index.



What are terms

  • All queries in RavenDB use an index - learn more about that here.
    Whether making a dynamic query which generates an auto-index or using a static index,
    the data from your documents is 'broken' into terms that are kept in the index.

  • This tokenization process (what terms will be generated) depends on the analyzer used,
    various analyzers differ in the way they split the text stream. Learn more in Analyzers.

  • The terms can then be queried to retrieve matching documents that contain them.

When to use suggestions

Querying for suggestions is useful in the following scenarios:

  • When query has no results:

    • When searching for documents that match some condition on a given string term,
      if the term is misspelled then you will Not get any results.
      You can then ask RavenDB to suggest similar terms that do exist in the index.

    • The suggested terms can then be used in a new query to retrieve matching documents,
      or simply presented to the user asking what they meant to query.

  • When looking for alternative terms:

    • When simply searching for additional alternative terms for a term that does exist.

The resulting suggested terms will Not include the term for which you search,
they will only contain the similar terms.

Suggest terms - for single term

Consider this example:
Based on the Northwind sample data, the following query has no resulting documents,
as no document in the Products collection contains the term chaig in its Name field.

// This dynamic query on the 'Products' collection has NO resulting documents
List<Product> products = session
    .Query<Product>()
    .Where(x => x.Name == "chaig")
    .ToList();
  • Executing the above query will generate the auto-index Auto/Products/ByName.
    This auto-index will contain a list of all available terms from the document field Name.
    The generated terms are visible in the Studio - see image below.

  • If you suspect that the term chaig in the query criteria is written incorrectly,
    you can ask RavenDB to suggest existing terms that are similar to chaig, as follows:.

// Query for suggested terms for single term:
// ==========================================

Dictionary<string, SuggestionResult> suggestions = session
     // Make a dynamic query on collection 'Products'
    .Query<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
         // Request to get terms from field 'Name' that are similar to 'chaig' 
        .ByField(x => x.Name, "chaig"))
    .Execute();
// Query for suggested terms for single term:
// ==========================================

Dictionary<string, SuggestionResult> suggestions = await asyncSession
     // Make a dynamic query on collection 'Products'
    .Query<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
         // Request to get terms from field 'Name' that are similar to 'chaig'  
        .ByField(x => x.Name, "chaig"))
    .ExecuteAsync();
// Define the suggestion request for single term
var suggestionRequest = new SuggestionWithTerm("Name")
{
    // Looking for terms from field 'Name' that are similar to term 'chaig'  
    Term = "chaig"
};

// Query for suggestions 
Dictionary<string, SuggestionResult> suggestions = session
    .Query<Product>()
     // Call 'SuggestUsing' - pass the suggestion request
    .SuggestUsing(suggestionRequest)
    .Execute();
// Query for suggested terms for single term:
// ==========================================

Dictionary<string, SuggestionResult> suggestions = session.Advanced
     // Make a dynamic document-query on collection 'Products'
    .DocumentQuery<Product>()
     // Call 'SuggestUsing' 
    .SuggestUsing(builder => builder
         // Request to get terms from field 'Name' that are similar to 'chaig' 
        .ByField(x => x.Name, "chaig"))
    .Execute();
// Query for terms from field 'Name' that are similar to 'chaig'
from "Products"
select suggest(Name, "chaig")

// The resulting suggested terms:
// ==============================

Console.WriteLine("Suggested terms in field 'Name' that are similar to 'chaig':");
foreach (string suggestedTerm in suggestions["Name"].Suggestions)
{
    Console.WriteLine("\t{0}", suggestedTerm);
}

// Suggested terms in field 'Name' that are similar to 'chaig':
//     chai
//     chang

Suggest terms - for multiple terms

// Query for suggested terms for multiple terms:
// =============================================

Dictionary<string, SuggestionResult> suggestions = session
     // Make a dynamic query on collection 'Products'
    .Query<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
         // Request to get terms from field 'Name' that are similar to 'chaig' OR 'tof' 
        .ByField(x => x.Name, new[] { "chaig", "tof" }))
    .Execute();
// Query for suggested terms for multiple terms:
// =============================================

Dictionary<string, SuggestionResult> suggestions = await asyncSession
     // Make a dynamic query on collection 'Products'
    .Query<Product>()
     // Call 'SuggestUsing' 
    .SuggestUsing(builder => builder
         // Request to get terms from field 'Name' that are similar to 'chaig' OR 'tof'
        .ByField(x => x.Name, new[] { "chaig", "tof" }))
    .ExecuteAsync();
// Define the suggestion request for multiple terms
var suggestionRequest = new SuggestionWithTerms("Name")
{
    // Looking for terms from field 'Name' that are similar to terms 'chaig' OR 'tof'  
    Terms = new[] { "chaig", "tof"}
};

// Query for suggestions 
Dictionary<string, SuggestionResult> suggestions = session
    .Query<Product>()
     // Call 'SuggestUsing' - pass the suggestion request
    .SuggestUsing(suggestionRequest)
    .Execute();
// Query for suggested terms for multiple terms:
// =============================================

Dictionary<string, SuggestionResult> suggestions = session.Advanced
     // Make a dynamic document-query on collection 'Products'
    .DocumentQuery<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
         // Request to get terms from field 'Name' that are similar to 'chaig' OR 'tof' 
        .ByField(x => x.Name, new[] { "chaig", "tof" }))
    .Execute();
// Query for terms from field 'Name' that are similar to 'chaig' OR 'tof'
from "Products" select suggest(Name, $p0)
{ "p0" : ["chaig", "tof"] }

// The resulting suggested terms:
// ==============================

// Suggested terms in field 'Name' that are similar to 'chaig' OR to 'tof':
//     chai
//     chang
//     tofu

Suggest terms - for multiple fields

// Query for suggested terms in multiple fields:
// =============================================

Dictionary<string, SuggestionResult> suggestions = session
     // Make a dynamic query on collection 'Companies'
    .Query<Company>()
     // Call 'SuggestUsing' to get suggestions for terms that are 
     // similar to 'chop-soy china' in first document field (e.g. 'Name') 
    .SuggestUsing(builder => builder
        .ByField(x => x.Name, "chop-soy china"))
     // Call 'AndSuggestUsing' to get suggestions for terms that are 
     // similar to 'maria larson' in an additional field (e.g. 'Contact.Name')
    .AndSuggestUsing(builder => builder
        .ByField(x => x.Contact.Name, "maria larson"))
    .Execute();
// Query for suggested terms in multiple fields:
// =============================================

Dictionary<string, SuggestionResult> suggestions = await asyncSession
     // Make a dynamic query on collection 'Companies'   
    .Query<Company>()
     // Call 'SuggestUsing' to get suggestions for terms that are 
     // similar to 'chop-soy china' in first document field (e.g. 'Name') 
    .SuggestUsing(builder => builder
        .ByField(x => x.Name, "chop-soy china"))
     // Call 'AndSuggestUsing' to get suggestions for terms that are 
     // similar to 'maria larson' in an additional field (e.g. 'Contact.Name')
    .AndSuggestUsing(builder => builder
        .ByField(x => x.Contact.Name, "maria larson"))
    .ExecuteAsync();
// Define suggestion requests for multiple fields:

var request1 = new SuggestionWithTerm("Name")
{
    // Looking for terms from field 'Name' that are similar to 'chop-soy china'  
    Term = "chop-soy china"
};

var request2 = new SuggestionWithTerm("Contact.Name")
{
    // Looking for terms from nested field 'Contact.Name' that are similar to 'maria larson'  
    Term = "maria larson"
};

Dictionary<string, SuggestionResult> suggestions = session
    .Query<Company>()
     // Call 'SuggestUsing' - pass the suggestion request for the first field
    .SuggestUsing(request1)
     // Call 'AndSuggestUsing' - pass the suggestion request for the second field
    .AndSuggestUsing(request2)
    .Execute();
// Query for suggested terms in multiple fields:
// =============================================

Dictionary<string, SuggestionResult> suggestions = session.Advanced
     // Make a dynamic document-query on collection 'Companies'
    .DocumentQuery<Company>()
     // Call 'SuggestUsing' to get suggestions for terms that are 
     // similar to 'chop-soy china' in first document field (e.g. 'Name') 
    .SuggestUsing(builder => builder
        .ByField(x => x.Name, "chop-soy china"))
     // Call 'AndSuggestUsing' to get suggestions for terms that are 
     // similar to 'maria larson' in an additional field (e.g. 'Contact.Name')
    .AndSuggestUsing(builder => builder
        .ByField(x => x.Contact.Name, "maria larson"))
    .Execute();
// Query for suggested terms from field 'Name' and field 'Contact.Name'
from "Companies"
select suggest(Name, "chop-soy china"), suggest(Contact.Name, "maria larson")

// The resulting suggested terms:
// ==============================

// Suggested terms in field 'Name' that is similar to 'chop-soy china':
//     chop-suey chinese

// Suggested terms in field 'Contact.Name' that are similar to 'maria larson':
//     maria larsson
//     marie bertrand
//     aria cruz
//     paula wilson
//     maria anders

Suggest terms - customize options and display name

// Query for suggested terms - customize options and display name:
// ===============================================================

Dictionary<string, SuggestionResult> suggestions = session
     // Make a dynamic query on collection 'Products'
    .Query<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
        .ByField(x => x.Name, "chaig")
         // Customize suggestions options
        .WithOptions(new SuggestionOptions
        {
            Accuracy = 0.4f,
            PageSize = 5,
            Distance = StringDistanceTypes.JaroWinkler,
            SortMode = SuggestionSortMode.Popularity
        })
         // Customize display name for results
        .WithDisplayName("SomeCustomName"))
    .Execute();
// Query for suggested terms - customize options and display name:
// ===============================================================

Dictionary<string, SuggestionResult> suggestions = await asyncSession
     // Make a dynamic query on collection 'Products'
    .Query<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
        .ByField(x => x.Name, "chaig")
         // Customize suggestions options
        .WithOptions(new SuggestionOptions
        {
            Accuracy = 0.4f,
            PageSize = 5,
            Distance = StringDistanceTypes.JaroWinkler,
            SortMode = SuggestionSortMode.Popularity
        })
         // Customize display name for results
        .WithDisplayName("SomeCustomName"))
    .ExecuteAsync();
// Define the suggestion request
var suggestionRequest = new SuggestionWithTerm("Name")
{
    // Looking for terms from field 'Name' that are similar to term 'chaig'  
    Term = "chaig",
    // Customize options
    Options = new SuggestionOptions 
    {
        Accuracy = 0.4f,
        PageSize = 5,
        Distance = StringDistanceTypes.JaroWinkler,
        SortMode = SuggestionSortMode.Popularity
    },
    // Customize display name
    DisplayField = "SomeCustomName"
};

// Query for suggestions 
Dictionary<string, SuggestionResult> suggestions = session
    .Query<Product>()
    // Call 'SuggestUsing' - pass the suggestion request
    .SuggestUsing(suggestionRequest)
    .Execute();
// Query for suggested terms - customize options and display name:
// ===============================================================

Dictionary<string, SuggestionResult> suggestions = session.Advanced
     // Make a dynamic query on collection 'Products'
    .DocumentQuery<Product>()
     // Call 'SuggestUsing'
    .SuggestUsing(builder => builder
        .ByField(x => x.Name, "chaig")
         // Customize suggestions options
        .WithOptions(new SuggestionOptions
        {
            Accuracy = 0.4f,
            PageSize = 5,
            Distance = StringDistanceTypes.JaroWinkler,
            SortMode = SuggestionSortMode.Popularity
        })
         // Customize display name for results
        .WithDisplayName("SomeCustomName"))
    .Execute();
// Query for suggested terms - customize options and display name
from "Products"
select suggest(
    Name,
    'chaig',
    '{ "Accuracy" : 0.4, "PageSize" : 5, "Distance" : "JaroWinkler", "SortMode" : "Popularity" }'
) as "SomeCustomName"

// The resulting suggested terms:
// ==============================

Console.WriteLine("Suggested terms:");
// Results are available under the custom name entry
foreach (string suggestedTerm in suggestions["SomeCustomName"].Suggestions)
{
    Console.WriteLine("\t{0}", suggestedTerm);
}

// Suggested terms:
//     chai
//     chang
//     chartreuse verte

The auto-index terms in Studio

Based on the Northwind sample data, these are the terms generated for index Auto/Products/ByName:

Figure 1. Auto-index terms

Terms generated for index Auto/Products/ByName

  1. The field name - derived from the document field that was used in the dynamic-query.
    In this example the field name is Name.

  2. The terms generated from the data that the Products collection documents have in their Name field.

Syntax

Suggest using:

// Overloads for requesting suggestions for term(s) in a field: 
ISuggestionQuery<T> SuggestUsing<T>(SuggestionBase suggestion);
ISuggestionQuery<T> SuggestUsing<T>(Action<ISuggestionBuilder<T>> builder);

// Overloads requesting suggestions for term(s) in another field in the same query:
ISuggestionQuery<T> AndSuggestUsing(SuggestionBase suggestion);
ISuggestionQuery<T> AndSuggestUsing(Action<ISuggestionBuilder<T>> builder);
Parameter Type Description
suggestion SuggestionWithTerm / SuggestionWithTerms An instance of SuggestionBase.
Defines the type of suggestion requested.
builder Action<ISuggestionBuilder<T>> Builder with a fluent API that constructs a SuggestionBase instance.

Builder operations:

ISuggestionOperations<T> ByField(string fieldName, string term);
ISuggestionOperations<T> ByField(string fieldName, string[] terms);
ISuggestionOperations<T> ByField(Expression<Func<T, object>> path, string term);
ISuggestionOperations<T> ByField(Expression<Func<T, object>> path, string[] terms);

ISuggestionOperations<T> WithDisplayName(string displayName);
ISuggestionOperations<T> WithOptions(SuggestionOptions options);
Parameter Type Description
fieldName string The index field in which to search for similar terms
path Expression<Func<T, object>> The index field in which to search for similar terms
term string The term for which to get suggested similar terms
terms string[] List of terms for which to get suggested similar terms
displayName string A custom name for the suggestions result (optional).
options SuggestionOptions Non-default options to use in the operation (optional).

Suggestions options:

public int PageSize { get; set; }
public StringDistanceTypes? Distance { get; set; }
public float? Accuracy { get; set; }
public SuggestionSortMode SortMode { get; set; }
Option Type Description
PageSize int
  • Maximum number of suggested terms that will be returned
  • Default is 15
Distance StringDistanceTypes
  • String distance algorithm to use
  • None / Levenshtein / JaroWinkler / NGram
  • Default is Levenshtein
Accuracy float?
  • Suggestion accuracy
  • Default is 0.5f
SortMode SuggestionSortMode
  • Indicates the order by which results are returned
  • None / Popularity
  • Default is Popularity