Catalog product search
The Catalog Search API provides the functionality needed for finding products in EPiServer Commerce using a variety of methods.
There are two distinct APIs that provide search functionality and each one of them offers unique capabilities. One is based on the Lucene .NET Engine (LS - Lucene Search), and the other is based on the SQL Server Full Text Search (FTS) feature.
The Catalog Search API is used to accomplish the following functionality:
- Browsing catalogs and categories
- Performing site-wide searches by keywords
- Guided search
- Browsing by brand, price and any other meta fields
Lucene search
Lucene.NET is a powerful search engine that is used by a number of popular sites with large amounts of data to index. Some of the capabilities of this search engine are:
- Full text search indexing
- Ranked search results
- Different query types available, for instance phrase queries
- Searching by field Numeric value range searches
- Date range searches
- "Did you mean" functionality to provide user with corrected search terms
Lucene.NET is a framework library, not a stand-alone application. It is integrated into EPiServer Commerce, which provides an API wrapper around the Lucene.NET search engine and several controls exemplifying how to use the API.
Advantages:
- Very fast search with capability to do counts and filters
- Allows controlling indexing and searching process
- Open Source architecture
Disadvantages:
- Have to manually update indexes
- Have to come up with workarounds for multi server environments
- Uses it's own query language
Primary use: This API is used mostly on the front-end to provide more advanced functionality for website visitors.
Implementation examples
In the following we will provide some examples of how to implement search features with Lucene search.
Example: Simple catalog entry search
CatalogEntrySearchCriteria criteria = new CatalogEntrySearchCriteria();
criteria.SearchPhrase = "canon";
SearchManager manager = new SearchManager(AppContext.Current.ApplicationName);
SearchResults results = manager.Search(criteria);
The SearchPhrase property can contain full Lucene Syntax like: title:"The Right Way" AND text:go. Refer to the Lucene documentation for more details on Query Parser Syntax.
Example: Catalog entry fuzzy search
In this example the results will be returned using fuzzy search, where approximate results will be returned.
CatalogEntrySearchCriteria criteria = new CatalogEntrySearchCriteria();
criteria.SearchPhrase = "fanon";
SearchManager manager = new SearchManager(AppContext.Current.ApplicationName);
SearchResults results = manager.Search(criteria);
if (results.TotalCount == 0)
{
criteria.IsFuzzySearch = true;
criteria.FuzzyMinSimilarity = 0.7f;
results = manager.Search(criteria);
}
Console.Write("Total Results: " + results.TotalCount.ToString();
Example: Catalog entry search with paging
This example shows how to return real Catalog Entries from the search API which can be used to bind directly to Web Controls like grid.
ICatalogSystem system = CatalogContext.Current;
// Get catalog lists
CatalogDto catalogs = system.GetCatalogDto();
// Create Entry Criteria
CatalogEntrySearchCriteria criteria = new CatalogEntrySearchCriteria();
// Bind default catalogs if none found
if (criteria.CatalogNames.Count == 0)
{
if (catalogs.Catalog.Count > 0)
{
foreach (CatalogDto.CatalogRow row in catalogs.Catalog)
{
if (row.IsActive &&
row.StartDate <= FrameworkContext.Current.CurrentDateTime &&
row.EndDate >= FrameworkContext.Current.CurrentDateTime)
{
criteria.CatalogNames.Add(row.Name);
}
}
}
}
// Define phrase we want to search
criteria.SearchPhrase = "canon";
// Create a manager
SearchManager manager = new SearchManager(AppContext.Current.ApplicationName);
SearchResults results = null;
// Define sort parameter
criteria.Sort = new SearchSort("DisplayName");
// Perform search
results = manager.Search(criteria);
Assert.IsTrue(results.TotalCount > 0, "No hits were found in Lucene index.");
// Get IDs we need
int[] resultIndexes = results.GetIntResults(0, 10 + 5); // we add padding here to accomodate entries that might have been deleted since last indexing
// Retrieve actual entry objects, with no caching
Entries entries = CatalogContext.Current.GetCatalogEntries(resultIndexes, false, new TimeSpan(), new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
entries.TotalResults = results.TotalCount;
Assert.IsTrue(entries.TotalResults > 0, "No entries were returned from the database.");
Full-text search
Advantages:
- Provides up to date data queries that are directly integrated with SQL Server
- Data is automatically repopulated and managed by SQL Server
- There are native TSQL commands to manipulate the FTS catalogs
Disadvantages:
- Slow and does not provide ways to optimize indexing
- Does not include functionality like fuzzy or proximity search which makes it hard to use such functionality
Primary use: This API is used mostly on the back-end where performance is not that critical.
Optimization
Catalog Search uses SQL Server Full Text Search capability out-of-the-box to optimize search requests. Other search products can be used to compliment this default functionality, and will be beneficial for large scale e-commerce sites.
Caching
The search API has a very flexible caching mechanism which allows developer to control how caching is handled by the search. A developer can specify whether or not results should be cached and the specific amount of time they should be cached. The properties are CacheResults (bool) and CacheTimeout (TimeSpan). You would generally want to cache simple search requests like browsing major categories. Since these requests will be the same for a large audience, it will benefit the performance of the site. On the other hand, you might not want to cache specific keyword searches, since those will be unique to a user and have a smaller chance of benefiting through caching.
You can change caching on the public site by going to the ecf.catalog.config file in your \PublicLayer\SDKs\B2CSampleSite\Configs folder and changing appropriate values in the <Cache/> element.
Response groups
Response groups are another way to make sure the site is performing to the maximum. When searching for entries/products in EPiServer Commerce, you can specify the response groups that should be returned. The search will perform the best with a smallest number of response groups returned.
Implementation examples
In the following we will provide some examples of how to implement search features with Full Text search.
Example: Browsing catalogs
The following is an example of browsing catalogs and categories.
ICatalogSystem system = CatalogContext.Current;
// Get catalog lists
CatalogDto catalogs = system.GetCatalogDto();
foreach (CatalogDto.CatalogRow catalog in catalogs.Catalog)
{
string catalogName = catalog.Name;
// Get Catalog Nodes
CatalogNodeDto nodes = system.GetCatalogNodesDto(catalogName);
foreach (CatalogNodeDto.CatalogNodeRow node in nodes.CatalogNode)
{
CatalogSearchParameters pars = new CatalogSearchParameters();
CatalogSearchOptions options = new CatalogSearchOptions();
options.CacheResults = true;
pars.CatalogNames.Add(catalogName);
pars.CatalogNodes.Add(node.Code);
Entries entries = CatalogContext.Current.FindItems(pars,
options,
new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
}
}
Example: Searching by keyword
The following example shows how to search catalogs using the specified keyword.
ICatalogSystem system = CatalogContext.Current;// Get catalog lists
CatalogDto catalogs = system.GetCatalogDto();
foreach (CatalogDto.CatalogRow catalog in catalogs.Catalog)
{
string catalogName = catalog.Name;
// Get Catalog Nodes
CatalogSearchParameters pars = new CatalogSearchParameters();
CatalogSearchOptions options = new CatalogSearchOptions();
pars.FreeTextSearchPhrase = "policy";
pars.CatalogNames.Add(catalogName);
Entries entries = CatalogContext.Current.FindItems(pars, options, new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
}
Example: Advanced keyword search
This example shows how to search catalogs using more advanced queries. You can specify additional parameters like "near". The tables are searched using the Full Text Search "ContainsTable" function. For more information refer to CONTAINSTABLE (Transact-SQL).
ICatalogSystem system = CatalogContext.Current;
// Get catalog lists
CatalogDto catalogs = system.GetCatalogDto();
foreach (CatalogDto.CatalogRow catalog in catalogs.Catalog)
{
string catalogName = catalog.Name;
// Get Catalog Nodes
CatalogSearchParameters pars = new CatalogSearchParameters();
CatalogSearchOptions options = new CatalogSearchOptions();
pars.AdvancedFreeTextSearchPhrase = "(\"sweet and savory\" NEAR sauces) OR (\"sweet and savory\" NEAR candies)";
pars.CatalogNames.Add(catalogName);
Entries entries = CatalogContext.Current.FindItems(pars, options, new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
}
Example: Join table search
The Catalog API allows joining additional tables, queries or views to the search query. This allows further customizations to the search without any modifications to the core framework. For example if you want to sort by meta fields you can join the specific meta table and you will be able to specify fields from meta table in your order by statements. The example below orders entries by DisplayName.
ICatalogSystem system = CatalogContext.Current;
// Get catalog lists
CatalogDto catalogs = system.GetCatalogDto();
foreach (CatalogDto.CatalogRow catalog in catalogs.Catalog)
{
string catalogName = catalog.Name;
// Get Catalog Nodes
CatalogNodeDto nodes = system.GetCatalogNodesDto(catalogName);
foreach (CatalogNodeDto.CatalogNodeRow node in nodes.CatalogNode)
{
CatalogSearchParameters pars = new CatalogSearchParameters();
CatalogSearchOptions options = new CatalogSearchOptions();
options.CacheResults = true;
pars.CatalogNames.Add(catalogName);
pars.CatalogNodes.Add(node.Code);
pars.JoinType = "inner join";
pars.Language = "en-us";
pars.JoinSourceTable = "CatalogEntry";
pars.JoinTargetQuery = "(select distinct ObjectId, DisplayName from CatalogEntryEx) CatalogEntryEx";
pars.JoinSourceTableKey = "CatalogEntryId";
pars.JoinTargetTableKey = "CatalogEntryEx.ObjectId";
pars.OrderByClause = "CatalogEntryEx.DisplayName";
Entries entries = CatalogContext.Current.FindItems(pars, options, new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
}
}
Example: Searching on metafield values with results displayed via the API
private void testCatalogEntiresSearch()
{
CatalogSearchParameters parameters = new CatalogSearchParameters();
parameters.SqlMetaWhereClause = "Meta.DisplayName = 'Canon'";
CatalogSearchOptions options = new CatalogSearchOptions();
options.Classes.Add("Brands");
options.RecordsToRetrieve = 20;
Entries result = CatalogContext.Current.FindItems (parameters, options, new CatalogEntryResponseGroup());
}
Last updated: Oct 21, 2014