Searching

Get data from the index

Indexes

Ucommerce has five built-in indexes: Store, Product, Catalog, Category, and PriceGroup.

To query an index, you need to take a dependency on IIndex<T> where T is one of the Ucommerce search models.

public class ProductController : Controller
{
    private readonly IIndex<ProductSearchModel> _productIndex;

    public ProductController(IIndex<ProductSearchModel> productIndex)
    {
        _productIndex = productIndex;
    }
}

The IIndex interface allows you to query the index with a LINQ-like fluid syntax. To get started, you get the ISearch<Model> object that you will do your queries on from the index.

ISearch<ProductSearchModel> query = _productIndex.AsSearchable(new CultureInfo("da-DK"));

Be aware that the interface is LINQ-like but not LINQ. This means the interface does not support certain things. See Query caveats for more details.

Filtering

You can use the Where() method to filter the results in the index down to only the objects you are interested in.

The simplest filter is to match on equality.

var SKUQuery = query.Where(x => x.Sku == "Some SKU");

Match

The Match class is for more advanced matching like Full Text, Literal, Fuzzy, and Wildcard.

// Full Text
var fullTextFilter = query.Where(x => x.DisplayName == Match.FullText("SearchTerm"));
// Fuzzy
var fuzzyFilter = query.Where(x => x.DisplayName == Match.Fuzzy("SearchTerm", 2));
// Literal
var literalFilter = query.Where(x => x.DisplayName == Match.Literal("SearchTerm"));
// Wildcard
var wildcardFilter = query.Where(x => x.DisplayName == Match.Wildcard("searchTerm*"));

Range

To query in a range, you can use the Range(int from, int to) method on the Match class.

var rangeQuery = query
                .Where(x => x.PricesInclTax["pricegroup"] == Match.Range(100, 200));

Sorting

You can sort the results with the Order and OrderByDescending methods.

// Order By
var orderedSKUQuery = query.Where(x => x.Sku == "Some SKU")
                .OrderBy(x => x.DisplayName);
// Order By Descending                
var descendingSKUQuery = query.Where(x => x.Sku == "Some SKU")
                .OrderByDescending(x => x.DisplayName);

Paging

Paging can be done using the Skip and Take methods.

// The 4th to 6th result
var pagedQuery = query.Skip(3).Take(3);

Results

Like with LINQ, the execution of the query is deferred until you finish the query definition and get the results by calling one of the following methods.

ResultSet<ProductSearchModel> result = await query.ToResultSet(token);
         
FacetResultSet<ProductSearchModel> resultWithFacets = await query.ToFacets(token: token);

long count = await query.Count(token);

ProductSearchModel firstProduct = await query.First(token);
ProductSearchModel? firstOrDefaultProduct = await query.FirstOrDefault(token);

ProductSearchModel singleProduct = await query.Single(token);
ProductSearchModel? singleOrDefaultProduct = await query.SingleOrDefault(token);

IImmutableList<NewClass> selectResult = await query.Select<NewClass>(
                            x => new NewClass
                            {
                                // Map properties
                            },
                            token);

You should call these methods last to avoid unexpected behavior and keep the query on a search engine level instead of in memory. This also avoids the use of excessive resources on your web server.

The method ToSuggestions mentioned in suggestions also triggers the query execution.

Total Count

If you need a total count for a query, it is available on the query result as TotalCount. The TotalCount property gives you the total number of objects in the index. To get the number of objects the query returns, use the Count property on the Results property.

var totalCount = result.TotalCount;
var querycount = result.Results.Count;

Suggestions

To give suggestions to the user when they are typing in a search field, you can use the Suggestions() search method.

// SuggestionResultSet<ProductSearchModel>
var suggestionsResult = await _productIndex.AsSearchable(new CultureInfo("da-DK")
                                .ToSuggestions(
                                    "DisplayName", //Property to base suggestions on
                                    "se",          //Search Term
                                    false,         //Fuzzy?
                                    token);

The Fuzzy parameter controls whether or not the suggestions should be fuzzy, which takes spelling errors into account. This will impact performance, so keep that in mind.

To get suggestions on a field you need to have added Suggestable() to the Index Definition.

Search Models

EntitySearch Model

Product

ProductSearchModel

Store

StoreSearchModel

Catalog

CatalogSearchModel

Category

CategorySearchModel

PriceGroup

PriceGroupSearchModel

Query Caveats

Although the fluid query syntax is LINQ-like, it isn't fully LINQ-compatible.

Known Limitations

Contains

❌ Using Contains() on an index model collection property does not work:

ISearch<ProductSearchModel> query = _productIndex
    .AsSearchable(new CultureInfo("da-DK"))
    .Where(product => product.CategoryProductRelations.Contains(someRelation));

✅ Using Contains() on an in-memory collection checking for an index model property works fine:

ISearch<ProductSearchModel> query = _productIndex
    .AsSearchable(new CultureInfo("da-DK"))
    .Where(product => productGuids.Contains(product.Guid))

Last updated