The Catalog module presents the ability to add items to your e-commerce store. It can be electronics, groceries, digital content or anything else. Items can be grouped into categories and catalogs. The item grouping is individual depending on the stock size, item types, vendors, etc.
The Catalog Module supports two types of catalogs - master and virtual.
- Master and Virtual catalogs
- Multiple languages
- Multiple currencies
- Physical and Digital products
- Subscription products
- SEO Information
- Product Variations
- Product & Category attributes
- Flexible properties inheritance
- Sort and filter product listing based on any attribute
- Associations
- Personalization
- Categories taxonomy
- Full-text search engine
- Enterprise ready - supports millions of the products
The module's behavior can be tuned through Platform Settings (Settings > Catalog). All settings are optional; the values listed below are the defaults applied when a setting is left unchanged.
| Setting | Type | Default | Description |
|---|---|---|---|
Catalog.ImageCategories |
String (dictionary) | — | Dictionary of possible image category options for catalog items. |
Catalog.AssociationGroups |
String (dictionary) | — | Product association group names. |
Catalog.EditorialReviewTypes |
String (dictionary) | QuickReview |
Dictionary of possible description types for an item. |
Catalog.CategoryDescriptionTypes |
String (dictionary) | QuickReview |
Dictionary of possible description types for a category. |
Catalog.UseSeoDeduplication |
Boolean | false |
Enable/disable detection of SEO duplicates. |
Catalog.Search.EventBasedIndexation.Enable |
Boolean | true |
Enable/disable automatic background indexing of catalog entities whenever they are changed. |
Catalog.ProductConfigurationMaximumFiles |
Positive integer | 5 |
Maximum number of files allowed in a product configuration section. |
Catalog.BrandStoreSetting.BrandsEnabled |
Boolean | false |
Enable/disable the Brands page on the storefront. Store-level, public. |
Catalog.BrandStoreSetting.BrandCatalogId |
String | — | Catalog ID used for brands. Store-level. |
Catalog.BrandStoreSetting.BrandPropertyName |
String | — | Property name used for the brand. Store-level. |
| Setting | Type | Default | Description |
|---|---|---|---|
Catalog.Search.UseCatalogIndexedSearchInManager |
Boolean | true |
Enable/disable indexed search (with advanced syntax) for the Catalog module in the back office. |
Catalog.Search.UseFullObjectIndexStoring |
Boolean | false |
Enable/disable storing serialized catalog objects in the index and returning them in search results. |
Catalog.Search.IndexLinkPriorityFields |
Boolean | true |
Enable/disable indexing of fields used to calculate the priority of linked objects in search results. |
Catalog.Search.DefaultAggregationSize |
Integer | 25 |
Size for aggregations when not defined in the store's aggregation properties. Set to 0 for unlimited size. |
VirtoCommerce.Search.IndexingJobs.IndexationDate.Product |
Date/time | — | Date and time the product indexing task starts. |
VirtoCommerce.Search.IndexingJobs.IndexationDate.Category |
Date/time | — | Date and time the category indexing task starts. |
Catalog.BrowseFilters.FilteredBrowsing |
JSON | — | Per-store faceted browsing filter configuration. Edited through the Filtering properties widget on the store page. Store-level. |
Catalog.BrowseFilters.FilteredBrowsingMigrated |
Boolean | false |
Internal flag marking that legacy filtered-browsing configuration has been migrated. Hidden; not intended for manual editing. |
Catalog.Search.BarcodeScannerEnabled |
Boolean | true |
Show the barcode scanner button in the storefront search bar. Store-level, public. See Barcode scanner search. |
Catalog.Search.BarcodeSearchFields |
JSON | — | Product index fields a scanned code is matched against; empty/null means full-text search. Edited through the Search configuration → Barcode scanner widget on the store page. Store-level, public. See Barcode scanner search. |
Catalog.Search.ProductSortings |
JSON | — | Per-store product sorting ("sort by") options — admin overrides of the built-in orderings plus any custom orderings. null means "use the code defaults". Edited through the Search configuration → Sorting widget on the store page. Store-level. See Configurable product sorting. |
These settings tune the module's backup/restore (export/import) pipeline. The defaults preserve the previous hard-coded behavior.
| Setting | Type | Default | Description |
|---|---|---|---|
Catalog.BackupRestore.BatchSize |
Positive integer | 50 |
Number of records saved per batch during catalog export and import. Lower values reduce memory usage; higher values improve throughput. |
Catalog.BackupRestore.ErrorPolicy |
String (Stop, SkipBatch, SkipItem) |
SkipItem |
Behavior when a batch fails to save during import: Stop aborts the module import, SkipBatch skips the whole failed batch, SkipItem retries items one-by-one and skips only the failing rows. |
Product sort options ("sort by") for category browsing and search are store-configurable and code-extensible rather than hard-coded. Category managers control which orderings shoppers see, their labels (localizable), their order, and which one is the default.
- Code-first resolvers are the source of truth for which orderings exist. Each ordering is an
IProductSortingResolverregistered in DI. The seven built-ins (Featured, A–Z, Z–A, Price low→high / high→low, Date new→old / old→new) ship as code, so they exist in every environment with no seeding and no migration.Featuredresolves to__score:desc;priority:desc;id:asc. - The store-level setting holds only overrides, not the source of truth.
Catalog.Search.ProductSortings(JSON, store-level) stores admin deltas keyed bycode(renamed label / changed order / hidden / edited clauses) plus any admin-authored custom orderings.null/empty means "use the code defaults", so changes to a resolver's code defaults flow through to untouched fields. - Composition.
IProductSortingServicemerges the resolvers with the stored deltas into the effective list. Two orthogonal per-resolver gate flags govern overriding:AllowOverride(admin may change name/order/visibility) andIsExpressionEditable(admin may edit the sort clauses), both defaulttrue. Setting either tofalsein code is a no-migration "kill switch" that forces the code value. - Default ordering is the first visible ordering (there is no stored
IsDefault); an empty incomingsortresolves to it.
Open Store → Search configuration → Sorting. Drag to reorder (the first visible row is the default), toggle visibility, rename (with per-language localization), edit the sort clauses, and add/remove custom orderings. The system code is the stable key used by the API and the storefront ?sort= URL, so it can be set only when an ordering is created.
Note on persisted order. Reordering and saving stores explicit
ordervalues for the orderings. For a resolver whose codeInfo.Orderdoes not equal its position in the default-sorted list, the saved value (a contiguous list index) won't match the code default, so an order-only delta remains inCatalog.Search.ProductSortingseven after the list is dragged back to the default arrangement — the setting does not return to empty once a reorder has been saved. This is expected behavior, not a defect: the saved list is the admin's persisted intent, the stored values still produce the same displayed order (in both the admin and the storefront), and an admin override always wins over the codeInfo.Orderuntil the ordering is changed again.
Contribute a new ordering by registering a resolver — no admin action, no DB seeding, no migration:
public class VendorAscendingProductSortingResolver : AbstractExpressionProductSortingResolver
{
public override string Code => "vendor-ascending";
public override ProductSortingInfo Info { get; } = new() { Name = "Vendor (A-Z)", Order = 35 };
protected override string DefaultExpression => "vendor:asc"; // logical expression; bound to the physical index field downstream
}
// in Module.Initialize:
serviceCollection.AddSingleton<IProductSortingResolver, VendorAscendingProductSortingResolver>();For an ordering whose expression depends on the request (e.g. a different order per category), implement IProductSortingResolver directly and compute the expression in GetSortExpression(ProductSortingContext context) (the context carries store, catalog, current category/outline, currency, culture, keyword, filter and facet).
- REST (admin):
GET/PUT api/catalog/product-sortings/store/{storeId}(plusGET .../fieldsfor the clause-field picker, derived from the product index schema). Guarded by thecatalog:BrowseFilters:Read/catalog:BrowseFilters:Updatepermissions. - GraphQL (storefront): the
productsconnection exposessortings { id name isDefault selected }. An emptysortapplies the store default, a knowncodeapplies that ordering, and an unknown token / raw expression passes through to the search engine unchanged.
The storefront search bar can scan a barcode with the device camera. Each store decides whether the scanner button is shown at all and how a scanned code is matched: by full-text search (the default, the scanned value is searched in the whole product text) or by an exact match on selected product index fields.
Open Store → Search configuration → Barcode scanner:
- Enable barcode scanner in the storefront — hides/shows the scanner button (
Catalog.Search.BarcodeScannerEnabled). - Match scanned code by — Full-text search or Exact match on selected fields.
- The field picker offers only fields that exist in the product search index: the built-in
code(SKU),gtinandmanufacturerPartNumber(MPN) plus every short text catalog property of type Product or Variation. Free text is not accepted and the server re-validates the selection on save. A previously saved field that is no longer in the index is shown with a missing from index badge and is removed from the selection as soon as any field or the match mode is changed, so it is never saved again.
Catalog.Search.BarcodeSearchFields is meant to be edited through this widget, which is the only place that validates
the selection against the live product index schema. Values written directly through the generic settings API are not
validated: an unknown field name is stored as-is and simply matches nothing (the widget then shows it as missing from
index and drops it from the selection at the first change).
- GTIN — the packaging barcode: UPC, EAN, ISBN or JAN.
- MPN — the manufacturer part number.
- SKU — the product code.
- Any other code, or several codes of the same kind: create a short text catalog property for products or variations (long text is not indexed as a filterable field) and select it in the blade. Mark the property multi-value to store several codes in one property — the term filter matches any of its values.
- Several kinds of code (e.g. GTIN and a custom property) can be selected at once; they are matched with OR.
- Products must be re-indexed after the values or the property definitions change. Matching is exact: the scanned value must equal the stored value.
The storefront sends the scanned value as the barcode:"<value>" filter of the products GraphQL query. The XCatalog
module expands that virtual filter into a term filter over the configured fields (OR across them, variations included);
with no configured field the scanned value stays an ordinary full-text keyword.
That expansion lives in the XCatalog module, not here: this module owns the settings, the REST API and the admin UI only. Because of that cross-repo dependency the changes must be merged in the order catalog -> x-catalog -> storefront theme.
- REST (admin):
GET/PUT api/catalog/barcode-search/store/{storeId}(plusGET .../store/{storeId}/fieldsfor the field picker, derived from the product index schema). Guarded by thecatalog:BrowseFilters:Read/catalog:BrowseFilters:Updatepermissions; saving an unknown field returns400. - Storefront: both settings are public, so they are exposed as store module settings and drive the scanner button and the matching mode.
Copyright (c) Virto Solutions LTD. All rights reserved.
Licensed under the Virto Commerce Open Software License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://virtocommerce.com/opensourcelicense
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
