Suggestions
RavenDB indexing mechanism in built upon Lucene engine that has a great suggestions feature. This capability has been also introduced to RavenDB and allows a significant improvement of search functionalities enhancing the overall user experience of the application.
Let's consider an example where the users have the option to look for products by their name. The index and query would look as follow:
0
// Define the suggestion request for multiple terms
var suggestionRequest = new SuggestionWithTerms("ProductName")
{
// Looking for terms from index-field 'ProductName' that are similar to 'chokolade' OR 'syrop'
Terms = new[] { "chokolade", "syrop"}
};
// Query the index for suggestions
Dictionary<string, SuggestionResult> suggestions = session
.Query<Products_ByName.IndexEntry, Products_ByName>()
// Call 'SuggestUsing' - pass the suggestion request
.SuggestUsing(suggestionRequest)
.Execute();
// This query on index 'Products/ByName' has NO resulting documents
List<Product> products = session
.Query<Products_ByName.IndexEntry, Products_ByName>()
.Search(x => x.ProductName, "chokolade")
.OfType<Product>()
.ToList();
If our database have Northwind
samples deployed then it will not return any results, but we can ask RavenDB for help by using:
// Query the index for suggested terms for single term:
// ====================================================
Dictionary<string, SuggestionResult> suggestions = session
// Query the index
.Query<Products_ByName.IndexEntry, Products_ByName>()
// Call 'SuggestUsing'
.SuggestUsing(builder => builder
// Request to get terms from index-field 'ProductName' that are similar to 'chokolade'
.ByField(x => x.ProductName, "chokolade"))
.Execute();
It will produce the suggestions:
Did you mean? chang chai
The Suggest
method is an extension contained in Raven.Client
namespace. It also has an overload that takes one parameter - SuggestionQuery
that allows
you to specify the suggestion query options:
- Field - the name of the field that you want to find suggestions in,
- Term - the provided by user search term,
- MaxSuggestions - the number of suggestions to return (default:
15
), - Distance - the enum that indicates what string distance algorithm should be used: JaroWinkler, Levenshtein (default) or NGram,
- Accuracy - the minimal accuracy required from a string distance for a suggestion match (default: 0.0),
- Popularity - determines whether the returned terms should be in order of popularity (default: false).
- Query
- Commands
- Index
session
.Query<Product, Products_ByName>()
.Suggest(
new SuggestionQuery
{
Field = "Name",
Term = "chaig",
Accuracy = 0.4f,
MaxSuggestions = 5,
Distance = StringDistanceTypes.JaroWinkler,
Popularity = true
});
store
.DatabaseCommands
.Suggest(
"Products/ByName",
new SuggestionQuery
{
Field = "Name",
Term = "chaig",
Accuracy = 0.4f,
MaxSuggestions = 5,
Distance = StringDistanceTypes.JaroWinkler,
Popularity = true
});
public class Products_ByName : AbstractIndexCreationTask<Product>
{
public Products_ByName()
{
Map = products => from product in products
select new
{
product.Name
};
Indexes.Add(x => x.Name, FieldIndexing.Analyzed); // (optional) splitting name into multiple tokens
Suggestion(x => x.Name); // configuring suggestions
}
}
Suggest over multiple words
RavenDB allows you to perform a suggestion query over multiple words. In order to use this functionalify you have to pass words that you are looking for in Term by using special RavenDB syntax (more details here):
var resultsByMultipleWords = session
.Query<Product, Products_ByName>()
.SuggestUsing(builder => builder
.ByField(x => x.Name, new[] { "chaig", "tof" })
.WithOptions(new SuggestionOptions
{
Accuracy = 0.4f,
PageSize = 5,
Distance = StringDistanceTypes.JaroWinkler,
SortMode = SuggestionSortMode.Popularity
}))
.Execute();
Console.WriteLine("Did you mean?");
foreach (string suggestion in resultsByMultipleWords["Name"].Suggestions)
{
Console.WriteLine("\t{0}", suggestion);
}
This will produce the results:
Did you mean? chai chang chartreuse chef tofu
Remarks
Suggestions does not take advantage of the encryption bundle. You should never use this feature on information that should be encrypted, because then you have a risk of storing sensitive data on a disk in unsecured manner.
Indexes with turned on suggestions tend to use much more CPU power than other indexes, this can impact indexing speed (querying is not impacted).