b2c-querying-data について
b2c-querying-dataは、Salesforce B2C Commerceの開発者向けに、インデックス検索APIとデータベースクエリAPIの使い分けを解説するスキルです。ページング、バッチ処理、高トラフィックページでの高コストな呼び出しの回避を扱います。
主な機能
- 01商品・注文・顧客のインデックス検索
- 02検索結果のページングと件数制限
- 03ProductSearchHitによるバリエーション情報の取得
- 04processProfilesによる顧客の一括処理
- 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();
注意: 顧客プロファイルについては、SystemObjectMgr.querySystemObjects よりも CustomerMgr.searchProfiles または CustomerMgr.processProfiles を優先してください。これらは検索インデックスを使用するため、パフォーマンスが大幅に向上します。
避けるべきデータベース負荷の高い API
これらの API はデータベースに直接アクセスするため、トラフィックの多いページではコストがかかります。インデックスを活用できる代替手段に置き換えてください。影響の詳細を含む完全な一覧については、パフォーマンスが重要な 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を使ってください(メモリが最適化されています) - ページサイズを制限する — 検索結果ページでは、1 ページあたり最大約 120 商品にしてください
setOrderableProductsOnly(true)を使う — 検索時に購入できない商品を除外します
実施しないこと
- 検索結果ページで商品バリエーションを反復処理しない — 代わりに
ProductSearchHitのメソッドを使ってください - 検索結果を後処理しない — 効率的に実行できるよう、すべての条件をクエリに含めてください
- ストアフロントのページで
queryAllSiteProducts()を使わない — 検索インデックスを迂回します - 顧客クエリで AND と OR を組み合わせない — パフォーマンスが低下します
- 1000 件を超える結果が得られることを前提にしない — 検索 API の結果は最大 1000 件です
- トラフィックの多いページでデータベース負荷の高い API を呼び出さない — カテゴリページ、検索結果、PDP、ホームページが該当します
ジョブ固有のガイドライン
- 大規模な顧客データセットでは、
queryProfilesよりprocessProfilesを使ってください - 結果セットのサイズに応じてメモリ使用量が増えないよう、ループ処理を設計してください
- 処理中のオブジェクトだけをメモリに保持し、参照を保持し続けないでください
- データは定期的にファイルへ書き出し、大きなデータ構造をメモリ上に構築しないでください
- トランザクションのサイズは、変更するビジネスオブジェクトが 1000 件未満になるよう制限してください
詳細なリファレンス
- パフォーマンスが重要な API — インデックスを活用できる API とデータベース負荷の高い API の完全な一覧