关于 b2c-querying-data
b2c-querying-data 为 Salesforce B2C Commerce 开发者提供数据查询实践,帮助按场景选择索引搜索或数据库查询 API。重点包括分页、批处理和避免高流量页面上的高开销调用。
主要功能
- 01使用索引 API 搜索商品、订单和客户
- 02为搜索结果分页并限制数量
- 03通过 ProductSearchHit 获取变体信息
- 04使用 processProfiles 批量处理客户
- 05识别并替换高开销数据库 API
适用场景
- 01构建高流量商品搜索和分类页面
- 02按状态、邮箱或日期查找订单
- 03在作业中处理大量客户档案
- 04排查查询慢或结果集过大的问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ salesforcecommercecloud/ b2c-developer-tooling --skill b2c-querying-data安装后在 Claude Code 中输入/b2c-querying-data确认已加载。
附带文件evals/trigger-evals.jsonreferences/PERFORMANCE-APIS.md
详细介绍
在 B2C Commerce 中查询数据
高效的数据查询对店面性能和作业稳定性至关重要。B2C Commerce 提供基于索引的搜索 API 和数据库查询 API——针对每种使用场景选择合适的 API,可避免性能问题。
商品搜索(店面)
店面的所有商品搜索都使用 ProductSearchModel。它基于索引构建,专为高流量页面设计。
基本商品搜索
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();
对搜索结果分页
始终对结果分页——切勿加载完整结果集:
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
}
从搜索命中项获取变体数据
使用 ProductSearchHit 方法,而不是加载完整的商品对象:
// 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!
搜索细化项
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.getAllRefinementValues('color');
ProductSearchModel API 摘要
| 方法 | 描述 |
|---|---|
search() | 执行搜索 |
setCategoryID(id) | 按类别筛选 |
setSearchPhrase(phrase) | 设置搜索关键词 |
setOrderableProductsOnly(flag) | 排除缺货商品 |
addRefinementValues(name, value) | 添加细化筛选条件 |
setPriceMin(price) / setPriceMax(price) | 按价格范围筛选 |
setSortingRule(rule) | 设置排序规则 |
getProductSearchHits() | 获取结果迭代器 |
getRefinements() | 获取可用的细化项 |
count | 结果总数 |
订单查询
OrderMgr.searchOrders / queryOrders
使用 searchOrders 通过索引查找订单,使用 queryOrders 执行数据库查询:
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',
'[email protected]',
Order.ORDER_STATUS_FAILED
);
while (orders.hasNext()) {
var order = orders.next();
// Process order
}
orders.close(); // Always close iterators
按日期范围查询
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 与 queryOrders 对比
| 方面 | searchOrders | queryOrders |
|---|---|---|
| 后端 | 搜索索引 | 数据库 |
| 性能 | 查询已编入索引的字段时速度快 | 速度较慢,可能执行全表扫描 |
| 使用场景 | 查询已编入索引的属性(状态、电子邮件、日期) | 查询未编入索引或自定义属性 |
| 结果限制 | 最多 1000 条命中 | 无硬性限制(但应分页) |
店面和高流量代码路径中优先使用 searchOrders。只有在需要查询搜索索引中不可用的属性时,才使用 queryOrders。
客户 / 配置文件查询
CustomerMgr(首选)
使用 searchProfiles 执行基于索引的搜索,使用 processProfiles 在作业中进行批处理:
var CustomerMgr = require('dw/customer/CustomerMgr');
// Index-backed search (storefront use)
var profiles = CustomerMgr.searchProfiles(
'email = {0}',
'lastLoginTime desc',
'[email protected]'
);
while (profiles.hasNext()) {
var profile = profiles.next();
// Process profile
}
profiles.close();
批处理(作业)
对于需要遍历大量配置文件的作业,使用 processProfiles——它具有经过优化的内存管理:
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);
重要提示: 对于客户数据,processProfiles 取代了旧版 queryProfiles 和 SystemObjectMgr.querySystemObjects。它使用全文搜索服务,性能更高,内存特性也更好。
客户查询行为
- 查询中的通配符(
*、%、+)会被过滤并替换为空格 LIKE和ILIKE会作为全文查询执行(匹配整个单词,而非子字符串)LIKE不区分大小写- 在同一查询中组合
AND和OR会降低性能 - 范围查询(例如,
a > b)会影响性能 - 结果限制为前 1000 条命中
系统对象查询(SystemObjectMgr)
用于查询客户以外的系统对象(例如,SitePreferences、目录):
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();
注意: 对于客户配置文件,优先使用 CustomerMgr.searchProfiles 或 CustomerMgr.processProfiles,而不是 SystemObjectMgr.querySystemObjects——它们使用搜索索引,性能明显更好。
应避免的高数据库负载 API
这些 API 会直接访问数据库,在高流量页面上开销很大。请改用对索引友好的替代方案。完整列表及影响详情请参阅 性能关键型 API。
| 应避免(高数据库负载) | 替代方案(对索引友好) |
|---|---|
Category.getProducts() / getOnlineProducts() | ProductSearchModel.setCategoryID() |
ProductMgr.queryAllSiteProducts() | ProductSearchModel.search() |
Product.getVariants() / getVariationModel() | ProductSearchHit 方法 |
Product.getPriceModel()(在循环中) | ProductSearchHit.getMinPrice() / getMaxPrice() |
CustomerMgr.queryProfiles() | CustomerMgr.searchProfiles() 或 processProfiles() |
相关技能
b2c-ordering技能 — 订单生命周期、状态转换、创建流程b2c-custom-objects技能 — 自定义对象 CRUD、OCAPI 搜索查询
最佳实践
应做事项
- 始终关闭迭代器 — 未关闭的迭代器会泄漏资源(
results.close()) - 对结果分页 — 使用
PagingModel或限制结果数;切勿加载不受限的结果集 - 将所有筛选条件放入查询中 — 不要在自定义代码中对结果进行后处理或筛选
- 使用基于索引的 API — 在店面页面中使用
ProductSearchModel、searchOrders、searchProfiles - 在作业中使用
processProfiles处理批量客户操作(内存效率更高) - 限制页面大小 — 搜索结果页面每页最多约 120 个产品
- 使用
setOrderableProductsOnly(true)— 在搜索层筛选掉不可用的产品
不应做事项
- 不要在搜索结果页面遍历产品变体 — 改用
ProductSearchHit方法 - 不要对搜索结果进行后处理 — 所有条件都必须放入查询中,以便高效执行
- 不要在店面页面使用
queryAllSiteProducts()— 它会绕过搜索索引 - 不要在客户查询中组合 AND + OR — 这会降低性能
- 不要依赖获取超过 1000 条结果 — 搜索 API 的命中数上限为 1000
- 不要在高流量页面调用高数据库负载 API — 包括类别页面、搜索结果页面、PDP 和首页
作业专用指南
- 对于大型客户数据集,优先使用
processProfiles,而非queryProfiles - 设计循环逻辑,确保内存消耗不会随结果集大小增长
- 内存中只保留当前正在处理的对象;不要保留对象引用
- 定期将数据流式写入文件;不要在内存中构建大型结构
- 将事务大小限制在 1000 个已修改业务对象以内
详细参考
- 性能关键型 API — 对索引友好与高数据库负载 API 的完整列表