Query for suggestions with index
-
Prior to reading this article, please refer to query for suggestions for general knowledge about Suggestions and for dynamic-queries examples.
-
In addition to getting suggested terms when making a dynamic-query,
you can query for similar terms when querying an index. -
This article provides examples of querying an index for suggestions.
Find the Suggestions API methods listed here. -
In this page:
Configure the index for suggestions
-
In order to be able to ask for suggested terms when querying an index field,
that field must first be configured for suggestions in the index definition. -
See the following sample index:
(This index will be used in the examples ahead).
// The IndexEntry class defines the index-fields
class Products_ByName_IndexEntry
{
private ?string $productName = null;
public function getProductName(): ?string
{
return $this->productName;
}
public function setProductName(?string $productName): void
{
$this->productName = $productName;
}
}
class Products_ByName extends AbstractIndexCreationTask
{
public function __construct()
{
parent::__construct();
// The 'Map' function defines the content of the index-fields
$this->map = "from product in docs.Products " .
"select new " .
"{ " .
" product.Name " .
"} ";
// Configure index-field 'ProductName' for suggestions
$this->suggestion("Name"); // configuring suggestions
// Optionally: set 'Search' on this field
// This will split the field content into multiple terms allowing for a full-text search
$this->index("Name", FieldIndexing::search()); // (optional) splitting name into multiple tokens
}
}
Increased indexing time:
-
When configuring an index for suggestions, then during the indexing process,
in addition to the regular breakdown of the data into terms (tokenization),
RavenDB will scramble the terms to simulate common errors. -
This can impact indexing speed but the cost of querying suggestions is Not impacted.
The index terms
Based on the Northwind sample data,
these are the terms generated for the above index Products/ByName
:

Terms generated for index Products/ByName
-
The index-field name - as defined in the index definition.
In this example the field name isProductName
. -
The terms that were generated for this index-field from the documents in the Products collection.
- The image shows a partial view out of the 163 terms in this list.
- The terms were generated by RavenDB's default search analyzer since full-text search was set on this field.
Suggest terms - for a single term
Based on the Northwind sample data,
the following query on the index Products/ByName
from above has no resulting documents,
since the term chokolade
does Not exist in the index terms for index-field ProductName
.
// This query on index 'Products/ByName' has NO resulting documents
/** @var array<Product> $products */
$products = $session
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
->search("ProductName", "chokolade")
->ofType(Product::class)
->toList();
If you suspect that the term chokolade
in the query criteria is written incorrectly,
you can ask RavenDB to suggest similar terms from the index, as follows:
// Query the index for suggested terms for single term:
// ====================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
// Query the index
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing'
->suggestUsing(function ($builder) {
// Request to get terms from index-field 'ProductName' that are similar to 'chokolade'
return $builder->byField("ProductName", "chokolade");
})
->execute();
// Define the suggestion request for single term
$suggestionRequest = new SuggestionWithTerm("ProductName");
$suggestionRequest->setTerm("chokolade");
// Query the index for suggestions
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing' - pass the suggestion request
->suggestUsing($suggestionRequest)
->Execute();
// Query the index for suggested terms for single term:
// ====================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session->advanced()
// Query the index
->documentQuery(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing'
->suggestUsing(function($builder) {
// Request to get terms from index-field 'ProductName' that are similar to 'chokolade'
return $builder->byField("ProductName", "chokolade");
})
->execute();
// Query for terms from index-field 'ProductName' that are similar to 'chokolade'
from index "Products/ByName"
select suggest(ProductName, "chokolade")
// The resulting suggested terms:
// ==============================
echo "Suggested terms in index-field 'ProductName' that are similar to 'chokolade':";
foreach ($suggestions["ProductName"]->getSuggestions() as $suggestedTerm)
{
echo "\t" . $suggestedTerm;
}
// Suggested terms in index-field 'ProductName' that are similar to 'chokolade':
// schokolade
// chocolade
// chocolate
Suggest terms - for multiple terms
// Query the index for suggested terms for multiple terms:
// =======================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
// Query the index
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing'
->suggestUsing(function($builder) {
return $builder
// Request to get terms from index-field 'ProductName' that are similar to 'chokolade' OR 'syrop'
->ByField("ProductName", ["chokolade", "syrop"]);
})
->execute();
// Define the suggestion request for multiple terms
$suggestionRequest = new SuggestionWithTerms("ProductName");
$suggestionRequest->setTerms([ "chokolade", "syrop" ]);
// Query the index for suggestions
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing' - pass the suggestion request
->suggestUsing($suggestionRequest)
->execute();
// Query the index for suggested terms for multiple terms:
// =======================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session->advanced()
// Query the index
->documentQuery(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing'
->suggestUsing(function($builder) {
return $builder
// Request to get terms from index-field 'ProductName' that are similar to 'chokolade' OR 'syrop'
->byField("ProductName", [ "chokolade", "syrop" ]);
})
->execute();
// Query for terms from index-field 'ProductName' that are similar to 'chokolade' OR 'syrop'
from index "Products/ByName" select suggest(ProductName, $p0)
{ "p0" : ["chokolade", "syrop"] }
// The resulting suggested terms:
// ==============================
// Suggested terms in index-field 'ProductName' that are similar to 'chokolade' OR to 'syrop':
// schokolade
// chocolade
// chocolate
// sirop
// syrup
Suggest terms - for multiple fields
// Query the index for suggested terms in multiple fields:
// =======================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
// Query the index
->query(Companies_ByNameAndByContactName_IndexEntry::class, Companies_ByNameAndByContactName::class)
// Call 'SuggestUsing' to get suggestions for terms that are
// similar to 'chese' in first index-field (e.g. 'CompanyName')
->suggestUsing(function($builder) {
return $builder
->byField("CompanyName", "chese" );
})
// Call 'AndSuggestUsing' to get suggestions for terms that are
// similar to 'frank' in an additional index-field (e.g. 'ContactName')
->andSuggestUsing(functioN($builder) {
return $builder
->byField("ContactName", "frank");
})
->execute();
// Define suggestion requests for multiple fields:
$request1 = new SuggestionWithTerm("CompanyName");
// Looking for terms from index-field 'CompanyName' that are similar to 'chese'
$request1->setTerm("chese");
$request2 = new SuggestionWithTerm("ContactName");
// Looking for terms from nested index-field 'ContactName' that are similar to 'frank'
$request2->setTerm("frank");
// Query the index for suggestions
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
->query(Companies_ByNameAndByContactName_IndexEntry::class, Companies_ByNameAndByContactName::class)
// Call 'SuggestUsing' - pass the suggestion request for the first index-field
->suggestUsing($request1)
// Call 'AndSuggestUsing' - pass the suggestion request for the second index-field
->andSuggestUsing($request2)
->execute();
// Query the index for suggested terms in multiple fields:
// =======================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session->advanced()
// Query the index
->documentQuery(Companies_ByNameAndByContactName_IndexEntry::class, Companies_ByNameAndByContactName::class)
// Call 'SuggestUsing' to get suggestions for terms that are
// similar to 'chese' in first index-field (e.g. 'CompanyName')
->suggestUsing(function($builder) {
return $builder
->ByField("CompanyName", "chese" );
})
// Call 'AndSuggestUsing' to get suggestions for terms that are
// similar to 'frank' in an additional index-field (e.g. 'ContactName')
->andSuggestUsing(function($builder) {
return $builder
->byField("ContactName", "frank");
})
->execute();
// The IndexEntry class defines the index-fields.
class Companies_ByNameAndByContactName_IndexEntry
{
private ?string $companyName = null;
private ?string $contactName = null;
public function getCompanyName(): ?string
{
return $this->companyName;
}
public function setCompanyName(?string $companyName): void
{
$this->companyName = $companyName;
}
public function getContactName(): ?string
{
return $this->contactName;
}
public function setContactName(?string $contactName): void
{
$this->contactName = $contactName;
}
}
class Companies_ByNameAndByContactName extends AbstractIndexCreationTask
{
public function __construct()
{
parent::__construct();
// The 'Map' function defines the content of the index-fields
$this->map= "from company in docs.Companies" .
"select new { " .
"CompanyName = company.Name, " .
"ContactName = company.Contact.Name " .
"}";
// Configure the index-fields for suggestions
$this->suggestion("CompanyName");
$this->suggestion("ContactName");
// Optionally: set 'Search' on the index-fields
// This will split the fields' content into multiple terms allowing for a full-text search
$this->index("CompanyName", FieldIndexing::search());
$this->index("ContactName", FieldIndexing::search());
}
}
// Query for suggested terms
// from index-field 'CompanyName' AND from index-field 'ContactName'
from index "Companies/ByNameAndByContactName"
select suggest(CompanyName, "chese"), suggest(ContactName, "frank")
// The resulting suggested terms:
// ==============================
// Suggested terms in index-field 'CompanyName' that is similar to 'chese':
// cheese
// chinese
// Suggested terms in index-field 'ContactName' that are similar to 'frank':
// fran
// franken
Suggest terms - customize options and display name
// Query the index for suggested terms - customize options and display name:
// =========================================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
// Query the index
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing'
->suggestUsing(function($builder) {
$suggestionOptions = new SuggestionOptions();
$suggestionOptions->setAccuracy(0.3);
$suggestionOptions->setPageSize(5);
$suggestionOptions->setDistance(StringDistanceTypes::nGram());
$suggestionOptions->setSortMode(SuggestionSortMode::popularity());
$builder
->byField("ProductName", "chokolade")
// Customize suggestions options
->withOptions($suggestionOptions)
// Customize display name for results
->withDisplayName("SomeCustomName");
})
->execute();
// Define the suggestion request
$suggestionRequest = new SuggestionWithTerm("ProductName");
// Looking for terms from index-field 'ProductName' that are similar to 'chokolade'
$suggestionRequest->setTerm("chokolade");
// Customize options
$options = new SuggestionOptions();
$options->setAccuracy(0.3);
$options->setPageSize(5);
$options->setDistance(StringDistanceTypes::nGram());
$options->setSortMode(SuggestionSortMode::popularity());
$suggestionRequest->setOptions($options);
// Customize display name
$suggestionRequest->setDisplayField("SomeCustomName");
// Query the index for suggestions
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session
->query(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing' - pass the suggestion request
->suggestUsing($suggestionRequest)
->execute();
// Query the index for suggested terms - customize options and display name:
// =========================================================================
/** @var array<SuggestionResult> $suggestions */
$suggestions = $session->advanced()
// Query the index
->documentQuery(Products_ByName_IndexEntry::class, Products_ByName::class)
// Call 'SuggestUsing'
->suggestUsing(function($builder) {
$options = new SuggestionOptions();
$options->setAccuracy(0.3);
$options->setPageSize(5);
$options->setDistance(StringDistanceTypes::nGram());
$options->setSortMode(SuggestionSortMode::popularity());
return $builder
->byField("ProductName", "chokolade")
// Customize suggestions options
->withOptions($options)
// Customize display name for results
->withDisplayName("SomeCustomName");
})
->execute();
// Query for suggested terms - customize options and display name
from index "Products/ByName"
select suggest(
ProductName,
"chokolade",
'{ "Accuracy" : 0.3, "PageSize" : 5, "Distance" : "NGram", "SortMode" : "Popularity" }'
) as "SomeCustomName"
// The resulting suggested terms:
// ==============================
echo "Suggested terms:";
// Results are available under the custom name entry
foreach ($suggestions["SomeCustomName"]->getSuggestions() as $suggestedTerm)
{
echo "\t" . $suggestedTerm;
}
// Suggested terms:
// chocolade
// schokolade
// chocolate
// chowder
// marmalade