| name | b2c-querying-data |
| description | Write efficient data queries in B2C Commerce for products, customers, and orders. Use this skill whenever the user needs to search products on a storefront page, look up customer profiles, query orders, paginate search results, or fix slow category pages. Also use when they ask about performance problems with data access, replacing database-intensive APIs with index-backed alternatives, or choosing between search vs query methods — even if they just say "my category page is slow" or "how do I find customers by email". |
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);
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);
var pageElements = pagingModel.pageElements;
while (pageElements.hasNext()) {
var hit = pageElements.next();
}
Getting Variation Data from Search Hits
Use ProductSearchHit methods instead of loading full product objects:
var representedColors = hit.getRepresentedVariationValues('color');
var representedIDs = hit.getRepresentedProductIDs();
var minPrice = hit.getMinPrice();
var maxPrice = hit.getMaxPrice();
var product = hit.product;
var variants = product.getVariants();
var priceModel = product.getPriceModel();
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();
var refinements = psm.getRefinements();
var colorValues = refinements.getAllRefinementValues('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');
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();
}
orders.close();
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();
}
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');
var profiles = CustomerMgr.searchProfiles(
'email = {0}',
'lastLoginTime desc',
'customer@example.com'
);
while (profiles.hasNext()) {
var profile = profiles.next();
}
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) {
}
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
LIKE and ILIKE execute as full-text queries (match whole words, not substrings)
LIKE is case-insensitive
- Combining
AND and OR in 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');
var results = SystemObjectMgr.querySystemObjects(
'Profile',
'custom.loyaltyTier = {0}',
'lastLoginTime desc',
'Gold'
);
while (results.hasNext()) {
var obj = results.next();
}
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
Best Practices
Do
- Always close iterators — unclosed iterators leak resources (
results.close())
- Page results — use
PagingModel or 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, searchProfiles for storefront pages
- Use
processProfiles for 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
ProductSearchHit methods 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
processProfiles over queryProfiles for 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