b2c-querying-data
Querying Data in B2C Commerce
Efficient data querying is critical for storefront performance and job stability. B2C Commerce provides index-backed search APIs and database query APIs—choosing the right one for each use case avoids performance problems.
Product Search (Storefront)
Use ProductSearchModel for all storefront product searches. It is index-backed and designed for high-traffic pages.
Basic Product Search
var ProductSearchModel = require('dw/catalog/ProductSearchModel');
var psm = new ProductSearchModel();
psm.setCategoryID('electronics');
psm.setOrderableProductsOnly(true); // Only in-stock products
psm.setSearchPhrase('laptop');
psm.search();
var hits = psm.getProductSearchHits();
while (hits.hasNext()) {
var hit = hits.next();
var productID = hit.productID;
var minPrice = hit.minPrice;
var maxPrice = hit.maxPrice;
}
hits.close();
Paging Search Results
Always page results—never load the full result set:
var ProductSearchModel = require('dw/catalog/ProductSearchModel');
var PagingModel = require('dw/web/PagingModel');
var psm = new ProductSearchModel();
psm.setCategoryID('mens-clothing');
psm.setOrderableProductsOnly(true);
psm.search();
var pagingModel = new PagingModel(psm.getProductSearchHits(), psm.count);
pagingModel.setPageSize(12);
pagingModel.setStart(0); // page offset
var pageElements = pagingModel.pageElements;
while (pageElements.hasNext()) {
var hit = pageElements.next();
// Render product tile
}
Getting Variation Data from Search Hits
Use ProductSearchHit methods instead of loading full product objects:
// GOOD: Get variation info from the search hit (index-backed)
var representedColors = hit.getRepresentedVariationValues('color');
var representedIDs = hit.getRepresentedProductIDs();
var minPrice = hit.getMinPrice();
var maxPrice = hit.getMaxPrice();
// BAD: Loading the full product and iterating variants (database-intensive)
var product = hit.product;
var variants = product.getVariants(); // Expensive!
var priceModel = product.getPriceModel(); // Expensive!
Search Refinements
var psm = new ProductSearchModel();
psm.setCategoryID('shoes');
psm.addRefinementValues('color', 'blue');
psm.addRefinementValues('size', '10');
psm.setPriceMin(50);
psm.setPriceMax(200);
psm.search();
// Get available refinement values for the current result set
var refinements = psm.getRefinements();
var colorValues = refinements.getNextLevelRefinementValues(
refinements.getRefinementDefinitionByName('color')
);
ProductSearchModel API Summary
| Method | Description |
|---|---|
search() |
Execute the search |
setCategoryID(id) |
Filter by category |
setSearchPhrase(phrase) |
Set search keywords |
setOrderableProductsOnly(flag) |
Exclude out-of-stock |
addRefinementValues(name, value) |
Add refinement filter |
setPriceMin(price) / setPriceMax(price) |
Price range filter |
setSortingRule(rule) |
Set sorting rule |
getProductSearchHits() |
Get result iterator |
getRefinements() |
Get available refinements |
count |
Total result count |
Order Queries
OrderMgr.searchOrders / queryOrders
Use searchOrders for index-backed order lookups and queryOrders for database queries:
var OrderMgr = require('dw/order/OrderMgr');
var Order = require('dw/order/Order');
// Index-backed search (preferred for common lookups)
var orders = OrderMgr.searchOrders(
'customerEmail = {0} AND status != {1}',
'creationDate desc',
'customer@example.com',
Order.ORDER_STATUS_FAILED
);
while (orders.hasNext()) {
var order = orders.next();
// Process order
}
orders.close(); // Always close iterators
Query by Date Range
var OrderMgr = require('dw/order/OrderMgr');
var Calendar = require('dw/util/Calendar');
var Order = require('dw/order/Order');
var startDate = new Calendar();
startDate.add(Calendar.DAY_OF_YEAR, -7);
var orders = OrderMgr.searchOrders(
'creationDate >= {0} AND status = {1}',
'creationDate desc',
startDate.time,
Order.ORDER_STATUS_NEW
);
while (orders.hasNext()) {
var order = orders.next();
// Process
}
orders.close();
searchOrders vs queryOrders
| Aspect | searchOrders |
queryOrders |
|---|---|---|
| Backing | Search index | Database |
| Performance | Fast for indexed fields | Slower, full table scan possible |
| Use when | Querying indexed attributes (status, email, dates) | Querying non-indexed or custom attributes |
| Result limit | Up to 1000 hits | No hard limit (but use paging) |
Prefer searchOrders for storefront and high-traffic code paths. Use queryOrders only when you need to query attributes not available in the search index.
Customer / Profile Queries
CustomerMgr (Preferred)
Use searchProfiles for index-backed searches and processProfiles for batch processing in jobs:
var CustomerMgr = require('dw/customer/CustomerMgr');
// Index-backed search (storefront use)
var profiles = CustomerMgr.searchProfiles(
'email = {0}',
'lastLoginTime desc',
'customer@example.com'
);
while (profiles.hasNext()) {
var profile = profiles.next();
// Process profile
}
profiles.close();
Batch Processing (Jobs)
Use processProfiles for jobs that need to iterate over many profiles—it has optimized memory management:
var CustomerMgr = require('dw/customer/CustomerMgr');
function processProfile(profile) {
// Process each profile individually
// Memory is managed automatically
}
// Process all profiles matching the query
CustomerMgr.processProfiles('gender = {0}', processProfile, 1);
Important: processProfiles replaces the older queryProfiles and SystemObjectMgr.querySystemObjects for customer data. It uses the full-text search service with better performance and memory characteristics.
Customer Query Behaviors
- Wildcards (
*,%,+) are filtered from queries and replaced by spaces LIKEandILIKEexecute as full-text queries (match whole words, not substrings)LIKEis case-insensitive- Combining
ANDandORin the same query degrades performance - Range queries (e.g.,
a > b) impact performance - Results are limited to the first 1000 hits
System Object Queries (SystemObjectMgr)
For querying system objects other than customers (e.g., SitePreferences, catalogs):
var SystemObjectMgr = require('dw/object/SystemObjectMgr');
// Query system objects
var results = SystemObjectMgr.querySystemObjects(
'Profile',
'custom.loyaltyTier = {0}',
'lastLoginTime desc',
'Gold'
);
while (results.hasNext()) {
var obj = results.next();
// Process
}
results.close();
Note: For customer profiles specifically, prefer CustomerMgr.searchProfiles or CustomerMgr.processProfiles over SystemObjectMgr.querySystemObjects—they use the search index and perform significantly better.
Database-Intensive APIs to Avoid
These APIs hit the database directly and are expensive on high-traffic pages. Replace them with index-friendly alternatives. See Performance-Critical APIs for the complete list with impact details.
| Avoid (Database-Intensive) | Use Instead (Index-Friendly) |
|---|---|
Category.getProducts() / getOnlineProducts() |
ProductSearchModel.setCategoryID() |
ProductMgr.queryAllSiteProducts() |
ProductSearchModel.search() |
Product.getVariants() / getVariationModel() |
ProductSearchHit methods |
Product.getPriceModel() (in loops) |
ProductSearchHit.getMinPrice() / getMaxPrice() |
CustomerMgr.queryProfiles() |
CustomerMgr.searchProfiles() or processProfiles() |
Related Skills
- b2c-ordering — Order lifecycle, status transitions, creation flows
- b2c-custom-objects — Custom object CRUD, OCAPI search queries
Best Practices
Do
- Always close iterators — unclosed iterators leak resources (
results.close()) - Page results — use
PagingModelor limit result counts; never load unbounded result sets - Put all filtering in the query — don't post-process or filter results in custom code
- Use index-backed APIs —
ProductSearchModel,searchOrders,searchProfilesfor storefront pages - Use
processProfilesfor batch customer operations in jobs (optimized memory) - Limit page size — maximum ~120 products per page for search result pages
- Use
setOrderableProductsOnly(true)— to filter unavailable products at the search level
Don't
- Don't iterate over product variants on search result pages — use
ProductSearchHitmethods instead - Don't post-process search results — all criteria must go into the query for efficient execution
- Don't use
queryAllSiteProducts()on storefront pages — it bypasses the search index - Don't combine AND + OR in customer queries — it degrades performance
- Don't rely on getting more than 1000 results — search APIs cap at 1000 hits
- Don't call database-intensive APIs on high-traffic pages — category pages, search results, PDPs, and homepage
Job-Specific Guidelines
- Use
processProfilesoverqueryProfilesfor large customer data sets - Design loop logic so memory consumption doesn't grow with result set size
- Keep only the currently processed object in memory; don't retain references
- Stream data to files regularly; don't build large structures in memory
- Limit transaction size to under 1000 modified business objects
Detailed References
- Performance-Critical APIs — full list of index-friendly vs database-intensive APIs
More from salesforcecommercecloud/b2c-developer-tooling
b2c-docs
Search and read B2C Commerce Script API documentation and XSD schemas using the b2c CLI. Use this skill whenever the user needs to look up class methods, understand API signatures, find available properties on commerce objects (baskets, orders, products, customers), or check XML schema formats for imports. Also use when writing server-side scripts and needing API reference — even if they just say "what methods does Basket have" or "what fields can I import for products".
116b2c-webdav
List, upload, download, and manage files on B2C Commerce instances via WebDAV. Use this skill whenever the user needs to upload files to IMPEX directories, download exports from an instance, list remote files, create or delete directories, or zip/unzip files on the server. Also use when managing file transfers to sandboxes or browsing instance file systems -- even if they just say 'upload a file to the instance' or 'check what's in the IMPEX folder'.
103b2c-slas-auth-patterns
Implement SLAS authentication patterns in B2C Commerce including passwordless login (email OTP, SMS OTP, passkeys), session bridging between PWA Kit/Storefront Next and SFRA, hybrid authentication (B2C 25.3+), token refresh flows, trusted system on behalf of (TSOB), and JWT validation. Use this skill whenever the user asks about shopper authentication beyond basic login, token exchange flows, passwordless or biometric auth, keeping sessions alive across storefronts, handling 409 Conflict errors on token endpoints, refreshing shopper tokens, or validating JWTs — even if they don't mention SLAS by name.
90b2c-config
Inspect and debug CLI configuration, instance connections, and authentication. Use this skill whenever the user needs to check which dw.json or credentials are active, manage multiple instance profiles, retrieve OAuth tokens for scripting, troubleshoot authentication failures or connection errors, or integrate with VS Code or other editors. Also use when environment variables override config or the wrong sandbox is being targeted -- even if they just say 'why is it connecting to the wrong instance' or 'get me an access token'.
90b2c-controllers
Create storefront controllers using SFRA or classic patterns with server.get/post, middleware chains, and res.render/json. Use this skill whenever the user needs to build a page route, handle form submissions, create AJAX endpoints, extend or override existing controllers, or add middleware to a request pipeline. Also use when debugging route registration or response rendering -- even if they just say 'new page endpoint' or 'handle a POST request'.
86b2c-scapi-schemas
Browse and retrieve SCAPI OpenAPI schema specifications. Use this skill whenever the user needs to list available SCAPI APIs, inspect endpoint paths or request/response shapes, explore data models for products or orders, check which fields an API returns, or understand SCAPI versioning. Also use when looking up API details before building an integration -- even if they just say 'what fields does the product API return' or 'show me the SCAPI endpoints'.
84